httpx 1.8.4 → 1.9.1

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/1_8_1.md +1 -1
  3. data/doc/release_notes/1_8_2.md +4 -4
  4. data/doc/release_notes/1_8_3.md +3 -5
  5. data/doc/release_notes/1_8_4.md +1 -1
  6. data/doc/release_notes/1_9_0.md +75 -0
  7. data/doc/release_notes/1_9_1.md +32 -0
  8. data/lib/httpx/adapters/faraday.rb +1 -1
  9. data/lib/httpx/adapters/webmock.rb +15 -2
  10. data/lib/httpx/altsvc.rb +29 -15
  11. data/lib/httpx/chainable.rb +4 -1
  12. data/lib/httpx/connection/http1.rb +34 -35
  13. data/lib/httpx/connection/http2.rb +146 -33
  14. data/lib/httpx/connection.rb +282 -162
  15. data/lib/httpx/headers.rb +2 -2
  16. data/lib/httpx/io/tcp.rb +42 -21
  17. data/lib/httpx/options.rb +86 -35
  18. data/lib/httpx/parser/http1.rb +3 -6
  19. data/lib/httpx/plugins/auth/digest.rb +1 -2
  20. data/lib/httpx/plugins/auth.rb +11 -2
  21. data/lib/httpx/plugins/aws_sdk_authentication.rb +7 -3
  22. data/lib/httpx/plugins/aws_sigv4.rb +1 -1
  23. data/lib/httpx/plugins/basic_auth.rb +2 -0
  24. data/lib/httpx/plugins/cache.rb +1 -1
  25. data/lib/httpx/plugins/callbacks.rb +8 -6
  26. data/lib/httpx/plugins/circuit_breaker.rb +1 -0
  27. data/lib/httpx/plugins/content_digest.rb +3 -5
  28. data/lib/httpx/plugins/digest_auth.rb +2 -0
  29. data/lib/httpx/plugins/expect.rb +10 -1
  30. data/lib/httpx/plugins/fiber_concurrency.rb +59 -16
  31. data/lib/httpx/plugins/follow_redirects.rb +1 -1
  32. data/lib/httpx/plugins/grpc.rb +21 -0
  33. data/lib/httpx/plugins/h2c.rb +12 -11
  34. data/lib/httpx/plugins/hedging.rb +284 -0
  35. data/lib/httpx/plugins/ntlm_auth.rb +5 -3
  36. data/lib/httpx/plugins/ntlm_v2_auth.rb +5 -3
  37. data/lib/httpx/plugins/oauth.rb +2 -2
  38. data/lib/httpx/plugins/proxy/http.rb +67 -26
  39. data/lib/httpx/plugins/proxy/socks5.rb +4 -4
  40. data/lib/httpx/plugins/proxy/ssh.rb +30 -11
  41. data/lib/httpx/plugins/proxy.rb +68 -54
  42. data/lib/httpx/plugins/push_promise.rb +4 -2
  43. data/lib/httpx/plugins/query.rb +1 -0
  44. data/lib/httpx/plugins/retries.rb +2 -2
  45. data/lib/httpx/plugins/server_sent_events.rb +1 -0
  46. data/lib/httpx/plugins/ssrf_filter.rb +1 -1
  47. data/lib/httpx/plugins/stream.rb +12 -10
  48. data/lib/httpx/plugins/stream_bidi.rb +4 -0
  49. data/lib/httpx/plugins/tracing.rb +99 -11
  50. data/lib/httpx/plugins/upgrade/h2.rb +3 -3
  51. data/lib/httpx/plugins/upgrade.rb +2 -0
  52. data/lib/httpx/plugins/webdav.rb +8 -0
  53. data/lib/httpx/request/body.rb +2 -1
  54. data/lib/httpx/request.rb +6 -6
  55. data/lib/httpx/resolver/https.rb +3 -2
  56. data/lib/httpx/resolver/native.rb +19 -9
  57. data/lib/httpx/resolver/resolver.rb +2 -2
  58. data/lib/httpx/resolver/system.rb +4 -0
  59. data/lib/httpx/resolver.rb +10 -6
  60. data/lib/httpx/response.rb +19 -7
  61. data/lib/httpx/selector.rb +111 -10
  62. data/lib/httpx/session.rb +36 -10
  63. data/lib/httpx/transcoder/json.rb +1 -1
  64. data/lib/httpx/version.rb +1 -1
  65. data/lib/httpx.rb +1 -0
  66. data/sig/altsvc.rbs +3 -3
  67. data/sig/callbacks.rbs +1 -0
  68. data/sig/chainable.rbs +3 -1
  69. data/sig/connection/http1.rbs +4 -2
  70. data/sig/connection/http2.rbs +23 -17
  71. data/sig/connection.rbs +29 -7
  72. data/sig/headers.rbs +1 -1
  73. data/sig/httpx.rbs +2 -0
  74. data/sig/io/tcp.rbs +5 -0
  75. data/sig/options.rbs +43 -12
  76. data/sig/parser/http1.rbs +1 -1
  77. data/sig/plugins/auth/digest.rbs +0 -1
  78. data/sig/plugins/auth.rbs +12 -5
  79. data/sig/plugins/expect.rbs +6 -0
  80. data/sig/plugins/fiber_concurrency.rbs +10 -1
  81. data/sig/plugins/hedging.rbs +44 -0
  82. data/sig/plugins/proxy/http.rbs +9 -2
  83. data/sig/plugins/proxy.rbs +3 -2
  84. data/sig/plugins/retries.rbs +2 -2
  85. data/sig/plugins/stream_bidi.rbs +0 -2
  86. data/sig/plugins/tracing.rbs +22 -5
  87. data/sig/request/body.rbs +1 -1
  88. data/sig/resolver/entry.rbs +2 -2
  89. data/sig/resolver/https.rbs +2 -0
  90. data/sig/resolver/native.rbs +3 -0
  91. data/sig/resolver/resolver.rbs +1 -1
  92. data/sig/resolver/system.rbs +2 -0
  93. data/sig/resolver.rbs +1 -1
  94. data/sig/response.rbs +1 -2
  95. data/sig/selector.rbs +17 -3
  96. data/sig/session.rbs +8 -6
  97. metadata +7 -1
