diff --git a/gems.rb b/gems.rb index 9bec290..ff3b643 100644 --- a/gems.rb +++ b/gems.rb @@ -15,4 +15,6 @@ group :test do gem "bake-test" gem "bake-test-external" + + gem "faraday-multipart" end diff --git a/lib/async/http/faraday/adapter.rb b/lib/async/http/faraday/adapter.rb index 8a59fc4..528d00f 100644 --- a/lib/async/http/faraday/adapter.rb +++ b/lib/async/http/faraday/adapter.rb @@ -18,6 +18,23 @@ module Async module HTTP module Faraday + class BodyWrapper < ::Protocol::HTTP::Body::Readable + def initialize(body, block_size: 4096) + @body = body + @block_size = block_size + end + + def close(error = nil) + @body.close if @body.respond_to?(:close) + ensure + super + end + + def read + @body.read(@block_size) + end + end + class Adapter < ::Faraday::Adapter CONNECTION_EXCEPTIONS = [ Errno::EADDRNOTAVAIL, @@ -99,7 +116,10 @@ def call(env) end if body = env.body - body = Body::Buffered.wrap(body) + # We need to wrap the body in a Readable object so that it can be read in chunks: + # Faraday's body only responds to `#read`. + # body = ::Protocol::HTTP::Body::Buffered.wrap(body) + body = BodyWrapper.new(body) end if headers = env.request_headers diff --git a/spec/async/http/faraday/adapter_spec.rb b/spec/async/http/faraday/adapter_spec.rb index 2f7e4bc..8f5b2dc 100644 --- a/spec/async/http/faraday/adapter_spec.rb +++ b/spec/async/http/faraday/adapter_spec.rb @@ -13,12 +13,15 @@ require 'async' +require 'faraday' +require 'faraday/multipart' + RSpec.describe Async::HTTP::Faraday::Adapter do include_context Async::RSpec::Reactor - let(:endpoint) { + let(:endpoint) do Async::HTTP::Endpoint.parse('http://127.0.0.1:9294') - } + end def run_server(response = Protocol::HTTP::Response[204], response_delay: nil) Sync do |task| @@ -59,15 +62,14 @@ def get_response(url = endpoint.url, path = '/index', adapter_options: {}) expect(get_response.body).to eq 'Hello World' end end - - it "client can get responce with respect to Content-Type encoding" do - run_server(Protocol::HTTP::Response[200, {'Content-Type' => 'text/html; charset=utf-8'}, ['こんにちは世界']]) do - body = get_response.body - expect(body.encoding).to eq Encoding::UTF_8 - expect(body).to eq 'こんにちは世界' - end - end - + + it "client can get responce with respect to Content-Type encoding" do + run_server(Protocol::HTTP::Response[200, {'Content-Type' => 'text/html; charset=utf-8'}, ['こんにちは世界']]) do + body = get_response.body + expect(body.encoding).to eq Encoding::UTF_8 + expect(body).to eq 'こんにちは世界' + end + end it "works without top level reactor" do response = get_response("https://www.google.com", "/search?q=ruby") @@ -121,4 +123,15 @@ def get_response(url = endpoint.url, path = '/index', adapter_options: {}) it 'wraps underlying exceptions into Faraday analogs' do expect { get_response(endpoint.url, '/index') }.to raise_error(Faraday::ConnectionFailed) end + + it 'can use multi-part post body' do + connection = Faraday.new do |builder| + builder.request :multipart + builder.adapter :async_http + end + + connection.post("https://httpbin.org/post") do |request| + request.body = { "file" => Faraday::Multipart::FilePart.new(StringIO.new("file content"), "text/plain", "file.txt") } + end + end end