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
@@ -76,10 +76,10 @@ module HTTPX
76
76
  super
77
77
  end
78
78
 
79
- def interests
80
- return if connecting? && @pending.none?(&:current_context?)
81
-
82
- super
79
+ def closed_for_select?
80
+ # @fiber-switch-guard
81
+ # a closed connection still holding requests will reconnect once the fiber closing its socket resumes.
82
+ super && @pending.empty?
83
83
  end
84
84
 
85
85
  def on_io_error(e)
@@ -88,17 +88,12 @@ module HTTPX
88
88
  # @fiber-switch-guard
89
89
  # sockets closed during fiber scheduler switches are raised in separate fibers than the fiber the
90
90
  # socket may be used in. this check verifies that this is actually about this socket.
91
- return unless to_io.closed?
92
-
93
- if @state == :closing
94
- # @fiber-switch-guard
95
- # if the connection is reused across fibers, the socket may have been closed in the other fiber
96
- # and switched here during the process, so continue what it was doing and transition to closed
97
- # via #call.
91
+ unless to_io.closed? && !backlog?
98
92
  call
99
- elsif !backlog?
100
- super
93
+ return
101
94
  end
95
+
96
+ super
102
97
  end
103
98
 
104
99
  def on_connect_error(e)
@@ -107,18 +102,50 @@ module HTTPX
107
102
  # @fiber-switch-guard
108
103
  # sockets closed during fiber scheduler switches are raised in separate fibers than the fiber the
109
104
  # socket may be used in. this check verifies that this is actually about this socket.
110
- return unless to_io.closed? && !backlog?
105
+ unless to_io.closed? && !backlog?
106
+ call
107
+ return
108
+ end
109
+
110
+ super
111
+ end
112
+
113
+ def pending_count
114
+ @pending.count(&:current_context?)
115
+ end
116
+
117
+ def interests
118
+ idling if (@state == :closed) && !no_more_requests?
111
119
 
112
120
  super
113
121
  end
114
122
 
115
123
  private
116
124
 
125
+ def handle_transition(nextstate)
126
+ case nextstate
127
+ when :inactive
128
+ # @fiber-switch-guard
129
+ # connections may have requests (pending or inflight) from other fibers.
130
+ return if @state == :open && backlog?
131
+ end
132
+ super
133
+ end
134
+
135
+ def no_requests_to_handle?
136
+ pending_count.zero? && @parser&.no_requests_to_handle?
137
+ end
138
+
139
+ # @fiber-switch-guard
140
+ # pending requests from other fibers are still being waited for.
141
+ def clear_pending_requests
142
+ @pending.reject!(&:current_context?)
143
+ end
144
+
117
145
  # checks whether the connection has any pending request (which the connection itself may
118
146
  # have stored, or it may be somewhere in the parser).
119
147
  def backlog?
120
- @pending.any? ||
121
- (@parser && (@parser.pending.any? || @parser.requests.any?))
148
+ @pending.any? || @parser&.backlog? || @parser&.waiting_for_ping?
122
149
  end
123
150
  end
124
151
 
@@ -132,6 +159,14 @@ module HTTPX
132
159
 
133
160
  super
134
161
  end
162
+
163
+ def backlog?
164
+ !no_more_requests?
165
+ end
166
+
167
+ def no_requests_to_handle?
168
+ @requests.none?(&:current_context?)
169
+ end
135
170
  end
136
171
 
137
172
  module HTTP2Methods
@@ -156,6 +191,14 @@ module HTTPX
156
191
  super
157
192
  end
158
193
 
194
+ def backlog?
195
+ !no_more_requests?
196
+ end
197
+
198
+ def no_requests_to_handle?
199
+ @streams.each_key.none?(&:current_context?)
200
+ end
201
+
159
202
  private
160
203
 
161
204
  def on_close(_, error, _)
@@ -61,7 +61,7 @@ module HTTPX
61
61
  end
62
62
 
63
63
  module InstanceMethods
64
- # returns a session with the *max_redirects* option set to +n+
64
+ # returns a session with the <tt>:max_redirects</tt> option set to +n+.
65
65
  def max_redirects(n)
