Adding support for streaming

Faraday supports streaming responses, which means that the response body is not loaded into memory all at once, but instead it is read in chunks. This can be particularly useful when dealing with large responses. Not all HTTP clients support this, so it is not required for adapters to support it.

However, if you do want to support it in your adapter, you can do so by leveraging helpers provided by the env object. Let’s see an example implementation first with some comments, and then we’ll explain it in more detail:

module Faraday
  class Adapter
    class FlorpHttp < Faraday::Adapter
      def call(env)
        super
        if env.stream_response? # check if the user wants to stream the response
          # start a streaming response.
          # on_data is a block that will let users consume the response body
          http_response = env.stream_response do |&on_data|
            # perform the request using FlorpHttp
            # the block will be called for each chunk of data
            FlorpHttp.perform_request(...) do |chunk|
              on_data.call(chunk)
            end
          end
          # the body is already consumed by the block
          # so it's good practice to set it to nil
          http_response.body = nil
        else
          # perform the request normally, no streaming.
          http_response = FlorpHttp.perform_request(...)
        end
        save_response(env, http_response.status, http_response.body, http_response.headers, http_response.reason_phrase)
      end
    end
  end
end

How it works

stream_response?

The first helper method we use is stream_response?. This method is provided by the env object and it returns true if the user wants to stream the response. This is controlled by the presence of an on_data callback in the request options.

stream_response

The second helper is stream_response. This method is also provided by the env object and it takes a block. The block will be called with a single argument, which is a callback that the user can use to consume the response body. All your adapter needs to do, is to call this callback with each chunk of data that you receive from the server.

The on_data callback will internally call the callback provided by the user, so you don’t need to worry about that. It will also keep track of the number of bytes that have been read, and pass that information to the user’s callback.

To see how this all works together, let’s see an example of how a user would use this feature:

# A buffer to store the streamed data
streamed = []

conn = Faraday.new('http://httpbingo.org')
conn.get('/stream/100') do |req|
  # Set a callback which will receive tuples of chunk Strings,
  # the sum of characters received so far, and the response environment.
  # The latter will allow access to the response status, headers and reason, as well as the request info.
  req.options.on_data = proc do |chunk, overall_received_bytes, env|
    puts "Received #{overall_received_bytes} characters"
    streamed << chunk
  end
end

# Joins all response chunks together
streamed.join

For more details on the user experience, check the Streaming Responses page.