@@ -10,19 +10,57 @@ module HTTPX
10
10
  end
11
11
  end
12
12
 
13
+ # adds support for the following options:
14
+ #
15
+ # :proxy_headers :: hash of HTTP headers to send while connecting to the proxy.
16
+ # :enable_proxy_tunnel :: when <tt>true</tt>, it forces the establishment of the CONNECT proxy
17
+ # tunnel, in cases where it wouldn't otherwise normally establish it
18
+ # (i.e. if the request is an http:// request).
19
+ # :connect_request_class :: class of the request object used for the CONNECT tunnel request.
20
+ module OptionsMethods
21
+ def extend_with_plugin_classes(pl)
22
+ super
23
+
24
+ @connect_request_class = @request_class.dup
25
+ Options::SET_TEMPORARY_NAME[@connect_request_class, pl]
26
+ @connect_request_class.__send__(:include, ConnectRequestMethods)
27
+ end
28
+
29
+ private
30
+
31
+ def option_connect_request_class(val)
32
+ val
33
+ end
34
+
35
+ def option_proxy_headers(value)
36
+ value = value.dup if value.frozen?
37
+
38
+ headers_class.new(value)
39
+ end
40
+
41
+ def option_enable_proxy_tunnel(value) # rubocop:disable Naming/PredicateMethod
42
+ !!value
43
+ end
44
+ end
45
+
13
46
  module InstanceMethods
47
+ # returns a new session with the proxy authentication scheme set to <tt>"basic"</tt>, for HTTP Basic Auth.
14
48
  def with_proxy_basic_auth(opts)