66
66
  with(max_redirects: n.to_i)
67
67
  end
@@ -69,6 +69,17 @@ module HTTPX
69
69
  end
70
70
  end
71
71
 
72
+ # adds support for the following options:
73
+ #
74
+ # :grpc_service :: name of the GRPC service service.
75
+ # :grpc_compression :: whether payloads should be GZIP-compressed (<tt>false</tt> by default).
76
+ # :grpc_rpcs :: maps local rpc names to the service RCP information.
77
+ # :grpc_deadline :: sets the service call deadline that the peer should stick to.
78
+ # :call_credentials :: callable which should yield a key-value hash which will be passed in the
79
+ # request HTTP headers.
80
+ #
81
+ # You're recommended not to set any of these options directly, and instead use methods such as
82
+ # InstanceMethods#rpc and InstanceMethods#build_stub.
72
83
  module OptionsMethods
73
84
  private
74
85
 
@@ -125,6 +136,9 @@ module HTTPX
125
136
  end
126
137
 
127
138
  module InstanceMethods
139
+ # returns a new session which sets the TLS channel credentials (+ca_path+ as the CA file,
140
+ # +key+ and +cert+ as the TLS public key and certificate, as well as other TLS options via
141
+ # +ssl_opts+, which should be compatible with what OpenSSL::SSL::SSLContext#set_params receives).
128
142
  def with_channel_credentials(ca_path, key = nil, cert = nil, **ssl_opts)
129
143
  # @type var ssl_params: ::Hash[::Symbol, untyped]
130
144
  ssl_params = {
@@ -144,6 +158,8 @@ module HTTPX
144
158
  with(ssl: ssl_params)
145
159
  end
146
160
 
161
+ # returns a new session with a service RPC identified by +rpc_name+, passing +input+ and +output+ as its
162
+ # input encoder and output decoder respectively; extra options (like <tt>:deadline</tt>) can be passed using +opts+.
147
163
  def rpc(rpc_name, input, output, **opts)
148
164
  rpc_name = rpc_name.to_s
149
165
  raise Error, "rpc #{rpc_name} already defined" if @options.grpc_rpcs.key?(rpc_name)
@@ -179,6 +195,9 @@ module HTTPX
179
195
  ))
180
196
  end
181
197
 
198
+ # returns a new GRPC Service enabled session pointing to a +origin+ URI; when the
199
+ # +service+ is a GRPC Service object (as the ones provided by the `grpc` gem), it'll
200
+ # automatically generate the RPC methods.
182
201
  def build_stub(origin, service: nil, compression: false)
183
202
  scheme = @options.ssl.empty? ? "http" : "https"
184
203
 
@@ -212,6 +231,8 @@ module HTTPX
212
231
  session.with(origin: origin, grpc_service: service, grpc_compression: compression)
213
232
  end
214
233
 
