Skip to content

Commit dc1da8d

Browse files
committed
Extract clients -> ClientCache for better isolation.
1 parent 6ad8fa5 commit dc1da8d

File tree

2 files changed

+92
-60
lines changed

2 files changed

+92
-60
lines changed

lib/async/http/faraday/adapter.rb

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
require 'async/http/client'
1717
require 'async/http/proxy'
1818

19+
require_relative 'client_cache'
20+
1921
module Async
2022
module HTTP
2123
module Faraday
@@ -65,70 +67,17 @@ class Adapter < ::Faraday::Adapter
6567
#
6668
# @parameter timeout [Integer] The timeout for requests.
6769
# @parameter options [Hash] Additional options to pass to the underlying Async::HTTP::Client.
68-
def initialize(*arguments, timeout: nil, **options, &block)
69-
super(*arguments, **options)
70-
71-
@timeout = timeout
72-
73-
@clients = {}
74-
75-
@options = options
76-
end
77-
78-
# Make a new client for the given endpoint.
79-
#
80-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to create the client for.
81-
def make_client(endpoint)
82-
Client.new(endpoint, **@connection_options)
83-
end
84-
85-
# Get the host key for the given endpoint.
86-
#
87-
# This is used to cache clients for the same host.
88-
#
89-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the host key for.
90-
def host_key(endpoint)
91-
url = endpoint.url.dup
92-
93-
url.path = ""
94-
url.fragment = nil
95-
url.query = nil
96-
97-
return url
98-
end
99-
100-
# Get a client for the given endpoint. If a client already exists for the host, it will be reused.
101-
#
102-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
103-
def client_for(endpoint)
104-
key = host_key(endpoint)
105-
106-
@clients.fetch(key) do
107-
@clients[key] = make_client(endpoint)
108-
end
109-
end
110-
111-
# Get a client for the given proxy endpoint and endpoint. If a client already exists for the host, it will be reused.
112-
#
113-
# @parameter proxy_endpoint [IO::Endpoint::Generic] The proxy endpoint to use.
114-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
115-
def proxy_client_for(proxy_endpoint, endpoint)
116-
key = [host_key(proxy_endpoint), host_key(endpoint)]
70+
def initialize(...)
71+
super
11772

118-
@clients.fetch(key) do
119-
client = client_for(proxy_endpoint)
120-
@clients[key] = client.proxied_client(endpoint)
121-
end
73+
@timeout = @connection_options.delete(:timeout)
74+
@clients = ClientCache.new(**@connection_options)
12275
end
12376

12477
# Close all clients.
12578
def close
12679
# The order of operations here is to avoid a race condition between iterating over clients (#close may yield) and creating new clients.
127-
clients = @clients.values
128-
129-
@clients.clear
130-
131-
clients.each(&:close)
80+
@clients.close
13281
end
13382

13483
# Make a request using the adapter.
@@ -148,9 +97,9 @@ def call(env)
14897

14998
if proxy = env.request.proxy
15099
proxy_endpoint = Endpoint.new(proxy.uri)
151-
client = self.proxy_client_for(proxy_endpoint, endpoint)
100+
client = @clients.proxy_client_for(proxy_endpoint, endpoint)
152101
else
153-
client = self.client_for(endpoint)
102+
client = @clients.client_for(endpoint)
154103
end
155104

156105
if body = env.body
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2018-2024, by Samuel Williams.
5+
# Copyright, 2018, by Andreas Garnaes.
6+
# Copyright, 2019, by Denis Talakevich.
7+
# Copyright, 2019-2020, by Igor Sidorov.
8+
# Copyright, 2023, by Genki Takiuchi.
9+
# Copyright, 2023, by Flavio Fernandes.
10+
# Copyright, 2024, by Jacob Frautschi.
11+
12+
require 'faraday'
13+
require 'faraday/adapter'
14+
require 'kernel/sync'
15+
16+
require 'async/http/client'
17+
require 'async/http/proxy'
18+
19+
module Async
20+
module HTTP
21+
module Faraday
22+
class ClientCache
23+
def initialize(**options)
24+
@options = options
25+
@clients = {}
26+
end
27+
28+
def close
29+
clients = @clients.values
30+
@clients.clear
31+
32+
clients.each(&:close)
33+
end
34+
35+
# Make a new client for the given endpoint.
36+
#
37+
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to create the client for.
38+
def make_client(endpoint)
39+
Client.new(endpoint, **@options)
40+
end
41+
42+
# Get the host key for the given endpoint.
43+
#
44+
# This is used to cache clients for the same host.
45+
#
46+
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the host key for.
47+
def host_key(endpoint)
48+
url = endpoint.url.dup
49+
50+
url.path = ""
51+
url.fragment = nil
52+
url.query = nil
53+
54+
return url
55+
end
56+
57+
# Get a client for the given endpoint. If a client already exists for the host, it will be reused.
58+
#
59+
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
60+
def client_for(endpoint)
61+
key = host_key(endpoint)
62+
63+
@clients.fetch(key) do
64+
@clients[key] = make_client(endpoint)
65+
end
66+
end
67+
68+
# Get a client for the given proxy endpoint and endpoint. If a client already exists for the host, it will be reused.
69+
#
70+
# @parameter proxy_endpoint [IO::Endpoint::Generic] The proxy endpoint to use.
71+
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
72+
def proxy_client_for(proxy_endpoint, endpoint)
73+
key = [host_key(proxy_endpoint), host_key(endpoint)]
74+
75+
@clients.fetch(key) do
76+
client = client_for(proxy_endpoint)
77+
@clients[key] = client.proxied_client(endpoint)
78+
end
79+
end
80+
end
81+
end
82+
end
83+
end

0 commit comments

Comments
 (0)