15
49
  with(proxy: opts.merge(scheme: "basic"))
16
50
  end
17
51
 
52
+ # returns a new session with the proxy authentication scheme set to <tt>"digest"</tt>, for HTTP Digest Auth.
18
53
  def with_proxy_digest_auth(opts)
19
54
  with(proxy: opts.merge(scheme: "digest"))
20
55
  end
21
56
 
57
+ # returns a new session with the proxy authentication scheme set to <tt>"ntlm"</tt>, for NTLM Auth.
22
58
  def with_proxy_ntlm_auth(opts)
23
59
  with(proxy: opts.merge(scheme: "ntlm"))
24
60
  end
25
61
 
62
+ private
63
+
26
64
  def fetch_response(request, selector, options)
27
65
  response = super
28
66
 
@@ -51,17 +89,21 @@ module HTTPX
51
89
  super
52
90
  end
53
91
 
54
- def force_close(*)
92
+ def handle_connect_error(*)
55
93
  if @state == :connecting
56
94
  # proxy connect related requests should not be reenqueed
57
95
  @parser.reset
58
- @inflight -= @parser.pending.size
59
- @parser.pending.clear
60
96
  end
61
97
 
62
98
  super
63
99
  end
64
100
 
101
+ def on_error(error, *)
102
+ error = error.to_connection_error if error.is_a?(RequestTimeoutError) && connecting?
103
+
104
+ super
105
+ end
106
+
65
107
  private
66
108
 
67
109
  def purge_after_closed
@@ -83,12 +125,12 @@ module HTTPX
83
125
  @tunnel_parser || begin
84
126
  if @parser
85
127
  # in case this happens, requests from @parser need to be reenqueued for delivery.
86
- enqueue_pending_requests_from_parser(@parser)
128
+ @parser.reset_requests
87
129
  @parser = nil
88
130
  end
89
131
 
90
132
  @tunnel_parser = @parser = parser =
91
- parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
133
+ parser_type(@io.protocol).new(@write_buffer, @pending, @options.merge(max_concurrent_requests: 1))
92
134
  parser.extend(ProxyParser)
93
135
  parser.on(:response, &method(:__http_on_connect))
94
136
  parser.on(:close) do
@@ -96,11 +138,12 @@ module HTTPX
96
138
 
97
139
  reset
98
140
  end
141
+ parser.on(:error, &method(:on_parser_error))
99
142
  parser.on(:reset) do
100
- if parser.pending.empty? && parser.empty?
143
+ if parser.empty?
101
144
  reset
102
145
  else
103
- enqueue_pending_requests_from_parser(parser)
146
+ parser.reset_requests
104
147
 
105
148
  initial_state = @state
106
149
 
@@ -121,7 +164,7 @@ module HTTPX
121
164
  if initial_state == :connecting
122
165
  parser = tunnel_parser
123
166
  parser.reset
124
- if @pending.first.is_a?(ConnectRequest)
167
+ if @pending.first.is_a?(@options.connect_request_class)
125
168
  connect_request = @pending.shift # this happened when reenqueing
126
169
  end
127
170
  end
@@ -129,14 +172,11 @@ module HTTPX
129
172
  idling
130
173
 
131
174
  @tunnel_parser = @parser = parser
132
- if connect_request
133
- @inflight += 1
134
- parser.send(connect_request)
135
- end
175
+ parser.send(connect_request) if connect_request
136
176
  transition(:connecting)
137
177
  end
138
178
  end
139
- __http_proxy_connect(parser)
179
+ __http_proxy_connect
140
180
  end
141
181
  return if @state == :connected
142
182
  when :connected
@@ -155,27 +195,29 @@ module HTTPX
155
195
  super
156
196
  end
157
197
 
158
- def __http_proxy_connect(parser)
198
+ def __http_proxy_connect
159
199
  req = @pending.first