234
+ # executes an RPC service identified by +rpc_method+. You're recommended to use the generated
235
+ # RPC methods directly though, in case you're using #build_stub.
215
236
  def execute(rpc_method, input,
216
237
  deadline: DEADLINE,
217
238
  metadata: nil,
@@ -68,29 +68,30 @@ module HTTPX
68
68
  end
69
69
 
70
70
  def upgrade_to_h2c(request, response)
71
- enqueue_pending_requests_from_parser(@parser)
71
+ @parser.reset_requests
72
72
 
73
- @parser = request.options.h2c_class.new(@write_buffer, @options)
73
+ @parser = request.options.h2c_class.new(@write_buffer, @pending, @options)
74
74
  set_parser_callbacks(@parser)
75
- @inflight += 1 # request is being completed below
76
75
  @parser.upgrade(request, response)
77
76
  @upgrade_protocol = "h2c"
78
77
  end
79
78
 
80
79
  private
81
80
 
82
- def send_request_to_parser(request)
83
- super
81
+ def send_request_to_parser(request, *)
82
+ super.tap do |is_inflight|
83
+ next unless is_inflight
84
84
 
85
- return unless request.headers["upgrade"] == "h2c" && parser.is_a?(Connection::HTTP1)
85
+ next unless request.headers["upgrade"] == "h2c" && parser.is_a?(Connection::HTTP1)
86
86
 
87
- max_concurrent_requests = parser.max_concurrent_requests
87
+ max_concurrent_requests = parser.max_concurrent_requests
88
88
 
89
- return if max_concurrent_requests == 1
89
+ next if max_concurrent_requests == 1
90
90
 
91
- parser.max_concurrent_requests = 1
92
- request.once(:response) do
93
- parser.max_concurrent_requests = max_concurrent_requests
91
+ parser.max_concurrent_requests = 1
92
+ request.once(:response) do
93
+ parser.max_concurrent_requests = max_concurrent_requests
94
+ end
94
95
  end
95
96
  end
96
97
  end
@@ -0,0 +1,284 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds support for request hedging, i.e. a latency-mitigation strategy that involves
7
+ # sending requests through multiple connections and returning the fastest response.
8
+ #
9
+ # https://gitlab.com/os85/httpx/wikis/Hedging#hedging
10
+ #
11
+ module Hedging
12
+ # :hedge_after:: seconds after which, if the request didn't complete yet, it'll be sent on a different connection
13
+ module OptionsMethods
14
+ private
15
+
16
+ def option_hedge_after(value)
17
+ if value.respond_to?(:call)
18
+ value1 = value
19
+ value1 = value1.method(:call) unless value1.respond_to?(:arity)
20
+
21
+ # allow ->(*) arity as well, which is < 0
22
+ raise TypeError, "`:hedge_after` proc has invalid number of parameters" unless value1.arity.negative? || value1.arity.between?(
23
+ 1, 2
24
+ )
25
+
26
+ else
27
+ value = Float(value)
28
+ raise TypeError, "`:hedge_after` must be positive" unless value.positive?
29
+ end
30
+
31
+ value
32
+ end
33
+ end
34
+
35
+ module InstanceMethods
36
+ def select_connection(connection, selector)
37
+ connection.try_yield_hedge_connection do |hedge_conn|
38
+ super.tap do
39
+ select_connection(hedge_conn, selector) if hedge_conn
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def do_request(request, connection, selector)
47
+ super.tap do
48
+ # forego hedging if the connection is already open, or if
49
+ # the connection is scheduled for DNS resolution, as pending requests
50
+ # will be hedged afterwards.
51
+ next if connection.open? || !connection.addresses?
52
+
53
+ hedge_request!(request, connection, selector)
54
+ end
55
+ end
56
+
57
+ # creates the hedge connection before a connection initiates the connection handshake.
58
+ # This excludes connections which are already open.
59
+ def on_resolver_connection(connection, selector)
60
+ super.tap do
61
+ next if connection.open? || connection.hedge_connection
62
+
63
+ connection.pending.each do |request|
64
+ hedge_request!(request, connection, selector)
65
+ end
66
+
67
+ on_resolver_connection(connection.hedge_connection, selector) if connection.hedge_connection
68
+ end
69
+ end
70
+
71
+ def hedge_connection(connection)
72
+ connection.hedge_connection || begin
73
+ new_connection = connection.dup
74
+ new_connection.log { "cloning hedge connection of connection##{connection.object_id}..." }
75
+ connection.hedge_connection = new_connection
76
+ new_connection
77
+ end
78
+ end
79
+
80
+ def hedge_request!(request, connection, selector)
81
+ hedge_after = when_to_hedge(request)
82
+
83
+ if hedge_after&.positive?
84
+ # hedge after interval
85
+ cb = selector.after(hedge_after) do
86
+ next if request.response
87
+
88
+ request.hedge_request = hedge_request = request.dup
89
+ hedge_conn = hedge_connection(connection)
90
+ hedge_conn.send(hedge_request)
91
+ select_connection(hedge_conn, selector)
92
+ end
93
+
94
+ # forego hedging if request is over
95
+ request.once(:response) do
96
+ cb.cancel
97
+ end
98
+ else
99
+ request.hedge_request = hedge_request = request.dup
100
+ hedge_connection(connection).send(hedge_request)
101
+ end
102
+ end
103
+
104
+ def when_to_hedge(request)
105
+ hedge_after = request.options.hedge_after
106
+
107
+ return unless hedge_after
108
+
109
+ hedge_after = hedge_after.call(request) if hedge_after.respond_to?(:call)
110
+
111
+ hedge_after
112
+ end
113
+
114
+ # hedge connections should not deactivate, instead they should terminate.
115
+ # this ensures that persistent hedge connections aren't treated as persistent,
116
+ # and close instead of deactivating.
117
+ def deactivate(selector, *)
118
+ hedge_connections = selector.each_connection.select(&:is_hedge_connection)
119
+
120
+ return super unless hedge_connections.any?
121
+
122
+ selector_close(selector, hedge_connections)
123
+
124
+ super
125
+ end
126
+
127
+ # do not check hedge connections back in
128
+ def can_checkin?(connection)
129
+ super && !connection.is_hedge_connection
130
+ end
131
+ end
132
+
133
+ module RequestMethods
134
+ # TODO: would be nice to get rid of this
135
+ attr_reader :connection
136
+
137
+ attr_reader :hedge_request
138
+
139
+ def initialize(*)
140
+ super
141
+ @is_hedge_request = false
142
+ end
143
+
144
+ def hedge_request=(request)
145
+ @hedge_request = request
146
+
147
+ return unless request
148
+
149
+ @is_hedge_request = !request.hedge_request.nil?
150
+
151
+ return if @is_hedge_request
152
+
153
+ request.hedge_request = self
154
+ end
155
+
156
+ def response=(response)
157
+ return super unless @hedge_request # rubocop:disable Lint/ReturnInVoidContext
158
+
159
+ prev_response = @response
160
+
161
+ begin
162
+ super
163
+ ensure
164
+ @response = prev_response if prev_response
165
+ end
166
+ end
167
+
168
+ def emit_response(response)
169
+ hedge_request = @hedge_request
170
+
171
+ return super unless hedge_request
172
+
173
+ return super unless hedge_request &&
174
+ (
175
+ hedge_request.response.nil? ||
176
+ !hedge_request.response.finished?
177
+ )
178
+
179
+ # if this response is for a hedge request, and the main request
180
+ # didn't complete yet, then we replace it.
181
+
182
+ super.tap do
183
+ hedge_request.response = response
184
+
185
+ # should cancel any pending HTTP/2 stream; there's no way to
186
+ # cancel an HTTP/1 request, so that one will run to completion.
187
+ hedge_request.emit(:refuse)
188
+ end
189
+ ensure
190
+ self.hedge_request = nil
191
+ end
192
+ end
193
+
194
+ module ResponseMethods
195
+ def finish!
196
+ connection = @request.connection
197
+
198
+ super
199
+
200
+ return unless connection && (hedge_connection = connection.hedge_connection)
201
+
202
+ # disconnect connection with its hedge, so that objects do not leak after
203
+ # they're not needed.
204
+ connection.hedge_connection = hedge_connection.hedge_connection = nil
205
+
206
+ # remarking itself as the main connection, so that it's not discarded when
207
+ # going back to the pool.
208
+ connection.is_hedge_connection = false
209
+ hedge_connection.is_hedge_connection = true
210
+ end
211
+ end
212
+
213
+ module ConnectionMethods
214
+ attr_reader :hedge_connection
215
+
216
+ attr_accessor :is_hedge_connection
217
+
218
+ def initialize(*)
219
+ super
220
+ @is_hedge_connection = false
221
+ end
222
+
223
+ def addresses=(addrs)
224
+ try_yield_hedge_connection do |hedge_conn|
225
+ super.tap do
226
+ hedge_conn.addresses = addrs if hedge_conn
227
+ end
228
+ end
229
+ end
230
+
231
+ def hedge_connection=(connection)
232
+ @hedge_connection = connection
233
+
234
+ return unless connection
235
+
236
+ @is_hedge_connection = !connection.hedge_connection.nil?
237
+
238
+ return if @is_hedge_connection
239
+
240
+ connection.hedge_connection = self
241
+ end
242
+
243
+ def try_yield_hedge_connection
244
+ return yield(nil) unless (hedge_conn = @hedge_connection)
245
+
246
+ @hedge_connection = nil
247
+
248
+ begin
249
+ hedge_conn.try_yield_hedge_connection do
250
+ yield(hedge_conn)
251
+ end
252
+ ensure
253
+ @hedge_connection = hedge_conn
254
+ end
255
+ end
256
+
257
+ # connections should not merge with their hedge connections
258
+ def mergeable?(connection)
259
+ super && connection.hedge_connection != self
260
+ end
261
+
262
+ def send(request)
263
+ super.tap do
264
+ if @hedge_connection && !request.hedge_request
265
+ request.hedge_request = hedge_request = request.dup
266
+ @hedge_connection.send(hedge_request)
267
+ end
268
+ end
269
+ end
270
+
271
+ private
272
+
273
+ def send_request_to_parser(request, *)
274
+ # if original request was in the queue, but has since been finished
275
+ # in the hedge connection.
276
+ return false if request.response&.finished?
277
+
278
+ super
279
+ end
280
+ end
281
+ end
282
+ register_plugin(:hedging, Hedging)
283
+ end
284
+ end
@@ -28,6 +28,7 @@ module HTTPX
28
28
  end
29
29
 
30
30
  module InstanceMethods
31
+ # returns a new session which authenticates requests using NTLM auth.
31
32
  def ntlm_auth(user, password, domain = nil)
32
33
  with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
33
34
  end
@@ -40,9 +41,10 @@ module HTTPX
40
41
 
41
42
  if ntlm
42
43
  request.authorize(ntlm.negotiate)
43
- probe_response = wrap { super(request).first }
44
+ responses = wrap { super(request) }
45
+ probe_response = responses.first
44
46
 
45
- return probe_response unless probe_response.is_a?(Response)
47
+ return responses unless probe_response.is_a?(Response)
46
48
 
47
49
  if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
48
50
  request.transition(:idle)
@@ -50,7 +52,7 @@ module HTTPX
50
52
  request.authorize(ntlm.authenticate(request, probe_response.headers["www-authenticate"]).encode("utf-8"))
51
53
  super(request)
52
54
  else
53
- probe_response
55
+ responses
54
56
  end
55
57
  else
56
58
  super(request)
@@ -54,6 +54,7 @@ module HTTPX
54
54
  end
55
55
 
56
56
  module InstanceMethods
57
+ # returns a new session which authenticates requests using NTLM v2 auth.
57
58
  def ntlm_auth(user, password, domain = nil)
58
59
  with(ntlm: Authenticator.new(user, password, domain: domain))
59
60
  end
@@ -66,9 +67,10 @@ module HTTPX
66
67
 
67
68
  if ntlm
68
69
  request.authorize(ntlm.negotiate)
69
- probe_response = wrap { super(request).first }
70
+ responses = wrap { super(request) }
71
+ probe_response = responses.first
70
72
 
71
- return probe_response unless probe_response.is_a?(Response)
73
+ return responses unless probe_response.is_a?(Response)
72
74
 
73
75
  if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
74
76
  request.transition(:idle)
@@ -77,7 +79,7 @@ module HTTPX
77
79
  probe_response.headers["www-authenticate"]).encode("utf-8"))
78
80
  super(request)
79
81
  else
80
- probe_response
82
+ responses
81
83
  end
82
84
  else
83
85
  super(request)
@@ -290,7 +290,7 @@ module HTTPX
290
290
 
291
291
  def reset_auth_header_value!
292
292
  super.tap do
293
- @oauth_session.reset if @oauth_session
293
+ @oauth_session&.reset!
294
294
  end
295
295
  end
296
296
 
@@ -323,7 +323,7 @@ module HTTPX
323
323
  module InstanceMethods
324
324
  private
325
325
 
326
- def prepare_to_retry(_request, response)
326
+ def prepare_to_retry(*)
327
327
  @oauth_session.reset! if @oauth_session
328
328
 
329
329
  super