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.
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