160
- if req && req.uri.scheme == "https"
200
+ enable_proxy_tunnel = (
201
+ req.options.enable_proxy_tunnel.nil? && req && req.uri.scheme == "https"
202
+ ) || req.options.enable_proxy_tunnel
203
+
204
+ if enable_proxy_tunnel
161
205
  # if the first request after CONNECT is to an https address, it is assumed that
162
206
  # all requests in the queue are not only ALL HTTPS, but they also share the certificate,
163
207
  # and therefore, will share the connection.
164
208
  #
165
- connect_request = ConnectRequest.new(req.uri, @options)
166
- @inflight += 1
167
- parser.send(connect_request)
209
+ connect_request = @options.connect_request_class.new(req.uri, @options)
210
+ send(connect_request)
168
211
  else
169
212
  handle_transition(:connected)
170
213
  end
171
214
  end
172
215
 
173
216
  def __http_on_connect(request, response)
174
- @inflight -= 1
175
217
  if response.is_a?(Response) && response.status == 200
176
218
  req = @pending.first
177
219
  request_uri = req.uri
178
- @io = ProxySSL.new(@io, request_uri, @options)
220
+ @io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
179
221
  transition(:connected)
180
222
  throw(:called)
181
223
  elsif response.is_a?(Response) &&
@@ -185,11 +227,9 @@ module HTTPX
185
227
 
186
228
  request.transition(:idle)
187
229
  request.headers["proxy-authorization"] = @options.proxy.authenticate(request, response.headers["proxy-authenticate"])
188
- @parser.send(request)
189
- @inflight += 1
230
+ send(request)
190
231
  else
191
- pending = @pending + @parser.pending
192
- while (req = pending.shift)
232
+ while (req = @pending.shift)
193
233
  response.finish!
194
234
  req.response = response
195
235
  req.emit_response(response)
@@ -219,10 +259,11 @@ module HTTPX
219
259
  end
220
260
  end
221
261
 
222
- class ConnectRequest < Request
262
+ module ConnectRequestMethods
223
263
  def initialize(uri, options)
224
264
  super("CONNECT", uri, options)
225
- @headers.delete("accept")
265
+ @headers = options.headers_class.new
266
+ merge_headers(options.proxy_headers) if options.proxy_headers
226
267
  end
227
268
 
228
269
  def path
@@ -42,10 +42,6 @@ module HTTPX
42
42
  end
43
43
  end
44
44
 
45
- def connecting?
46
- super || @state == :authenticating || @state == :negotiating
47
- end
48
-
49
45
  def interests
50
46
  if @state == :connecting || @state == :authenticating || @state == :negotiating
51
47
  return @write_buffer.empty? ? :r : :w
@@ -56,6 +52,10 @@ module HTTPX
56
52
 
57
53
  private
58
54
 
55
+ def proxy_connecting?
56
+ super || @state == :authenticating || @state == :negotiating
57
+ end
58
+
59
59
  def handle_transition(nextstate)
60
60
  return super unless @options.proxy && @options.proxy.uri.scheme == "socks5"
61
61
 
@@ -21,31 +21,50 @@ module HTTPX
21
21
  end
22
22
 
23
23
  module InstanceMethods
24
+ def initialize(*)
25
+ super
26
+ @_gateway = nil
27
+ end
28
+
24
29
  def request(*args, **options)
25
30
  raise ArgumentError, "must perform at least one request" if args.empty?
26
31
 
27
32
  requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
28
33
 
29
- request = requests.first or return super
34
+ request = requests.first
35
+
36
+ return super unless request
30
37
 
31
38
  request_options = request.options
32
39
 
33
40
  return super unless request_options.proxy
34
41
 
35
- ssh_options = request_options.proxy
36
- ssh_uris = ssh_options.delete(:uri)
37
- ssh_uri = URI.parse(ssh_uris.shift)
42
+ ssh_options = request_options.proxy.dup
43
+ ssh_uris = Array(ssh_options.delete(:uri))
44
+
45
+ proxy_ns = 0
46
+
47
+ begin
48
+ ssh_uri = URI(ssh_uris[proxy_ns])
49
+
50
+ return super unless ssh_uri.scheme == "ssh"
51
+
52
+ ssh_username = ssh_options.delete(:username)
53
+ ssh_options[:port] ||= ssh_uri.port || 22
54
+ if request_options.debug
55
+ ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
56
+ end
38
57
 
