httpx 1.1.5 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/doc/release_notes/1_2_0.md +49 -0
  4. data/lib/httpx/adapters/webmock.rb +25 -3
  5. data/lib/httpx/altsvc.rb +57 -2
  6. data/lib/httpx/chainable.rb +40 -29
  7. data/lib/httpx/connection/http1.rb +27 -22
  8. data/lib/httpx/connection/http2.rb +7 -3
  9. data/lib/httpx/connection.rb +45 -60
  10. data/lib/httpx/extensions.rb +0 -15
  11. data/lib/httpx/options.rb +84 -27
  12. data/lib/httpx/plugins/aws_sigv4.rb +2 -2
  13. data/lib/httpx/plugins/basic_auth.rb +1 -1
  14. data/lib/httpx/plugins/callbacks.rb +91 -0
  15. data/lib/httpx/plugins/circuit_breaker.rb +2 -0
  16. data/lib/httpx/plugins/cookies.rb +19 -9
  17. data/lib/httpx/plugins/digest_auth.rb +1 -1
  18. data/lib/httpx/plugins/follow_redirects.rb +11 -0
  19. data/lib/httpx/plugins/grpc.rb +2 -2
  20. data/lib/httpx/plugins/h2c.rb +20 -8
  21. data/lib/httpx/plugins/proxy/socks4.rb +2 -2
  22. data/lib/httpx/plugins/proxy/socks5.rb +2 -2
  23. data/lib/httpx/plugins/proxy.rb +14 -32
  24. data/lib/httpx/plugins/rate_limiter.rb +1 -1
  25. data/lib/httpx/plugins/retries.rb +4 -0
  26. data/lib/httpx/plugins/ssrf_filter.rb +142 -0
  27. data/lib/httpx/plugins/stream.rb +1 -1
  28. data/lib/httpx/plugins/upgrade/h2.rb +1 -1
  29. data/lib/httpx/plugins/upgrade.rb +1 -1
  30. data/lib/httpx/plugins/webdav.rb +1 -1
  31. data/lib/httpx/pool.rb +32 -28
  32. data/lib/httpx/request/body.rb +3 -3
  33. data/lib/httpx/request.rb +3 -5
  34. data/lib/httpx/resolver/https.rb +3 -2
  35. data/lib/httpx/resolver/native.rb +1 -0
  36. data/lib/httpx/resolver/resolver.rb +17 -6
  37. data/lib/httpx/session.rb +13 -82
  38. data/lib/httpx/timers.rb +3 -10
  39. data/lib/httpx/transcoder.rb +1 -1
  40. data/lib/httpx/version.rb +1 -1
  41. data/sig/altsvc.rbs +33 -0
  42. data/sig/chainable.rbs +1 -0
  43. data/sig/connection/http1.rbs +2 -1
  44. data/sig/connection.rbs +16 -16
  45. data/sig/options.rbs +10 -2
  46. data/sig/plugins/callbacks.rbs +38 -0
  47. data/sig/plugins/cookies.rbs +2 -0
  48. data/sig/plugins/follow_redirects.rbs +2 -0
  49. data/sig/plugins/proxy/socks4.rbs +2 -1
  50. data/sig/plugins/proxy/socks5.rbs +2 -1
  51. data/sig/plugins/proxy.rbs +11 -1
  52. data/sig/pool.rbs +1 -3
  53. data/sig/resolver/resolver.rbs +3 -1
  54. data/sig/session.rbs +4 -4
  55. 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
- REQUEST_IVARS = %i[@params @form @xml @json @body].freeze
230
- private_constant :REQUEST_IVARS
231
+ REQUEST_BODY_IVARS = %i[@headers @params @form @xml @json @body].freeze
231
232
 
232
233
  def ==(other)
233
- ivars = instance_variables | other.instance_variables
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
- case ivar
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
- raise ArgumentError, "#{other} is not a valid set of options" unless other.respond_to?(:to_hash)
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
- h2 = other.to_hash
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
- h1 = to_hash
274
+ opts = dup
255
275
 
256
- return self if h1 >= h2
276
+ other_ivars.each do |ivar|
277
+ v = OTHER_LOOKUP[other, ivar, ivar_map]
257
278
 
258
- merged = h1.merge(h2) do |_k, v1, v2|
259
- if v1.respond_to?(:merge) && v2.respond_to?(:merge)
260
- v1.merge(v2)
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
- self.class.new(merged)
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 initialize_dup(other)
276
- instance_variables.each do |ivar|
277
- instance_variable_set(ivar, other.instance_variable_get(ivar).dup)
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/general/latest/gr/signature-version-4.html
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/general/latest/gr/sigv4-create-canonical-request.html
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://tools.ietf.org/html/rfc7617)
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
@@ -25,6 +25,8 @@ module HTTPX
25
25
  end
26
26
 
27
27
  module InstanceMethods
28
+ include HTTPX::Callbacks
29
+
28
30
  def initialize(*)
29
31
  super
30
32
  @circuit_store = CircuitStore.new(@options)
@@ -71,22 +71,32 @@ module HTTPX
71
71
  end
72
72
 
73
73
  module OptionsMethods
74
- def do_initialize(*)
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
- return unless @headers.key?("cookie")
79
+ value
80
+ end
78
81
 
79
- @headers.delete("cookie").each do |ck|
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
- @cookies.add(Cookie.new(name, value))
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://tools.ietf.org/html/rfc7616)
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.
@@ -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
@@ -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://tools.ietf.org/html/rfc7540#section-3.2)
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
- parser_options = @options.merge(max_concurrent_requests: request.options.max_concurrent_requests)
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 < Error; end
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 < Error; end
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- class HTTPProxyError < Error; end
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) || build_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 build_altsvc_connection(_, _, _, _, _, options)
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/RateLimiter
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