diff --git a/lib/async/http/faraday/adapter.rb b/lib/async/http/faraday/adapter.rb index d010bbc..ce3352e 100644 --- a/lib/async/http/faraday/adapter.rb +++ b/lib/async/http/faraday/adapter.rb @@ -12,6 +12,8 @@ require 'faraday' require 'faraday/adapter' + +require 'async/barrier' require 'kernel/sync' require 'async/http/client' @@ -48,8 +50,47 @@ def read end end + class ParallelManager + def initialize(options = {}) + @options = options + @barrier = nil + end + + def run + if $VERBOSE + warn "Please update your Faraday version!", uplevel: 2 + end + end + + def async(&block) + if @barrier + @barrier.async(&block) + else + Sync(&block) + end + end + + def execute(&block) + Sync do + @barrier = Async::Barrier.new + + yield + + @barrier.wait + ensure + @barrier&.stop + end + end + end + # An adapter that allows Faraday to use Async::HTTP as the underlying HTTP client. class Adapter < ::Faraday::Adapter + self.supports_parallel = true + + def self.setup_parallel_manager(**options) + ParallelManager.new(options) + end + # The exceptions that are considered connection errors and result in a `Faraday::ConnectionFailed` exception. CONNECTION_EXCEPTIONS = [ Errno::EADDRNOTAVAIL, @@ -98,6 +139,21 @@ def call(env) # For compatibility with the default adapter: env.url.path = '/' if env.url.path.empty? + if parallel_manager = env.parallel_manager + parallel_manager.async do + perform_request(env) + env.response.finish(env) + end + else + perform_request(env) + end + + @app.call(env) + end + + private + + def perform_request(env) with_client(env) do |endpoint, client| if body = env.body # We need to ensure the body is wrapped in a Readable object so that it can be read in chunks: @@ -149,8 +205,6 @@ def call(env) raise ::Faraday::ConnectionFailed, e end - private - def with_client(env) Sync do endpoint = Endpoint.new(env.url) diff --git a/test/async/http/faraday/adapter/parallel.rb b/test/async/http/faraday/adapter/parallel.rb new file mode 100644 index 0000000..2aa4e0f --- /dev/null +++ b/test/async/http/faraday/adapter/parallel.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require 'async/http/faraday' + +require 'sus/fixtures/async/reactor_context' +require 'sus/fixtures/async/http/server_context' + +describe Async::HTTP::Faraday::Adapter do + with "a local http server" do + include Sus::Fixtures::Async::ReactorContext + include Sus::Fixtures::Async::HTTP::ServerContext + + + let(:app) do + Protocol::HTTP::Middleware.for do |request| + Protocol::HTTP::Response[200, {}, ['Hello World']] + end + end + + it "client can get resource" do + adapter = Faraday.new(bound_url) do |builder| + builder.adapter :async_http + end + + response1 = response2 = response3 = nil + + adapter.in_parallel do + response1 = adapter.get("/index") + response2 = adapter.get("/index") + response3 = adapter.get("/index") + end + + expect(response1.body).to be == 'Hello World' + expect(response2.body).to be == 'Hello World' + expect(response3.body).to be == 'Hello World' + ensure + adapter&.close + end + end +end \ No newline at end of file