39
- return super unless ssh_uri.scheme == "ssh"
58
+ request_uri = URI(requests.first.uri)
59
+ @_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
60
+ rescue StandardError => e
61
+ log { "failed connecting to ssh proxy, trying next..." }
62
+ proxy_ns += 1
63
+ raise e if proxy_ns >= ssh_uris.size
40
64
 
41
- ssh_username = ssh_options.delete(:username)
42
- ssh_options[:port] ||= ssh_uri.port || 22
43
- if request_options.debug
44
- ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
65
+ retry
45
66
  end
46
67
 
47
- request_uri = URI(requests.first.uri)
48
- @_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
49
68
  begin
50
69
  @_gateway.open(request_uri.host, request_uri.port) do |local_port|
51
70
  io = build_gateway_socket(local_port, request_uri, request_options)
@@ -11,7 +11,7 @@ module HTTPX
11
11
  #
12
12
  # * HTTP proxies
13
13
  # * HTTPS proxies
14
- # * Socks4/4a proxies
14
+ # * Socks4 proxies (also supports Socks4a)
15
15
  # * Socks5 proxies
16
16
  #
17
17
  # https://gitlab.com/os85/httpx/wikis/Proxy
@@ -19,6 +19,7 @@ module HTTPX
19
19
  module Proxy
20
20
  class ProxyConnectionError < ProxyError; end
21
21
 
22
+ # :nodoc:
22
23
  PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
23
24
 
24
25
  class << self
@@ -39,21 +40,24 @@ module HTTPX
39
40
  end
40
41
  end
41
42
 
43
+ # Manages the proxy parameters and the proxy authentication flow.
42
44
  class Parameters
43
45
  attr_reader :uri, :username, :password, :scheme, :no_proxy
44
46
 
45
- def initialize(uri: nil, scheme: nil, username: nil, password: nil, no_proxy: nil, **extra)
47
+ def initialize(uri: nil, ns: 0, scheme: nil, username: nil, password: nil, no_proxy: nil, **extra)
46
48
  @no_proxy = Array(no_proxy) if no_proxy
47
49
  @uris = Array(uri)
48
- uri = @uris.first
50
+ uri = @uris[ns]
49
51
 
50
52
  @username = username
51
53
  @password = password
52
54
 
53
- @ns = 0
55
+ @initial_credentials = @username && @password
56
+
57
+ @ns = ns
54
58
 
55
59
  if uri
56
- @uri = uri.is_a?(URI::Generic) ? uri : URI(uri)
60
+ @uri = URI(uri)
57
61
  @username ||= @uri.user
58
62
  @password ||= @uri.password
59
63
  end
@@ -74,22 +78,15 @@ module HTTPX
74
78
  end
75
79
 
76
80
  def shift
77
- # TODO: this operation must be synchronized
78
- @ns += 1
79
- @uri = @uris[@ns]
80
-
81
- return unless @uri
82
-
83
- @uri = URI(@uri) unless @uri.is_a?(URI::Generic)
84
-
85
- scheme = infer_default_auth_scheme(@uri)
86
-
87
- return unless scheme != @scheme
88
-
89
- @scheme = scheme
90
- @username = username || @uri.user
91
- @password = password || @uri.password
92
- @authenticator = load_authenticator(scheme, @username, @password)
81
+ return if @ns >= @uris.size - 1
82
+
83
+ Parameters.new(
84
+ uri: @uris,
85
+ ns: @ns + 1,
86
+ username: (@username if @initial_credentials),
87
+ password: (@password if @initial_credentials),
88
+ no_proxy: @no_proxy
89
+ )
93
90
  end
