httpx 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/doc/release_notes/1_6_0.md +50 -0
- data/lib/httpx/adapters/datadog.rb +23 -13
- data/lib/httpx/adapters/faraday.rb +14 -9
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/callbacks.rb +1 -1
- data/lib/httpx/connection/http1.rb +5 -6
- data/lib/httpx/connection/http2.rb +30 -12
- data/lib/httpx/connection.rb +17 -24
- data/lib/httpx/errors.rb +3 -1
- data/lib/httpx/io/ssl.rb +1 -4
- data/lib/httpx/io/tcp.rb +25 -16
- data/lib/httpx/io/unix.rb +4 -3
- data/lib/httpx/loggable.rb +4 -1
- data/lib/httpx/options.rb +252 -158
- data/lib/httpx/plugins/aws_sdk_authentication.rb +2 -0
- data/lib/httpx/plugins/aws_sigv4.rb +2 -0
- data/lib/httpx/plugins/callbacks.rb +13 -1
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/content_digest.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +2 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +2 -0
- data/lib/httpx/plugins/grpc.rb +2 -0
- data/lib/httpx/plugins/h2c.rb +26 -16
- data/lib/httpx/plugins/internal_telemetry.rb +0 -49
- data/lib/httpx/plugins/ntlm_auth.rb +2 -0
- data/lib/httpx/plugins/oauth.rb +2 -0
- data/lib/httpx/plugins/persistent.rb +27 -18
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy/ssh.rb +2 -0
- data/lib/httpx/plugins/proxy.rb +61 -20
- data/lib/httpx/plugins/response_cache/file_store.rb +2 -2
- data/lib/httpx/plugins/response_cache.rb +2 -0
- data/lib/httpx/plugins/retries.rb +2 -0
- data/lib/httpx/plugins/ssrf_filter.rb +2 -2
- data/lib/httpx/plugins/stream_bidi.rb +3 -3
- data/lib/httpx/plugins/upgrade/h2.rb +11 -1
- data/lib/httpx/plugins/upgrade.rb +8 -0
- data/lib/httpx/pool.rb +15 -10
- data/lib/httpx/request/body.rb +8 -3
- data/lib/httpx/request.rb +22 -11
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +3 -1
- data/lib/httpx/resolver/multi.rb +5 -2
- data/lib/httpx/resolver/native.rb +15 -6
- data/lib/httpx/resolver/resolver.rb +3 -5
- data/lib/httpx/resolver/system.rb +1 -1
- data/lib/httpx/resolver.rb +34 -21
- data/lib/httpx/response/body.rb +1 -1
- data/lib/httpx/response/buffer.rb +13 -18
- data/lib/httpx/selector.rb +92 -34
- data/lib/httpx/session.rb +89 -30
- data/lib/httpx/session_extensions.rb +3 -2
- data/lib/httpx/transcoder/form.rb +1 -13
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/transcoder/multipart.rb +14 -0
- data/lib/httpx/transcoder/utils/deflater.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +1 -1
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -0
- data/sig/connection/http2.rbs +4 -0
- data/sig/connection.rbs +6 -6
- data/sig/errors.rbs +3 -1
- data/sig/io/ssl.rbs +1 -1
- data/sig/io/tcp.rbs +13 -7
- data/sig/io/udp.rbs +7 -2
- data/sig/io/unix.rbs +0 -1
- data/sig/io.rbs +0 -3
- data/sig/options.rbs +63 -10
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/h2c.rbs +5 -1
- data/sig/plugins/persistent.rbs +1 -1
- data/sig/plugins/proxy/socks4.rbs +1 -1
- data/sig/plugins/proxy/socks5.rbs +1 -1
- data/sig/plugins/proxy.rbs +5 -2
- data/sig/plugins/ssrf_filter.rbs +1 -1
- data/sig/plugins/stream_bidi.rbs +2 -2
- data/sig/request.rbs +4 -1
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/native.rbs +1 -0
- data/sig/resolver/resolver.rbs +2 -3
- data/sig/resolver/system.rbs +2 -2
- data/sig/resolver.rbs +10 -11
- data/sig/response.rbs +2 -2
- data/sig/selector.rbs +18 -10
- data/sig/session.rbs +4 -0
- data/sig/transcoder/form.rbs +3 -3
- data/sig/transcoder/multipart.rbs +9 -3
- metadata +9 -3
data/lib/httpx/options.rb
CHANGED
@@ -15,70 +15,16 @@ module HTTPX
|
|
15
15
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
16
16
|
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
17
17
|
|
18
|
-
|
19
|
-
ip_address_families = begin
|
20
|
-
list = Socket.ip_address_list
|
21
|
-
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? && !a.ipv6_unique_local? }
|
22
|
-
[Socket::AF_INET6, Socket::AF_INET]
|
23
|
-
else
|
24
|
-
[Socket::AF_INET]
|
25
|
-
end
|
26
|
-
rescue NotImplementedError
|
27
|
-
[Socket::AF_INET]
|
28
|
-
end.freeze
|
18
|
+
@options_names = []
|
29
19
|
|
30
|
-
|
31
|
-
|
32
|
-
name = mod.name || "#{mod.superclass.name}(plugin)"
|
33
|
-
name = "#{name}/#{pl}" if pl
|
34
|
-
mod.set_temporary_name(name)
|
35
|
-
end
|
36
|
-
end
|
20
|
+
class << self
|
21
|
+
attr_reader :options_names
|
37
22
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
:debug_redact => ENV.key?("HTTPX_DEBUG_REDACT"),
|
43
|
-
:ssl => EMPTY_HASH,
|
44
|
-
:http2_settings => { settings_enable_push: 0 }.freeze,
|
45
|
-
:fallback_protocol => "http/1.1",
|
46
|
-
:supported_compression_formats => %w[gzip deflate],
|
47
|
-
:decompress_response_body => true,
|
48
|
-
:compress_request_body => true,
|
49
|
-
:timeout => {
|
50
|
-
connect_timeout: CONNECT_TIMEOUT,
|
51
|
-
settings_timeout: SETTINGS_TIMEOUT,
|
52
|
-
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
53
|
-
operation_timeout: OPERATION_TIMEOUT,
|
54
|
-
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
55
|
-
read_timeout: READ_TIMEOUT,
|
56
|
-
write_timeout: WRITE_TIMEOUT,
|
57
|
-
request_timeout: REQUEST_TIMEOUT,
|
58
|
-
},
|
59
|
-
:headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
|
60
|
-
:headers => {},
|
61
|
-
:window_size => WINDOW_SIZE,
|
62
|
-
:buffer_size => BUFFER_SIZE,
|
63
|
-
:body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
|
64
|
-
:request_class => Class.new(Request, &SET_TEMPORARY_NAME),
|
65
|
-
:response_class => Class.new(Response, &SET_TEMPORARY_NAME),
|
66
|
-
:request_body_class => Class.new(Request::Body, &SET_TEMPORARY_NAME),
|
67
|
-
:response_body_class => Class.new(Response::Body, &SET_TEMPORARY_NAME),
|
68
|
-
:pool_class => Class.new(Pool, &SET_TEMPORARY_NAME),
|
69
|
-
:connection_class => Class.new(Connection, &SET_TEMPORARY_NAME),
|
70
|
-
:options_class => Class.new(self, &SET_TEMPORARY_NAME),
|
71
|
-
:transport => nil,
|
72
|
-
:addresses => nil,
|
73
|
-
:persistent => false,
|
74
|
-
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
75
|
-
:resolver_options => { cache: true }.freeze,
|
76
|
-
:pool_options => EMPTY_HASH,
|
77
|
-
:ip_families => ip_address_families,
|
78
|
-
:close_on_fork => false,
|
79
|
-
}.freeze
|
23
|
+
def inherited(klass)
|
24
|
+
super
|
25
|
+
klass.instance_variable_set(:@options_names, @options_names.dup)
|
26
|
+
end
|
80
27
|
|
81
|
-
class << self
|
82
28
|
def new(options = {})
|
83
29
|
# let enhanced options go through
|
84
30
|
return options if self == Options && options.class < self
|
@@ -87,14 +33,34 @@ module HTTPX
|
|
87
33
|
super
|
88
34
|
end
|
89
35
|
|
36
|
+
def freeze
|
37
|
+
@options_names.freeze
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
90
41
|
def method_added(meth)
|
91
42
|
super
|
92
43
|
|
93
44
|
return unless meth =~ /^option_(.+)$/
|
94
45
|
|
95
|
-
optname = Regexp.last_match(1)
|
46
|
+
optname = Regexp.last_match(1)
|
47
|
+
|
48
|
+
if optname =~ /^(.+[^_])_+with/
|
49
|
+
# ignore alias method chain generated methods.
|
50
|
+
# this is the case with RBS runtime tests.
|
51
|
+
# it relies on the "_with/_without" separator, which is the most used convention,
|
52
|
+
# however it shouldn't be used in practice in httpx given the plugin architecture
|
53
|
+
# as the main extension API.
|
54
|
+
orig_name = Regexp.last_match(1)
|
55
|
+
|
56
|
+
return if @options_names.include?(orig_name.to_sym)
|
57
|
+
end
|
58
|
+
|
59
|
+
optname = optname.to_sym
|
96
60
|
|
97
61
|
attr_reader(optname)
|
62
|
+
|
63
|
+
@options_names << optname unless @options_names.include?(optname)
|
98
64
|
end
|
99
65
|
end
|
100
66
|
|
@@ -103,7 +69,7 @@ module HTTPX
|
|
103
69
|
# :debug :: an object which log messages are written to (must respond to <tt><<</tt>)
|
104
70
|
# :debug_level :: the log level of messages (can be 1, 2, or 3).
|
105
71
|
# :debug_redact :: whether header/body payload should be redacted (defaults to <tt>false</tt>).
|
106
|
-
# :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::
|
72
|
+
# :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::SSL)
|
107
73
|
# :http2_settings :: a hash of options to be passed to a HTTP2::Connection (ex: <tt>{ max_concurrent_streams: 2 }</tt>)
|
108
74
|
# :fallback_protocol :: version of HTTP protocol to use by default in the absence of protocol negotiation
|
109
75
|
# like ALPN (defaults to <tt>"http/1.1"</tt>)
|
@@ -123,6 +89,11 @@ module HTTPX
|
|
123
89
|
# :request_body_class :: class used to instantiate a request body
|
124
90
|
# :response_body_class :: class used to instantiate a response body
|
125
91
|
# :connection_class :: class used to instantiate connections
|
92
|
+
# :http1_class :: class used to manage HTTP1 sessions
|
93
|
+
# :http2_class :: class used to imanage HTTP2 sessions
|
94
|
+
# :resolver_native_class :: class used to resolve names using pure ruby DNS implementation
|
95
|
+
# :resolver_system_class :: class used to resolve names using system-based (getaddrinfo) name resolution
|
96
|
+
# :resolver_https_class :: class used to resolve names using DoH
|
126
97
|
# :pool_class :: class used to instantiate the session connection pool
|
127
98
|
# :options_class :: class used to instantiate options
|
128
99
|
# :transport :: type of transport to use (set to "unix" for UNIX sockets)
|
@@ -143,99 +114,46 @@ module HTTPX
|
|
143
114
|
# it only works if the session is persistent (and ruby 3.1 or higher is used).
|
144
115
|
#
|
145
116
|
# This list of options are enhanced with each loaded plugin, see the plugin docs for details.
|
146
|
-
def initialize(options =
|
147
|
-
|
148
|
-
freeze
|
149
|
-
end
|
150
|
-
|
151
|
-
def freeze
|
152
|
-
@origin.freeze
|
153
|
-
@base_path.freeze
|
154
|
-
@timeout.freeze
|
155
|
-
@headers.freeze
|
156
|
-
@addresses.freeze
|
157
|
-
@supported_compression_formats.freeze
|
158
|
-
@ssl.freeze
|
159
|
-
@http2_settings.freeze
|
160
|
-
@pool_options.freeze
|
161
|
-
@resolver_options.freeze
|
162
|
-
@ip_families.freeze
|
163
|
-
super
|
164
|
-
end
|
165
|
-
|
166
|
-
def option_origin(value)
|
167
|
-
URI(value)
|
168
|
-
end
|
169
|
-
|
170
|
-
def option_base_path(value)
|
171
|
-
String(value)
|
172
|
-
end
|
173
|
-
|
174
|
-
def option_headers(value)
|
175
|
-
headers_class.new(value)
|
176
|
-
end
|
177
|
-
|
178
|
-
def option_timeout(value)
|
179
|
-
Hash[value]
|
180
|
-
end
|
117
|
+
def initialize(options = EMPTY_HASH)
|
118
|
+
options_names = self.class.options_names
|
181
119
|
|
182
|
-
|
183
|
-
|
184
|
-
|
120
|
+
defaults =
|
121
|
+
case options
|
122
|
+
when Options
|
123
|
+
unknown_options = options.class.options_names - options_names
|
185
124
|
|
186
|
-
|
187
|
-
transport = value.to_s
|
188
|
-
raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)
|
125
|
+
raise Error, "unknown option: #{unknown_options.first}" unless unknown_options.empty?
|
189
126
|
|
190
|
-
|
191
|
-
|
127
|
+
DEFAULT_OPTIONS.merge(options)
|
128
|
+
else
|
129
|
+
options.each_key do |k|
|
130
|
+
raise Error, "unknown option: #{k}" unless options_names.include?(k)
|
131
|
+
end
|
192
132
|
|
193
|
-
|
194
|
-
|
195
|
-
end
|
133
|
+
options.empty? ? DEFAULT_OPTIONS : DEFAULT_OPTIONS.merge(options)
|
134
|
+
end
|
196
135
|
|
197
|
-
|
198
|
-
|
199
|
-
end
|
136
|
+
options_names.each do |k|
|
137
|
+
v = defaults[k]
|
200
138
|
|
201
|
-
|
202
|
-
|
203
|
-
max_concurrent_requests max_requests window_size buffer_size
|
204
|
-
body_threshold_size debug_level
|
205
|
-
].each do |option|
|
206
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
207
|
-
# converts +v+ into an Integer before setting the +#{option}+ option.
|
208
|
-
def option_#{option}(value) # def option_max_requests(v)
|
209
|
-
value = Integer(value) unless value.infinite?
|
210
|
-
raise TypeError, ":#{option} must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive?
|
139
|
+
if v.nil?
|
140
|
+
instance_variable_set(:"@#{k}", v)
|
211
141
|
|
212
|
-
|
142
|
+
next
|
213
143
|
end
|
214
|
-
OUT
|
215
|
-
end
|
216
144
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
Hash[value]
|
223
|
-
end
|
224
|
-
OUT
|
145
|
+
value = __send__(:"option_#{k}", v)
|
146
|
+
instance_variable_set(:"@#{k}", value)
|
147
|
+
end
|
148
|
+
|
149
|
+
freeze
|
225
150
|
end
|
226
151
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
compress_request_body decompress_response_body
|
233
|
-
persistent close_on_fork
|
234
|
-
].each do |method_name|
|
235
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
236
|
-
# sets +v+ as the value of the +#{method_name}+ option
|
237
|
-
def option_#{method_name}(v); v; end # def option_smth(v); v; end
|
238
|
-
OUT
|
152
|
+
def freeze
|
153
|
+
self.class.options_names.each do |ivar|
|
154
|
+
instance_variable_get(:"@#{ivar}").freeze
|
155
|
+
end
|
156
|
+
super
|
239
157
|
end
|
240
158
|
|
241
159
|
REQUEST_BODY_IVARS = %i[@headers].freeze
|
@@ -262,11 +180,12 @@ module HTTPX
|
|
262
180
|
def merge(other)
|
263
181
|
ivar_map = nil
|
264
182
|
other_ivars = case other
|
265
|
-
when
|
183
|
+
when Options
|
184
|
+
other.instance_variables
|
185
|
+
else
|
186
|
+
other = Hash[other] unless other.is_a?(Hash)
|
266
187
|
ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
|
267
188
|
ivar_map.keys
|
268
|
-
else
|
269
|
-
other.instance_variables
|
270
189
|
end
|
271
190
|
|
272
191
|
return self if other_ivars.empty?
|
@@ -297,70 +216,177 @@ module HTTPX
|
|
297
216
|
|
298
217
|
def to_hash
|
299
218
|
instance_variables.each_with_object({}) do |ivar, hs|
|
300
|
-
|
219
|
+
val = instance_variable_get(ivar)
|
220
|
+
|
221
|
+
next if val.nil?
|
222
|
+
|
223
|
+
hs[ivar[1..-1].to_sym] = val
|
301
224
|
end
|
302
225
|
end
|
303
226
|
|
304
227
|
def extend_with_plugin_classes(pl)
|
228
|
+
# extend request class
|
305
229
|
if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
|
306
230
|
@request_class = @request_class.dup
|
307
231
|
SET_TEMPORARY_NAME[@request_class, pl]
|
308
232
|
@request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
|
309
233
|
@request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
310
234
|
end
|
235
|
+
# extend response class
|
311
236
|
if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
|
312
237
|
@response_class = @response_class.dup
|
313
238
|
SET_TEMPORARY_NAME[@response_class, pl]
|
314
239
|
@response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
315
240
|
@response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
316
241
|
end
|
242
|
+
# extend headers class
|
317
243
|
if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
|
318
244
|
@headers_class = @headers_class.dup
|
319
245
|
SET_TEMPORARY_NAME[@headers_class, pl]
|
320
246
|
@headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
321
247
|
@headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
322
248
|
end
|
249
|
+
# extend request body class
|
323
250
|
if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
|
324
251
|
@request_body_class = @request_body_class.dup
|
325
252
|
SET_TEMPORARY_NAME[@request_body_class, pl]
|
326
253
|
@request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
327
254
|
@request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
328
255
|
end
|
256
|
+
# extend response body class
|
329
257
|
if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
|
330
258
|
@response_body_class = @response_body_class.dup
|
331
259
|
SET_TEMPORARY_NAME[@response_body_class, pl]
|
332
260
|
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
333
261
|
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
334
262
|
end
|
263
|
+
# extend connection pool class
|
335
264
|
if defined?(pl::PoolMethods)
|
336
265
|
@pool_class = @pool_class.dup
|
337
266
|
SET_TEMPORARY_NAME[@pool_class, pl]
|
338
267
|
@pool_class.__send__(:include, pl::PoolMethods)
|
339
268
|
end
|
269
|
+
# extend connection class
|
340
270
|
if defined?(pl::ConnectionMethods)
|
341
271
|
@connection_class = @connection_class.dup
|
342
272
|
SET_TEMPORARY_NAME[@connection_class, pl]
|
343
273
|
@connection_class.__send__(:include, pl::ConnectionMethods)
|
344
274
|
end
|
275
|
+
# extend http1 class
|
276
|
+
if defined?(pl::HTTP1Methods)
|
277
|
+
@http1_class = @http1_class.dup
|
278
|
+
SET_TEMPORARY_NAME[@http1_class, pl]
|
279
|
+
@http1_class.__send__(:include, pl::HTTP1Methods)
|
280
|
+
end
|
281
|
+
# extend http2 class
|
282
|
+
if defined?(pl::HTTP2Methods)
|
283
|
+
@http2_class = @http2_class.dup
|
284
|
+
SET_TEMPORARY_NAME[@http2_class, pl]
|
285
|
+
@http2_class.__send__(:include, pl::HTTP2Methods)
|
286
|
+
end
|
287
|
+
# extend native resolver class
|
288
|
+
if defined?(pl::ResolverNativeMethods)
|
289
|
+
@resolver_native_class = @resolver_native_class.dup
|
290
|
+
SET_TEMPORARY_NAME[@resolver_native_class, pl]
|
291
|
+
@resolver_native_class.__send__(:include, pl::ResolverNativeMethods)
|
292
|
+
end
|
293
|
+
# extend system resolver class
|
294
|
+
if defined?(pl::ResolverSystemMethods)
|
295
|
+
@resolver_system_class = @resolver_system_class.dup
|
296
|
+
SET_TEMPORARY_NAME[@resolver_system_class, pl]
|
297
|
+
@resolver_system_class.__send__(:include, pl::ResolverSystemMethods)
|
298
|
+
end
|
299
|
+
# extend https resolver class
|
300
|
+
if defined?(pl::ResolverHTTPSMethods)
|
301
|
+
@resolver_https_class = @resolver_https_class.dup
|
302
|
+
SET_TEMPORARY_NAME[@resolver_https_class, pl]
|
303
|
+
@resolver_https_class.__send__(:include, pl::ResolverHTTPSMethods)
|
304
|
+
end
|
305
|
+
|
345
306
|
return unless defined?(pl::OptionsMethods)
|
346
307
|
|
308
|
+
# extend option class
|
309
|
+
# works around lack of initialize_dup callback
|
347
310
|
@options_class = @options_class.dup
|
311
|
+
# (self.class.options_names)
|
348
312
|
@options_class.__send__(:include, pl::OptionsMethods)
|
349
313
|
end
|
350
314
|
|
351
315
|
private
|
352
316
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
317
|
+
# number options
|
318
|
+
%i[
|
319
|
+
max_concurrent_requests max_requests window_size buffer_size
|
320
|
+
body_threshold_size debug_level
|
321
|
+
].each do |option|
|
322
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
323
|
+
# converts +v+ into an Integer before setting the +#{option}+ option.
|
324
|
+
private def option_#{option}(value) # private def option_max_requests(v)
|
325
|
+
value = Integer(value) unless value.respond_to?(:infinite?) && value.infinite?
|
326
|
+
raise TypeError, ":#{option} must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive?
|
357
327
|
|
358
|
-
|
359
|
-
|
328
|
+
value
|
329
|
+
end
|
330
|
+
OUT
|
331
|
+
end
|
360
332
|
|
361
|
-
|
362
|
-
|
363
|
-
|
333
|
+
# hashable options
|
334
|
+
%i[ssl http2_settings resolver_options pool_options].each do |option|
|
335
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
336
|
+
# converts +v+ into an Hash before setting the +#{option}+ option.
|
337
|
+
private def option_#{option}(value) # def option_ssl(v)
|
338
|
+
Hash[value]
|
339
|
+
end
|
340
|
+
OUT
|
341
|
+
end
|
342
|
+
|
343
|
+
%i[
|
344
|
+
request_class response_class headers_class request_body_class
|
345
|
+
response_body_class connection_class http1_class http2_class
|
346
|
+
resolver_native_class resolver_system_class resolver_https_class options_class pool_class
|
347
|
+
io fallback_protocol debug debug_redact resolver_class
|
348
|
+
compress_request_body decompress_response_body
|
349
|
+
persistent close_on_fork
|
350
|
+
].each do |method_name|
|
351
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
352
|
+
# sets +v+ as the value of the +#{method_name}+ option
|
353
|
+
private def option_#{method_name}(v); v; end # private def option_smth(v); v; end
|
354
|
+
OUT
|
355
|
+
end
|
356
|
+
|
357
|
+
def option_origin(value)
|
358
|
+
URI(value)
|
359
|
+
end
|
360
|
+
|
361
|
+
def option_base_path(value)
|
362
|
+
String(value)
|
363
|
+
end
|
364
|
+
|
365
|
+
def option_headers(value)
|
366
|
+
headers_class.new(value)
|
367
|
+
end
|
368
|
+
|
369
|
+
def option_timeout(value)
|
370
|
+
Hash[value]
|
371
|
+
end
|
372
|
+
|
373
|
+
def option_supported_compression_formats(value)
|
374
|
+
Array(value).map(&:to_s)
|
375
|
+
end
|
376
|
+
|
377
|
+
def option_transport(value)
|
378
|
+
transport = value.to_s
|
379
|
+
raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)
|
380
|
+
|
381
|
+
transport
|
382
|
+
end
|
383
|
+
|
384
|
+
def option_addresses(value)
|
385
|
+
Array(value).map { |entry| Resolver::Entry.convert(entry) }
|
386
|
+
end
|
387
|
+
|
388
|
+
def option_ip_families(value)
|
389
|
+
Array(value)
|
364
390
|
end
|
365
391
|
|
366
392
|
def access_option(obj, k, ivar_map)
|
@@ -371,5 +397,73 @@ module HTTPX
|
|
371
397
|
obj.instance_variable_get(k)
|
372
398
|
end
|
373
399
|
end
|
400
|
+
|
401
|
+
SET_TEMPORARY_NAME = ->(klass, pl = nil) do
|
402
|
+
if klass.respond_to?(:set_temporary_name) # ruby 3.4 only
|
403
|
+
name = klass.name || "#{klass.superclass.name}(plugin)"
|
404
|
+
name = "#{name}/#{pl}" if pl
|
405
|
+
klass.set_temporary_name(name)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
|
410
|
+
ip_address_families = begin
|
411
|
+
list = Socket.ip_address_list
|
412
|
+
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
|
413
|
+
[Socket::AF_INET6, Socket::AF_INET]
|
414
|
+
else
|
415
|
+
[Socket::AF_INET]
|
416
|
+
end
|
417
|
+
rescue NotImplementedError
|
418
|
+
[Socket::AF_INET]
|
419
|
+
end.freeze
|
420
|
+
|
421
|
+
DEFAULT_OPTIONS = {
|
422
|
+
:max_requests => Float::INFINITY,
|
423
|
+
:debug => nil,
|
424
|
+
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
425
|
+
:debug_redact => ENV.key?("HTTPX_DEBUG_REDACT"),
|
426
|
+
:ssl => EMPTY_HASH,
|
427
|
+
:http2_settings => { settings_enable_push: 0 }.freeze,
|
428
|
+
:fallback_protocol => "http/1.1",
|
429
|
+
:supported_compression_formats => %w[gzip deflate],
|
430
|
+
:decompress_response_body => true,
|
431
|
+
:compress_request_body => true,
|
432
|
+
:timeout => {
|
433
|
+
connect_timeout: CONNECT_TIMEOUT,
|
434
|
+
settings_timeout: SETTINGS_TIMEOUT,
|
435
|
+
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
436
|
+
operation_timeout: OPERATION_TIMEOUT,
|
437
|
+
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
438
|
+
read_timeout: READ_TIMEOUT,
|
439
|
+
write_timeout: WRITE_TIMEOUT,
|
440
|
+
request_timeout: REQUEST_TIMEOUT,
|
441
|
+
}.freeze,
|
442
|
+
:headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
|
443
|
+
:headers => EMPTY_HASH,
|
444
|
+
:window_size => WINDOW_SIZE,
|
445
|
+
:buffer_size => BUFFER_SIZE,
|
446
|
+
:body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
|
447
|
+
:request_class => Class.new(Request, &SET_TEMPORARY_NAME),
|
448
|
+
:response_class => Class.new(Response, &SET_TEMPORARY_NAME),
|
449
|
+
:request_body_class => Class.new(Request::Body, &SET_TEMPORARY_NAME),
|
450
|
+
:response_body_class => Class.new(Response::Body, &SET_TEMPORARY_NAME),
|
451
|
+
:pool_class => Class.new(Pool, &SET_TEMPORARY_NAME),
|
452
|
+
:connection_class => Class.new(Connection, &SET_TEMPORARY_NAME),
|
453
|
+
:http1_class => Class.new(Connection::HTTP1, &SET_TEMPORARY_NAME),
|
454
|
+
:http2_class => Class.new(Connection::HTTP2, &SET_TEMPORARY_NAME),
|
455
|
+
:resolver_native_class => Class.new(Resolver::Native, &SET_TEMPORARY_NAME),
|
456
|
+
:resolver_system_class => Class.new(Resolver::System, &SET_TEMPORARY_NAME),
|
457
|
+
:resolver_https_class => Class.new(Resolver::HTTPS, &SET_TEMPORARY_NAME),
|
458
|
+
:options_class => Class.new(self, &SET_TEMPORARY_NAME),
|
459
|
+
:transport => nil,
|
460
|
+
:addresses => nil,
|
461
|
+
:persistent => false,
|
462
|
+
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
463
|
+
:resolver_options => { cache: true }.freeze,
|
464
|
+
:pool_options => EMPTY_HASH,
|
465
|
+
:ip_families => ip_address_families,
|
466
|
+
:close_on_fork => false,
|
467
|
+
}.freeze
|
374
468
|
end
|
375
469
|
end
|
@@ -32,6 +32,18 @@ module HTTPX
|
|
32
32
|
MOD
|
33
33
|
end
|
34
34
|
|
35
|
+
def plugin(*args, &blk)
|
36
|
+
super(*args).tap do |sess|
|
37
|
+
CALLBACKS.each do |cb|
|
38
|
+
next unless callbacks_for?(cb)
|
39
|
+
|
40
|
+
sess.callbacks(cb).concat(callbacks(cb))
|
41
|
+
end
|
42
|
+
|
43
|
+
sess.wrap(&blk) if blk
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
35
47
|
private
|
36
48
|
|
37
49
|
def branch(options, &blk)
|
@@ -85,7 +97,7 @@ module HTTPX
|
|
85
97
|
end
|
86
98
|
end
|
87
99
|
request.on(:response) do |res|
|
88
|
-
emit_or_callback_error(:response_completed, request, res)
|
100
|
+
emit_or_callback_error(:response_completed, request, res) if res.is_a?(Response)
|
89
101
|
end
|
90
102
|
end
|
91
103
|
|
@@ -105,6 +105,8 @@ module HTTPX
|
|
105
105
|
# :circuit_breaker_half_open_drip_rate :: the rate of requests a circuit allows to be performed when in an half-open state
|
106
106
|
# (defaults to <tt>1</tt>).
|
107
107
|
module OptionsMethods
|
108
|
+
private
|
109
|
+
|
108
110
|
def option_circuit_breaker_max_attempts(value)
|
109
111
|
attempts = Integer(value)
|
110
112
|
raise TypeError, ":circuit_breaker_max_attempts must be positive" unless attempts.positive?
|
@@ -43,6 +43,8 @@ module HTTPX
|
|
43
43
|
# :validate_content_digest :: whether a <tt>Content-Digest</tt> header in the response should be validated;
|
44
44
|
# can also be a callable object (i.e. <tt>->(res) { ... }</tt>, defaults to <tt>false</tt>)
|
45
45
|
module OptionsMethods
|
46
|
+
private
|
47
|
+
|
46
48
|
def option_content_digest_algorithm(value)
|
47
49
|
raise TypeError, ":content_digest_algorithm must be one of 'sha-256', 'sha-512'" unless SUPPORTED_ALGORITHMS.key?(value)
|
48
50
|
|
@@ -74,6 +74,8 @@ module HTTPX
|
|
74
74
|
#
|
75
75
|
# :cookies :: cookie jar for the session (can be a Hash, an Array, an instance of HTTPX::Plugins::Cookies::CookieJar)
|
76
76
|
module OptionsMethods
|
77
|
+
private
|
78
|
+
|
77
79
|
def option_headers(*)
|
78
80
|
value = super
|
79
81
|
|
@@ -90,8 +92,6 @@ module HTTPX
|
|
90
92
|
jar
|
91
93
|
end
|
92
94
|
|
93
|
-
private
|
94
|
-
|
95
95
|
def merge_cookie_in_jar(cookies, jar)
|
96
96
|
cookies.each do |ck|
|
97
97
|
ck.split(/ *; */).each do |cookie|
|
@@ -24,6 +24,8 @@ module HTTPX
|
|
24
24
|
#
|
25
25
|
# :digest :: instance of HTTPX::Plugins::Authentication::Digest, used to authenticate requests in the session.
|
26
26
|
module OptionsMethods
|
27
|
+
private
|
28
|
+
|
27
29
|
def option_digest(value)
|
28
30
|
raise TypeError, ":digest must be a #{Authentication::Digest}" unless value.is_a?(Authentication::Digest)
|
29
31
|
|
data/lib/httpx/plugins/expect.rb
CHANGED
@@ -26,6 +26,8 @@ module HTTPX
|
|
26
26
|
# before retrying without the Expect header (defaults to <tt>2</tt>).
|
27
27
|
# :expect_threshold_size :: min threshold (in bytes) of the request payload to enable the 100-continue negotiation on.
|
28
28
|
module OptionsMethods
|
29
|
+
private
|
30
|
+
|
29
31
|
def option_expect_timeout(value)
|
30
32
|
seconds = Float(value)
|
31
33
|
raise TypeError, ":expect_timeout must be positive" unless seconds.positive?
|