httpx 1.1.5 → 1.2.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/README.md +5 -5
- data/doc/release_notes/1_2_0.md +49 -0
- data/lib/httpx/adapters/webmock.rb +25 -3
- data/lib/httpx/altsvc.rb +57 -2
- data/lib/httpx/chainable.rb +40 -29
- data/lib/httpx/connection/http1.rb +27 -22
- data/lib/httpx/connection/http2.rb +7 -3
- data/lib/httpx/connection.rb +45 -60
- data/lib/httpx/extensions.rb +0 -15
- data/lib/httpx/options.rb +84 -27
- data/lib/httpx/plugins/aws_sigv4.rb +2 -2
- data/lib/httpx/plugins/basic_auth.rb +1 -1
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +19 -9
- data/lib/httpx/plugins/digest_auth.rb +1 -1
- data/lib/httpx/plugins/follow_redirects.rb +11 -0
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/h2c.rb +20 -8
- data/lib/httpx/plugins/proxy/socks4.rb +2 -2
- data/lib/httpx/plugins/proxy/socks5.rb +2 -2
- data/lib/httpx/plugins/proxy.rb +14 -32
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +4 -0
- data/lib/httpx/plugins/ssrf_filter.rb +142 -0
- data/lib/httpx/plugins/stream.rb +1 -1
- data/lib/httpx/plugins/upgrade/h2.rb +1 -1
- data/lib/httpx/plugins/upgrade.rb +1 -1
- data/lib/httpx/plugins/webdav.rb +1 -1
- data/lib/httpx/pool.rb +32 -28
- data/lib/httpx/request/body.rb +3 -3
- data/lib/httpx/request.rb +3 -5
- data/lib/httpx/resolver/https.rb +3 -2
- data/lib/httpx/resolver/native.rb +1 -0
- data/lib/httpx/resolver/resolver.rb +17 -6
- data/lib/httpx/session.rb +13 -82
- data/lib/httpx/timers.rb +3 -10
- data/lib/httpx/transcoder.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +33 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -1
- data/sig/connection.rbs +16 -16
- data/sig/options.rbs +10 -2
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/follow_redirects.rbs +2 -0
- data/sig/plugins/proxy/socks4.rbs +2 -1
- data/sig/plugins/proxy/socks5.rbs +2 -1
- data/sig/plugins/proxy.rbs +11 -1
- data/sig/pool.rbs +1 -3
- data/sig/resolver/resolver.rbs +3 -1
- data/sig/session.rbs +4 -4
- metadata +10 -4
data/lib/httpx/options.rb
CHANGED
@@ -11,6 +11,7 @@ module HTTPX
|
|
11
11
|
MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
|
12
12
|
KEEP_ALIVE_TIMEOUT = 20
|
13
13
|
SETTINGS_TIMEOUT = 10
|
14
|
+
CLOSE_HANDSHAKE_TIMEOUT = 10
|
14
15
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
15
16
|
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
16
17
|
|
@@ -39,6 +40,7 @@ module HTTPX
|
|
39
40
|
:timeout => {
|
40
41
|
connect_timeout: CONNECT_TIMEOUT,
|
41
42
|
settings_timeout: SETTINGS_TIMEOUT,
|
43
|
+
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
42
44
|
operation_timeout: OPERATION_TIMEOUT,
|
43
45
|
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
44
46
|
read_timeout: READ_TIMEOUT,
|
@@ -226,44 +228,69 @@ module HTTPX
|
|
226
228
|
OUT
|
227
229
|
end
|
228
230
|
|
229
|
-
|
230
|
-
private_constant :REQUEST_IVARS
|
231
|
+
REQUEST_BODY_IVARS = %i[@headers @params @form @xml @json @body].freeze
|
231
232
|
|
232
233
|
def ==(other)
|
233
|
-
|
234
|
+
super || options_equals?(other)
|
235
|
+
end
|
236
|
+
|
237
|
+
def options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS)
|
238
|
+
# headers and other request options do not play a role, as they are
|
239
|
+
# relevant only for the request.
|
240
|
+
ivars = instance_variables - ignore_ivars
|
241
|
+
other_ivars = other.instance_variables - ignore_ivars
|
242
|
+
|
243
|
+
return false if ivars.size != other_ivars.size
|
244
|
+
|
245
|
+
return false if ivars.sort != other_ivars.sort
|
246
|
+
|
234
247
|
ivars.all? do |ivar|
|
235
|
-
|
236
|
-
when :@headers
|
237
|
-
# currently, this is used to pick up an available matching connection.
|
238
|
-
# the headers do not play a role, as they are relevant only for the request.
|
239
|
-
true
|
240
|
-
when *REQUEST_IVARS
|
241
|
-
true
|
242
|
-
else
|
243
|
-
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
244
|
-
end
|
248
|
+
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
245
249
|
end
|
246
250
|
end
|
247
251
|
|
252
|
+
OTHER_LOOKUP = ->(obj, k, ivar_map) {
|
253
|
+
case obj
|
254
|
+
when Hash
|
255
|
+
obj[ivar_map[k]]
|
256
|
+
else
|
257
|
+
obj.instance_variable_get(k)
|
258
|
+
end
|
259
|
+
}
|
248
260
|
def merge(other)
|
249
|
-
|
261
|
+
ivar_map = nil
|
262
|
+
other_ivars = case other
|
263
|
+
when Hash
|
264
|
+
ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
|
265
|
+
ivar_map.keys
|
266
|
+
else
|
267
|
+
other.instance_variables
|
268
|
+
end
|
269
|
+
|
270
|
+
return self if other_ivars.empty?
|
250
271
|
|
251
|
-
|
252
|
-
return self if h2.empty?
|
272
|
+
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == OTHER_LOOKUP[other, ivar, ivar_map] }
|
253
273
|
|
254
|
-
|
274
|
+
opts = dup
|
255
275
|
|
256
|
-
|
276
|
+
other_ivars.each do |ivar|
|
277
|
+
v = OTHER_LOOKUP[other, ivar, ivar_map]
|
257
278
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
else
|
262
|
-
v2
|
279
|
+
unless v
|
280
|
+
opts.instance_variable_set(ivar, v)
|
281
|
+
next
|
263
282
|
end
|
283
|
+
|
284
|
+
v = opts.__send__(:"option_#{ivar[1..-1]}", v)
|
285
|
+
|
286
|
+
orig_v = instance_variable_get(ivar)
|
287
|
+
|
288
|
+
v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)
|
289
|
+
|
290
|
+
opts.instance_variable_set(ivar, v)
|
264
291
|
end
|
265
292
|
|
266
|
-
|
293
|
+
opts
|
267
294
|
end
|
268
295
|
|
269
296
|
def to_hash
|
@@ -272,10 +299,40 @@ module HTTPX
|
|
272
299
|
end
|
273
300
|
end
|
274
301
|
|
275
|
-
def
|
276
|
-
|
277
|
-
|
302
|
+
def extend_with_plugin_classes(pl)
|
303
|
+
if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
|
304
|
+
@request_class = @request_class.dup
|
305
|
+
@request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
|
306
|
+
@request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
307
|
+
end
|
308
|
+
if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
|
309
|
+
@response_class = @response_class.dup
|
310
|
+
@response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
311
|
+
@response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
278
312
|
end
|
313
|
+
if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
|
314
|
+
@headers_class = @headers_class.dup
|
315
|
+
@headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
316
|
+
@headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
317
|
+
end
|
318
|
+
if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
|
319
|
+
@request_body_class = @request_body_class.dup
|
320
|
+
@request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
321
|
+
@request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
322
|
+
end
|
323
|
+
if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
|
324
|
+
@response_body_class = @response_body_class.dup
|
325
|
+
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
326
|
+
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
327
|
+
end
|
328
|
+
if defined?(pl::ConnectionMethods)
|
329
|
+
@connection_class = @connection_class.dup
|
330
|
+
@connection_class.__send__(:include, pl::ConnectionMethods)
|
331
|
+
end
|
332
|
+
return unless defined?(pl::OptionsMethods)
|
333
|
+
|
334
|
+
@options_class = @options_class.dup
|
335
|
+
@options_class.__send__(:include, pl::OptionsMethods)
|
279
336
|
end
|
280
337
|
|
281
338
|
private
|
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin adds AWS Sigv4 authentication.
|
7
7
|
#
|
8
|
-
# https://docs.aws.amazon.com/
|
8
|
+
# https://docs.aws.amazon.com/IAM/latest/UserGuide/signing-elements.html
|
9
9
|
#
|
10
10
|
# https://gitlab.com/os85/httpx/wikis/AWS-SigV4
|
11
11
|
#
|
@@ -185,7 +185,7 @@ module HTTPX
|
|
185
185
|
def canonical_query
|
186
186
|
params = query.split("&")
|
187
187
|
# params = params.map { |p| p.match(/=/) ? p : p + '=' }
|
188
|
-
# From: https://docs.aws.amazon.com/
|
188
|
+
# From: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html#create-canonical-request
|
189
189
|
# Sort the parameter names by character code point in ascending order.
|
190
190
|
# Parameters with duplicate names should be sorted by value.
|
191
191
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds helper methods to implement HTTP Basic Auth (https://
|
6
|
+
# This plugin adds helper methods to implement HTTP Basic Auth (https://datatracker.ietf.org/doc/html/rfc7617)
|
7
7
|
#
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/Auth#basic-auth
|
9
9
|
#
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin adds suppoort for callbacks around the request/response lifecycle.
|
7
|
+
#
|
8
|
+
# https://gitlab.com/os85/httpx/-/wikis/Events
|
9
|
+
#
|
10
|
+
module Callbacks
|
11
|
+
# connection closed user-space errors happen after errors can be surfaced to requests,
|
12
|
+
# so they need to pierce through the scheduler, which is only possible by simulating an
|
13
|
+
# interrupt.
|
14
|
+
class CallbackError < Exception; end # rubocop:disable Lint/InheritException
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
include HTTPX::Callbacks
|
18
|
+
|
19
|
+
%i[
|
20
|
+
connection_opened connection_closed
|
21
|
+
request_error
|
22
|
+
request_started request_body_chunk request_completed
|
23
|
+
response_started response_body_chunk response_completed
|
24
|
+
].each do |meth|
|
25
|
+
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
26
|
+
def on_#{meth}(&blk) # def on_connection_opened(&blk)
|
27
|
+
on(:#{meth}, &blk) # on(:connection_opened, &blk)
|
28
|
+
end # end
|
29
|
+
MOD
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def init_connection(uri, options)
|
35
|
+
connection = super
|
36
|
+
connection.on(:open) do
|
37
|
+
emit_or_callback_error(:connection_opened, connection.origin, connection.io.socket)
|
38
|
+
end
|
39
|
+
connection.on(:close) do
|
40
|
+
emit_or_callback_error(:connection_closed, connection.origin) if connection.used?
|
41
|
+
end
|
42
|
+
|
43
|
+
connection
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_request_callbacks(request)
|
47
|
+
super
|
48
|
+
|
49
|
+
request.on(:headers) do
|
50
|
+
emit_or_callback_error(:request_started, request)
|
51
|
+
end
|
52
|
+
request.on(:body_chunk) do |chunk|
|
53
|
+
emit_or_callback_error(:request_body_chunk, request, chunk)
|
54
|
+
end
|
55
|
+
request.on(:done) do
|
56
|
+
emit_or_callback_error(:request_completed, request)
|
57
|
+
end
|
58
|
+
|
59
|
+
request.on(:response_started) do |res|
|
60
|
+
if res.is_a?(Response)
|
61
|
+
emit_or_callback_error(:response_started, request, res)
|
62
|
+
res.on(:chunk_received) do |chunk|
|
63
|
+
emit_or_callback_error(:response_body_chunk, request, res, chunk)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
emit_or_callback_error(:request_error, request, res.error)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
request.on(:response) do |res|
|
70
|
+
emit_or_callback_error(:response_completed, request, res)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def emit_or_callback_error(*args)
|
75
|
+
emit(*args)
|
76
|
+
rescue StandardError => e
|
77
|
+
ex = CallbackError.new(e.message)
|
78
|
+
ex.set_backtrace(e.backtrace)
|
79
|
+
raise ex
|
80
|
+
end
|
81
|
+
|
82
|
+
def receive_requests(*)
|
83
|
+
super
|
84
|
+
rescue CallbackError => e
|
85
|
+
raise e.cause
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
register_plugin :callbacks, Callbacks
|
90
|
+
end
|
91
|
+
end
|
@@ -71,22 +71,32 @@ module HTTPX
|
|
71
71
|
end
|
72
72
|
|
73
73
|
module OptionsMethods
|
74
|
-
def
|
75
|
-
super
|
74
|
+
def option_headers(*)
|
75
|
+
value = super
|
76
|
+
|
77
|
+
merge_cookie_in_jar(value.delete("cookie"), @cookies) if defined?(@cookies) && value.key?("cookie")
|
76
78
|
|
77
|
-
|
79
|
+
value
|
80
|
+
end
|
78
81
|
|
79
|
-
|
82
|
+
def option_cookies(value)
|
83
|
+
jar = value.is_a?(Jar) ? value : Jar.new(value)
|
84
|
+
|
85
|
+
merge_cookie_in_jar(@headers.delete("cookie"), jar) if defined?(@headers) && @headers.key?("cookie")
|
86
|
+
|
87
|
+
jar
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def merge_cookie_in_jar(cookies, jar)
|
93
|
+
cookies.each do |ck|
|
80
94
|
ck.split(/ *; */).each do |cookie|
|
81
95
|
name, value = cookie.split("=", 2)
|
82
|
-
|
96
|
+
jar.add(Cookie.new(name, value))
|
83
97
|
end
|
84
98
|
end
|
85
99
|
end
|
86
|
-
|
87
|
-
def option_cookies(value)
|
88
|
-
value.is_a?(Jar) ? value : Jar.new(value)
|
89
|
-
end
|
90
100
|
end
|
91
101
|
end
|
92
102
|
register_plugin :cookies, Cookies
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds helper methods to implement HTTP Digest Auth (https://
|
6
|
+
# This plugin adds helper methods to implement HTTP Digest Auth (https://datatracker.ietf.org/doc/html/rfc7616)
|
7
7
|
#
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/Auth#digest-auth
|
9
9
|
#
|
@@ -34,6 +34,12 @@ module HTTPX
|
|
34
34
|
def option_allow_auth_to_other_origins(value)
|
35
35
|
value
|
36
36
|
end
|
37
|
+
|
38
|
+
def option_redirect_on(value)
|
39
|
+
raise TypeError, ":redirect_on must be callable" unless value.respond_to?(:call)
|
40
|
+
|
41
|
+
value
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
39
45
|
module InstanceMethods
|
@@ -57,6 +63,11 @@ module HTTPX
|
|
57
63
|
# build redirect request
|
58
64
|
redirect_uri = __get_location_from_response(response)
|
59
65
|
|
66
|
+
if options.redirect_on
|
67
|
+
redirect_allowed = options.redirect_on.call(redirect_uri)
|
68
|
+
return response unless redirect_allowed
|
69
|
+
end
|
70
|
+
|
60
71
|
if response.status == 305 && options.respond_to?(:proxy)
|
61
72
|
# The requested resource MUST be accessed through the proxy given by
|
62
73
|
# the Location field. The Location field gives the URI of the proxy.
|
data/lib/httpx/plugins/grpc.rb
CHANGED
@@ -261,14 +261,14 @@ module HTTPX
|
|
261
261
|
headers["grpc-timeout"] = "#{deadline}m"
|
262
262
|
end
|
263
263
|
|
264
|
-
headers = headers.merge(metadata) if metadata
|
264
|
+
headers = headers.merge(metadata.transform_keys(&:to_s)) if metadata
|
265
265
|
|
266
266
|
# prepare compressor
|
267
267
|
compression = @options.grpc_compression == true ? "gzip" : @options.grpc_compression
|
268
268
|
|
269
269
|
headers["grpc-encoding"] = compression if compression
|
270
270
|
|
271
|
-
headers.merge!(@options.call_credentials.call) if @options.call_credentials
|
271
|
+
headers.merge!(@options.call_credentials.call.transform_keys(&:to_s)) if @options.call_credentials
|
272
272
|
|
273
273
|
build_request("POST", uri, headers: headers, body: input)
|
274
274
|
end
|
data/lib/httpx/plugins/h2c.rb
CHANGED
@@ -4,9 +4,9 @@ module HTTPX
|
|
4
4
|
module Plugins
|
5
5
|
#
|
6
6
|
# This plugin adds support for upgrading a plaintext HTTP/1.1 connection to HTTP/2
|
7
|
-
# (https://
|
7
|
+
# (https://datatracker.ietf.org/doc/html/rfc7540#section-3.2)
|
8
8
|
#
|
9
|
-
# https://gitlab.com/os85/httpx/wikis/Upgrade#h2c
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2c
|
10
10
|
#
|
11
11
|
module H2C
|
12
12
|
VALID_H2C_VERBS = %w[GET OPTIONS HEAD].freeze
|
@@ -73,22 +73,34 @@ module HTTPX
|
|
73
73
|
@inflight -= prev_parser.requests.size
|
74
74
|
end
|
75
75
|
|
76
|
-
|
77
|
-
@parser = H2CParser.new(@write_buffer, parser_options)
|
76
|
+
@parser = H2CParser.new(@write_buffer, @options)
|
78
77
|
set_parser_callbacks(@parser)
|
79
78
|
@inflight += 1
|
80
79
|
@parser.upgrade(request, response)
|
81
80
|
@upgrade_protocol = "h2c"
|
82
81
|
|
83
|
-
if request.options.max_concurrent_requests != @options.max_concurrent_requests
|
84
|
-
@options = @options.merge(max_concurrent_requests: nil)
|
85
|
-
end
|
86
|
-
|
87
82
|
prev_parser.requests.each do |req|
|
88
83
|
req.transition(:idle)
|
89
84
|
send(req)
|
90
85
|
end
|
91
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def send_request_to_parser(request)
|
91
|
+
super
|
92
|
+
|
93
|
+
return unless request.headers["upgrade"] == "h2c" && parser.is_a?(Connection::HTTP1)
|
94
|
+
|
95
|
+
max_concurrent_requests = parser.max_concurrent_requests
|
96
|
+
|
97
|
+
return if max_concurrent_requests == 1
|
98
|
+
|
99
|
+
parser.max_concurrent_requests = 1
|
100
|
+
request.once(:response) do
|
101
|
+
parser.max_concurrent_requests = max_concurrent_requests
|
102
|
+
end
|
103
|
+
end
|
92
104
|
end
|
93
105
|
end
|
94
106
|
register_plugin(:h2c, H2C)
|
@@ -4,7 +4,7 @@ require "resolv"
|
|
4
4
|
require "ipaddr"
|
5
5
|
|
6
6
|
module HTTPX
|
7
|
-
class Socks4Error <
|
7
|
+
class Socks4Error < HTTPProxyError; end
|
8
8
|
|
9
9
|
module Plugins
|
10
10
|
module Proxy
|
@@ -85,7 +85,7 @@ module HTTPX
|
|
85
85
|
end
|
86
86
|
|
87
87
|
class SocksParser
|
88
|
-
include Callbacks
|
88
|
+
include HTTPX::Callbacks
|
89
89
|
|
90
90
|
def initialize(buffer, options)
|
91
91
|
@buffer = buffer
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
-
class Socks5Error <
|
4
|
+
class Socks5Error < HTTPProxyError; end
|
5
5
|
|
6
6
|
module Plugins
|
7
7
|
module Proxy
|
@@ -137,7 +137,7 @@ module HTTPX
|
|
137
137
|
end
|
138
138
|
|
139
139
|
class SocksParser
|
140
|
-
include Callbacks
|
140
|
+
include HTTPX::Callbacks
|
141
141
|
|
142
142
|
def initialize(buffer, options)
|
143
143
|
@buffer = buffer
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
-
class HTTPProxyError <
|
4
|
+
class HTTPProxyError < ConnectionError; end
|
5
5
|
|
6
6
|
module Plugins
|
7
7
|
#
|
@@ -143,7 +143,7 @@ module HTTPX
|
|
143
143
|
proxy = Parameters.new(**proxy_opts)
|
144
144
|
|
145
145
|
proxy_options = options.merge(proxy: proxy)
|
146
|
-
connection = pool.find_connection(uri, proxy_options) ||
|
146
|
+
connection = pool.find_connection(uri, proxy_options) || init_connection(uri, proxy_options)
|
147
147
|
unless connections.nil? || connections.include?(connection)
|
148
148
|
connections << connection
|
149
149
|
set_connection_callbacks(connection, connections, options)
|
@@ -151,19 +151,15 @@ module HTTPX
|
|
151
151
|
connection
|
152
152
|
end
|
153
153
|
|
154
|
-
def build_connection(uri, options)
|
155
|
-
proxy = options.proxy
|
156
|
-
return super unless proxy
|
157
|
-
|
158
|
-
init_connection("tcp", uri, options)
|
159
|
-
end
|
160
|
-
|
161
154
|
def fetch_response(request, connections, options)
|
162
155
|
response = super
|
163
156
|
|
164
|
-
if response.is_a?(ErrorResponse) &&
|
165
|
-
__proxy_error?(response) && !@_proxy_uris.empty?
|
157
|
+
if response.is_a?(ErrorResponse) && proxy_error?(request, response)
|
166
158
|
@_proxy_uris.shift
|
159
|
+
|
160
|
+
# return last error response if no more proxies to try
|
161
|
+
return response if @_proxy_uris.empty?
|
162
|
+
|
167
163
|
log { "failed connecting to proxy, trying next..." }
|
168
164
|
request.transition(:idle)
|
169
165
|
send_request(request, connections, options)
|
@@ -172,13 +168,7 @@ module HTTPX
|
|
172
168
|
response
|
173
169
|
end
|
174
170
|
|
175
|
-
def
|
176
|
-
return if options.proxy
|
177
|
-
|
178
|
-
super
|
179
|
-
end
|
180
|
-
|
181
|
-
def __proxy_error?(response)
|
171
|
+
def proxy_error?(_request, response)
|
182
172
|
error = response.error
|
183
173
|
case error
|
184
174
|
when NativeResolveError
|
@@ -235,14 +225,6 @@ module HTTPX
|
|
235
225
|
end
|
236
226
|
end
|
237
227
|
|
238
|
-
def send(request)
|
239
|
-
return super unless (
|
240
|
-
@options.proxy && @state != :idle && connecting?
|
241
|
-
)
|
242
|
-
|
243
|
-
(@proxy_pending ||= []) << request
|
244
|
-
end
|
245
|
-
|
246
228
|
def connecting?
|
247
229
|
return super unless @options.proxy
|
248
230
|
|
@@ -271,6 +253,12 @@ module HTTPX
|
|
271
253
|
|
272
254
|
private
|
273
255
|
|
256
|
+
def initialize_type(uri, options)
|
257
|
+
return super unless options.proxy
|
258
|
+
|
259
|
+
"tcp"
|
260
|
+
end
|
261
|
+
|
274
262
|
def connect
|
275
263
|
return super unless @options.proxy
|
276
264
|
|
@@ -278,12 +266,6 @@ module HTTPX
|
|
278
266
|
when :idle
|
279
267
|
transition(:connecting)
|
280
268
|
when :connected
|
281
|
-
if @proxy_pending
|
282
|
-
while (req = @proxy_pendind.shift)
|
283
|
-
send(req)
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
269
|
transition(:open)
|
288
270
|
end
|
289
271
|
end
|
@@ -9,7 +9,7 @@ module HTTPX
|
|
9
9
|
# * when the server is unavailable (503);
|
10
10
|
# * when a 3xx request comes with a "retry-after" value
|
11
11
|
#
|
12
|
-
# https://gitlab.com/os85/httpx/wikis/
|
12
|
+
# https://gitlab.com/os85/httpx/wikis/Rate-Limiter
|
13
13
|
#
|
14
14
|
module RateLimiter
|
15
15
|
class << self
|
@@ -132,6 +132,10 @@ module HTTPX
|
|
132
132
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) }
|
133
133
|
end
|
134
134
|
|
135
|
+
def proxy_error?(request, response)
|
136
|
+
super && !request.retries.positive?
|
137
|
+
end
|
138
|
+
|
135
139
|
#
|
136
140
|
# Atttempt to set the request to perform a partial range request.
|
137
141
|
# This happens if the peer server accepts byte-range requests, and
|