94
91
 
95
92
  def can_authenticate?(...)
@@ -144,8 +141,26 @@ module HTTPX
144
141
 
145
142
  # adds support for the following options:
146
143
  #
147
- # :proxy :: proxy options defining *:uri*, *:username*, *:password* or
148
- # *:scheme* (i.e. <tt>{ uri: "http://proxy" }</tt>)
144
+ # :proxy :: hash of proxy options, where a URI an be passed containing most of the requred information,
145
+ # or instead the different proxy options can be passed independently.
146
+ #
147
+ # The following proxy options are supported:
148
+ #
149
+ # :uri :: proxy origin URI, or list of URIs; in the case of the latter, any failed attempt to connect to a list item
150
+ # will retry for the next item of the list, until all elements are exhausted or it times out meanwhile.
151
+ # <tt>:username</tt> and <tt>:password</tt> will be inferred from it when available. The URI scheme will identify
152
+ # the proxy type. (ex: <tt>"http://proxi-uri:8980"</tt>, <tt>"http://user:pass@proxy-uri:8181"</tt>,
153
+ # <tt>"socks5://user:pass@socks-proxy-uri:10300"</tt>, <tt>["http://proxi-uri:8980", "http://user:pass@proxy-uri:8181"]</tt>).
154
+ # :scheme :: identifies the authentication scheme to use with the proxy, if necessary; defaults to <tt>"basic"</tt>, for basic auth,
155
+ # can also be <tt>"digest"</tt> or <tt>"ntml"</tt>). Your recommended to use session methods like HTTP::InstanceMethods#with_proxy_basic_auth
156
+ # to set these instead.
157
+ # :username :: the username to use when authenticating with the proxy
158
+ # :password :: the password to use when authenticating with the proxy
159
+ # :no_proxy :: A list of hostnames (or regex patterns) for which the proxy should be bypassed
160
+ # (ex: <tt>["example.com", "localhost", /.*\.google\.com/]</tt>). Can also receive ["*"] as a catch-all.
161
+ #
162
+ # if you systems setting environment variables like `HTTP_PROXY` or `NO_PROXY` though, you won't need to set
163
+ # most of the above.
149
164
  module OptionsMethods
150
165
  private
151
166
 
@@ -161,25 +176,31 @@ module HTTPX
161
176
  end
162
177
 
163
178
  module InstanceMethods
164
- def find_connection(request_uri, selector, options)
165
- return super unless options.respond_to?(:proxy)
179
+ def initialize(*)
180
+ super
181
+ @proxy_parameters = @options.proxy
182
+ end
183
+
184
+ def initialize_dup(other)
185
+ @proxy_parameters = other.instance_variable_get(:@proxy_parameters)
186
+ super
187
+ end
166
188
 
189
+ def find_connection(request_uri, selector, options)
167
190
  if (next_proxy = request_uri.find_proxy)
168
191
  return super(request_uri, selector, options.merge(proxy: Parameters.new(uri: next_proxy)))
169
192
  end
170
193
 
171
- proxy = options.proxy
172
-
173
- return super unless proxy
194
+ return super unless @proxy_parameters
174
195
 
175
- next_proxy = proxy.uri
196
+ next_proxy = @proxy_parameters.uri
176
197
 
177
198
  raise ProxyError, "Failed to connect to proxy" unless next_proxy
178
199
 
179
200
  raise ProxyError,
180
201
  "#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
181
202
 
182
- if (no_proxy = proxy.no_proxy)
203
+ if (no_proxy = @proxy_parameters.no_proxy)
183
204
  no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
184
205
 
185
206
  unless no_proxy != "*" && # NO_PROXY=* bypasses proxy use
@@ -189,7 +210,7 @@ module HTTPX
189
210
  end
190
211
  end
191
212
 
192
- super(request_uri, selector, options.merge(proxy: proxy))
213
+ super(request_uri, selector, options.merge(proxy: @proxy_parameters))
193
214
  end
194
215
 
195
216
  private
@@ -201,10 +222,10 @@ module HTTPX
201
222
  response = super
202
223
 
203
224
  if response.is_a?(ErrorResponse) && proxy_error?(request, response, options)
204
- options.proxy.shift
225
+ @proxy_parameters = options.proxy.shift
205
226
 
206
227
  # return last error response if no more proxies to try
207
- return response if options.proxy.uri.nil?
228
+ return response unless @proxy_parameters
208
229
 
209
230
  log { "failed connecting to proxy, trying next..." }
210
231
  request.transition(:idle)
@@ -222,22 +243,22 @@ module HTTPX
222
243
  end
223
244
  end
224
245
 
225
- def proxy_error?(_request, response, options)
226
- return false unless options.proxy
246
+ def proxy_error?(_request, response, _options)
247
+ proxy_parameters = @proxy_parameters
248
+
249
+ return false unless proxy_parameters
227
250
 
228
251
  error = response.error
229
252
  case error
230
253
  when NativeResolveError
231
- proxy_uri = URI(options.proxy.uri)
254
+ proxy_uri = proxy_parameters.uri
232
255
 
233
256
  unresolved_host = error.host
234
257
 
235
258
  # failed resolving proxy domain
236
259
  unresolved_host == proxy_uri.host
237
260
  when ResolveError
238
- proxy_uri = URI(options.proxy.uri)
239
-
240
- error.message.end_with?(proxy_uri.to_s)
261
+ error.message.end_with?(proxy_parameters.uri.to_s)
241
262
  when ProxyConnectionError
242
263
  # timeout errors connecting to proxy
243
264
  true
@@ -256,7 +277,7 @@ module HTTPX
256
277
 
257
278
  # redefining the connection origin as the proxy's URI,
258
279
  # as this will be used as the tcp peer ip.
259
- @proxy_uri = URI(@options.proxy.uri)
280
+ @proxy_uri = @options.proxy.uri
260
281
  end
261
282
 
262
283
  def peer
@@ -266,7 +287,7 @@ module HTTPX
266
287
  def connecting?
267
288
  return super unless @options.proxy
268
289
 
269
- super || @state == :connecting || @state == :connected
290
+ super || proxy_connecting?
270
291
  end
271
292
 
272
293
  def call
@@ -288,15 +309,6 @@ module HTTPX
288
309
  raise e
289
310
  end
290
311
 
291
- def reset
292
- return super unless @options.proxy
293
-
294
- @state = :open
295
-
296
- super
297
- # emit(:close)
298
- end
299
-
300
312
  private
301
313
 
302
314
  def initialize_type(uri, options)
@@ -305,6 +317,10 @@ module HTTPX
305
317
  "tcp"
306
318
  end
307
319
 
320
+ def proxy_connecting?
321
+ @state == :connecting || @state == :connected
322
+ end
323
+
308
324
  def connect
309
325
  return super unless @options.proxy
310
326
 
@@ -312,7 +328,7 @@ module HTTPX
312
328
  when :idle
313
329
  transition(:connecting)
314
330
  when :connected
315
- transition(:open)
331
+ super
316
332
  end
317
333
  end
318
334
 
@@ -321,9 +337,7 @@ module HTTPX
321
337
 
322
338
  case nextstate
323
339
  when :closing
324
- # this is a hack so that we can use the super method
325
- # and it'll think that the current state is open
326
- @state = :open if @state == :connecting
340
+ @parser = nil if proxy_connecting?
327
341
  end
328
342
  super
329
343
  end
@@ -12,8 +12,10 @@ module HTTPX
12
12
  #
13
13
  module PushPromise
14
14
  def self.extra_options(options)
15
- options.merge(http2_settings: { settings_enable_push: 1 },
16
- max_concurrent_requests: 1)
15
+ options.merge(
16
+ http2_settings: { settings_enable_push: 1 },
17
+ max_concurrent_requests: 1
18
+ )
17
19
  end
18
20
 
19
21
  module ResponseMethods
@@ -14,6 +14,7 @@ module HTTPX
14
14
  end
15
15
 
16
16
  module InstanceMethods
17
+ # sends a QUERY request for the given +uri+, using +options+ for each.
17
18
  def query(*uri, **options)
18
19
  request("QUERY", uri, **options)
19
20
  end
@@ -165,7 +165,7 @@ module HTTPX
165
165
  # retry-after must be calculated before prepare_to_retry, as it relies on
166
166
  # state changed byit.
167
167
  retry_after = when_to_retry(request, response, options)
168
- prepare_to_retry(request, response)
168
+ prepare_to_retry(request, response, retry_after)
169
169
 
170
170
  if retry_after&.positive?
171
171
  retry_start = Utils.now
@@ -207,7 +207,7 @@ module HTTPX
207
207
  RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) } && !ex.is_a?(TotalRequestTimeoutError)
208
208
  end
209
209
 
210
- def prepare_to_retry(request, response)
210
+ def prepare_to_retry(request, response, _retry_after)
211
211
  request.retries -= 1 unless can_reconnect?(request, response)
212
212
  request.transition(:idle)
213
213
  end
@@ -40,6 +40,7 @@ module HTTPX
40
40
  end
41
41
 
42
42
  module InstanceMethods
43
+ # sends a stream request.
43
44
  def request(*, **options)
44
45
  options[:stream] = true if options[:event_stream]
45
46
 
@@ -142,7 +142,7 @@ module HTTPX
142
142
  end
143
143
 
144
144
  def addresses=(addrs)
145
- addrs.reject! do |ipaddr|
145
+ addrs = addrs.reject do |ipaddr|
146
146
  ipaddr = ipaddr.address
147
147
  next false if @options.safe_private_ranges&.any? { |r| r.include?(ipaddr) }
148
148
 
@@ -125,12 +125,12 @@ module HTTPX
125
125
  # https://gitlab.com/os85/httpx/wikis/Stream
126
126
  #
127
127
  module Stream
128
- STREAM_REQUEST_OPTIONS = { timeout: { read_timeout: Float::INFINITY, operation_timeout: 60 }.freeze }.freeze
128
+ STREAM_REQUEST_OPTIONS = { timeout: { operation_timeout: 60 }.freeze }.freeze
129
129
 
130
130
  def self.extra_options(options)
131
131
  options.merge(
132
132
  stream: false,
133
- timeout: { read_timeout: Float::INFINITY, operation_timeout: 60 },
133
+ timeout: { operation_timeout: 60 },
134
134
  stream_response_class: Class.new(StreamResponse, &Options::SET_TEMPORARY_NAME).freeze
135
135
  )
136
136
  end
@@ -140,14 +140,6 @@ module HTTPX
140
140
  # :stream :: whether the request to process should be handled as a stream (defaults to <tt>false</tt>).
141
141
  # :stream_response_class :: Class used to build the stream response object.
142
142
  module OptionsMethods
143
- def option_stream(val)
144
- val
145
- end
146
-
147
- def option_stream_response_class(value)
148
- value
149
- end
150
-
151
143
  def extend_with_plugin_classes(pl)
152
144
  return super unless defined?(pl::StreamResponseMethods)
153
145
 
@@ -157,6 +149,16 @@ module HTTPX
157
149
 
158
150
  super
159
151
  end
152
+
153
+ private
154
+
155
+ def option_stream(val)
156
+ val
157
+ end
158
+
159
+ def option_stream_response_class(value)
160
+ value
161
+ end
160
162
  end
161
163
 
162
164
  module InstanceMethods
@@ -158,6 +158,10 @@ module HTTPX
158
158
  @closed ? :closed : :open
159
159
  end
160
160
 
161
+ def closed_for_select?
162
+ @closed
163
+ end
164
+
161
165
  # noop
162
166
  def log(**, &_); end
163
167