httpx 1.8.0 → 1.8.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.
- checksums.yaml +4 -4
- data/doc/release_notes/1_7_1.md +1 -2
- data/doc/release_notes/1_8_1.md +27 -0
- data/lib/httpx/adapters/datadog.rb +9 -0
- data/lib/httpx/adapters/faraday.rb +2 -2
- data/lib/httpx/adapters/webmock.rb +6 -0
- data/lib/httpx/connection/http1.rb +5 -11
- data/lib/httpx/connection/http2.rb +33 -6
- data/lib/httpx/connection.rb +44 -15
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +8 -9
- data/lib/httpx/io/tcp.rb +11 -4
- data/lib/httpx/io/unix.rb +2 -2
- data/lib/httpx/loggable.rb +11 -4
- data/lib/httpx/plugins/auth/digest.rb +1 -1
- data/lib/httpx/plugins/auth.rb +11 -2
- data/lib/httpx/plugins/callbacks.rb +12 -0
- data/lib/httpx/plugins/digest_auth.rb +4 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +25 -1
- data/lib/httpx/plugins/persistent.rb +18 -2
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/stream.rb +2 -2
- data/lib/httpx/plugins/stream_bidi.rb +2 -0
- data/lib/httpx/pool.rb +2 -2
- data/lib/httpx/request/body.rb +2 -2
- data/lib/httpx/request.rb +24 -9
- data/lib/httpx/resolver/cache/base.rb +1 -8
- data/lib/httpx/resolver/https.rb +20 -20
- data/lib/httpx/resolver/native.rb +7 -0
- data/lib/httpx/resolver/resolver.rb +4 -0
- data/lib/httpx/resolver/system.rb +5 -2
- data/lib/httpx/response/body.rb +2 -2
- data/lib/httpx/response.rb +3 -4
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +6 -1
- data/lib/httpx/session_extensions.rb +2 -2
- data/lib/httpx/timers.rb +3 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http2.rbs +3 -0
- data/sig/connection.rbs +5 -1
- data/sig/loggable.rbs +4 -0
- data/sig/plugins/auth.rbs +4 -0
- data/sig/plugins/persistent.rbs +3 -0
- data/sig/request.rbs +2 -3
- data/sig/resolver/system.rbs +3 -0
- data/sig/selector.rbs +2 -0
- data/sig/timers.rbs +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eece37bd091a7fea17ccc62eca63acaee714c08658d081a938fc38495a4c8813
|
|
4
|
+
data.tar.gz: 72dcd038803106f08a0317c8d7bc97d22d6cdf5c469ba8f8f9d380d00e8f5ea7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5af43571f7da30f709883474fb61ef3661b31fa836d249c423726f4815fb7b2bf5c1281e22ca7ad15c00ed26b2ffc950d521b14a9f5c02e7d95d4198b4b986d0
|
|
7
|
+
data.tar.gz: bd73537be9a610b590d7897cd96f7b6213ece24ea7079ce995a3e05cbee363dcf0f575405ea8d2e612a64737e3bbc70dbdc25ab9a24f5b7e154b9a5187974ffe
|
data/doc/release_notes/1_7_1.md
CHANGED
|
@@ -17,5 +17,4 @@
|
|
|
17
17
|
* `:stream_bidi` plugin: fix internal state preventing bidi requests from being retried.
|
|
18
18
|
* `:stream_bidi` plugin: will only allow initial request body being passed using `:body` param (others, like `:json`, will raise an exception)
|
|
19
19
|
* https resolver: return `:idle` on `#state` calls when no connection is available (sometimes called in internal log messages).
|
|
20
|
-
* selector loop fix: when there are no selectables and an interval is passed, sleep instead of returning (thereby avoiding potential busy loop).
|
|
21
|
-
*
|
|
20
|
+
* selector loop fix: when there are no selectables and an interval is passed, sleep instead of returning (thereby avoiding potential busy loop).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 1.8.1
|
|
2
|
+
|
|
3
|
+
## Improvements
|
|
4
|
+
|
|
5
|
+
* the session will now eagerly write initial data to connections instead of waiting for I/O the first time (saves a syscall).
|
|
6
|
+
* removed all rescue blocks rescuing `StandardError` internally; this could interfere with usages of `httpx` inside `Timeout.timeout` blocks, which may emit an exception at any point, including in blocks where that exception could be rescued, leaving the session in a potentially irrecoverable state and looping.
|
|
7
|
+
* debug logs change the display of ids from 'tid:123, fid:123' to 'tid=123 fid:123'.
|
|
8
|
+
* and when using the `datadog` adapter, all related correlation ids (trace id, span id, etc) will also be added to the identifiers in the same format
|
|
9
|
+
* plugins built on top of the `:retries` plugin will skip the `retry_after` interval when certain conditions are met.
|
|
10
|
+
* `:persistent` plugin: request will be retried immediately, when the error happens after the keep alive interval expired.
|
|
11
|
+
* `:auth` plugin: request will be retried immediately if it failed due to a "401 Unauthorized Error".
|
|
12
|
+
* `:ssrf_filter` plugin: removed public/private references of exception message.
|
|
13
|
+
|
|
14
|
+
## Bugfixes
|
|
15
|
+
|
|
16
|
+
* `:auth` plugin: handle edge case where `:auth_header_expires_at` returns no value.
|
|
17
|
+
* `:digest_auth` plugin: do not retry if there was an issue building building the authorization header after the probe.
|
|
18
|
+
* `webmock` adapter: prevent mocked requests from opening a connection to the peer server that will not be used.
|
|
19
|
+
* clear and cancel request timers on completion or reset, ensuring that they won't be retained way past the point where the request is gone.
|
|
20
|
+
* connection: cancel and discard ping timer before closing (removes timer reference)
|
|
21
|
+
* connection: fix logic where purging-while-resending requests to a coalesced connection was leaving references in the old connection.
|
|
22
|
+
* moved an `on_body_chunk` callback to the `:callbacks` plugin, where it's used and expected to be defined.
|
|
23
|
+
* `http2`: avoid closing the connection on internal errors which are supposed to be specific to a request, such as the max headers or max response body size checks.
|
|
24
|
+
* system resolver will use an internal exception when using `Timeout.timeout` on resolutions to avoid conflicts with the default timeout exception class.
|
|
25
|
+
* prevent edge case where `#alpn_protocol` was being called on a `TCPSocket`.
|
|
26
|
+
* fixed v1.8.0 regression causing connection to loop when an error happens while connecting.
|
|
27
|
+
* resolver cache: fixed cache eviction logic, where alias answers weren't being evicted, causing resolutions to stall after a certain number of resolutions.
|
|
@@ -185,6 +185,13 @@ module Datadog::Tracing
|
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
+
# patches httpx debug logs to include datadog correlation ids
|
|
189
|
+
module LoggablePatch
|
|
190
|
+
def log_identifiers
|
|
191
|
+
"#{super} #{Datadog::Tracing.log_correlation}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
188
195
|
module Configuration
|
|
189
196
|
# Default settings for httpx
|
|
190
197
|
#
|
|
@@ -297,6 +304,8 @@ module Datadog::Tracing
|
|
|
297
304
|
|
|
298
305
|
::HTTPX.send(:remove_const, :Session)
|
|
299
306
|
::HTTPX.send(:const_set, :Session, datadog_session.class)
|
|
307
|
+
|
|
308
|
+
::HTTPX::Loggable.singleton_class.prepend(LoggablePatch)
|
|
300
309
|
end
|
|
301
310
|
end
|
|
302
311
|
|
|
@@ -162,18 +162,12 @@ module HTTPX
|
|
|
162
162
|
|
|
163
163
|
return unless request
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
request.log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
|
|
165
|
+
request.log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
|
166
|
+
request.log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
response = request.response
|
|
170
169
|
|
|
171
|
-
|
|
172
|
-
rescue StandardError => e
|
|
173
|
-
error_response = ErrorResponse.new(request, e)
|
|
174
|
-
request.response = error_response
|
|
175
|
-
dispatch(request)
|
|
176
|
-
end
|
|
170
|
+
response << chunk
|
|
177
171
|
end
|
|
178
172
|
|
|
179
173
|
def on_complete
|
|
@@ -416,7 +410,7 @@ module HTTPX
|
|
|
416
410
|
end
|
|
417
411
|
|
|
418
412
|
def capitalized(field)
|
|
419
|
-
UPCASED
|
|
413
|
+
UPCASED.fetch(field) { field.split("-").map(&:capitalize).join("-") }
|
|
420
414
|
end
|
|
421
415
|
end
|
|
422
416
|
end
|
|
@@ -37,6 +37,7 @@ module HTTPX
|
|
|
37
37
|
@streams = {}
|
|
38
38
|
@drains = {}
|
|
39
39
|
@pings = []
|
|
40
|
+
@streams_to_close_after_receive = []
|
|
40
41
|
@buffer = buffer
|
|
41
42
|
@handshake_completed = false
|
|
42
43
|
@wait_for_handshake = @settings.key?(:wait_for_handshake) ? @settings.delete(:wait_for_handshake) : true
|
|
@@ -110,6 +111,12 @@ module HTTPX
|
|
|
110
111
|
|
|
111
112
|
def <<(data)
|
|
112
113
|
@connection << data
|
|
114
|
+
|
|
115
|
+
while (stream, request, error = @streams_to_close_after_receive.shift)
|
|
116
|
+
# these streams were marked for cancellation due to errors found while processing the
|
|
117
|
+
# data received by the peer.
|
|
118
|
+
emit_stream_error(stream, request, error)
|
|
119
|
+
end
|
|
113
120
|
end
|
|
114
121
|
|
|
115
122
|
def send(request, head = false)
|
|
@@ -128,7 +135,7 @@ module HTTPX
|
|
|
128
135
|
rescue ::HTTP2::Error::StreamLimitExceeded
|
|
129
136
|
@pending.unshift(request)
|
|
130
137
|
false
|
|
131
|
-
rescue
|
|
138
|
+
rescue ::HTTP2::Error::Error, ArgumentError => e
|
|
132
139
|
emit(:error, request, e)
|
|
133
140
|
end
|
|
134
141
|
|
|
@@ -333,6 +340,8 @@ module HTTPX
|
|
|
333
340
|
@streams[request] = stream
|
|
334
341
|
|
|
335
342
|
handle(request, stream) if request.expects?
|
|
343
|
+
rescue HTTPX::Error => e
|
|
344
|
+
@streams_to_close_after_receive << [stream, request, e]
|
|
336
345
|
end
|
|
337
346
|
|
|
338
347
|
def on_stream_trailers(stream, request, response, h)
|
|
@@ -345,7 +354,18 @@ module HTTPX
|
|
|
345
354
|
def on_stream_data(stream, request, data)
|
|
346
355
|
request.log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
|
|
347
356
|
request.log(level: 2, color: :green) { "#{stream.id}: <- #{log_redact_body(data.inspect)}" }
|
|
357
|
+
|
|
358
|
+
return unless request.response
|
|
359
|
+
|
|
348
360
|
request.response << data
|
|
361
|
+
rescue HTTPX::Error => e
|
|
362
|
+
if stream.state == :closing
|
|
363
|
+
# there won't be any more chunks to process after this one.
|
|
364
|
+
emit_stream_error(stream, request, e)
|
|
365
|
+
else
|
|
366
|
+
# defer until the last chunk from the payload is processed.
|
|
367
|
+
@streams_to_close_after_receive << [stream, request, e]
|
|
368
|
+
end
|
|
349
369
|
end
|
|
350
370
|
|
|
351
371
|
def on_stream_refuse(stream, request, error)
|
|
@@ -381,11 +401,12 @@ module HTTPX
|
|
|
381
401
|
emit(:response, request, response)
|
|
382
402
|
end
|
|
383
403
|
else
|
|
384
|
-
response = request.response
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
404
|
+
if (response = request.response)
|
|
405
|
+
if response.is_a?(Response) && response.status == 421
|
|
406
|
+
emit(:error, request, :http_1_1_required)
|
|
407
|
+
else
|
|
408
|
+
emit(:response, request, response)
|
|
409
|
+
end
|
|
389
410
|
end
|
|
390
411
|
end
|
|
391
412
|
send(@pending.shift) unless @pending.empty?
|
|
@@ -509,6 +530,12 @@ module HTTPX
|
|
|
509
530
|
emit(:pong)
|
|
510
531
|
end
|
|
511
532
|
|
|
533
|
+
def emit_stream_error(stream, request, error)
|
|
534
|
+
teardown(request)
|
|
535
|
+
stream.close
|
|
536
|
+
emit(:error, request, error)
|
|
537
|
+
end
|
|
538
|
+
|
|
512
539
|
def teardown(request = nil)
|
|
513
540
|
if request
|
|
514
541
|
@drains.delete(request)
|
data/lib/httpx/connection.rb
CHANGED
|
@@ -161,15 +161,17 @@ module HTTPX
|
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def purge_pending(&block)
|
|
164
|
-
pendings = []
|
|
165
164
|
if @parser
|
|
166
165
|
pending = @parser.pending
|
|
167
166
|
@inflight -= pending.size
|
|
168
|
-
|
|
167
|
+
pending.reject! do |req|
|
|
168
|
+
block.call(req)
|
|
169
|
+
true
|
|
170
|
+
end
|
|
169
171
|
end
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
@pending.reject! do |req|
|
|
173
|
+
block.call(req)
|
|
174
|
+
true
|
|
173
175
|
end
|
|
174
176
|
end
|
|
175
177
|
|
|
@@ -203,7 +205,7 @@ module HTTPX
|
|
|
203
205
|
return @parser.interests if @parser
|
|
204
206
|
|
|
205
207
|
nil
|
|
206
|
-
rescue
|
|
208
|
+
rescue Error => e
|
|
207
209
|
on_error(e)
|
|
208
210
|
nil
|
|
209
211
|
end
|
|
@@ -215,6 +217,8 @@ module HTTPX
|
|
|
215
217
|
def call
|
|
216
218
|
case @state
|
|
217
219
|
when :idle
|
|
220
|
+
return if no_more_requests?
|
|
221
|
+
|
|
218
222
|
connect
|
|
219
223
|
|
|
220
224
|
# when opening the tcp or ssl socket fails
|
|
@@ -222,7 +226,7 @@ module HTTPX
|
|
|
222
226
|
|
|
223
227
|
consume
|
|
224
228
|
when :closed
|
|
225
|
-
return if
|
|
229
|
+
return if no_more_requests?
|
|
226
230
|
|
|
227
231
|
# there are pending requests to send, restart the state machine.
|
|
228
232
|
idling
|
|
@@ -247,10 +251,14 @@ module HTTPX
|
|
|
247
251
|
consume
|
|
248
252
|
end
|
|
249
253
|
nil
|
|
250
|
-
rescue
|
|
254
|
+
rescue Errno::ECONNRESET,
|
|
255
|
+
Errno::EINVAL,
|
|
256
|
+
SocketError,
|
|
257
|
+
IOError,
|
|
258
|
+
TLSError => e
|
|
251
259
|
@write_buffer.clear
|
|
252
260
|
on_io_error(e)
|
|
253
|
-
rescue
|
|
261
|
+
rescue Error => e
|
|
254
262
|
@write_buffer.clear
|
|
255
263
|
on_error(e)
|
|
256
264
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
@@ -258,6 +266,10 @@ module HTTPX
|
|
|
258
266
|
raise e
|
|
259
267
|
end
|
|
260
268
|
|
|
269
|
+
def initial_call
|
|
270
|
+
call
|
|
271
|
+
end
|
|
272
|
+
|
|
261
273
|
def close
|
|
262
274
|
transition(:active) if @state == :inactive
|
|
263
275
|
|
|
@@ -314,6 +326,11 @@ module HTTPX
|
|
|
314
326
|
# (example: HTTP/1 parser disabling pipelining)
|
|
315
327
|
return if @state == :idle && @pending.any?
|
|
316
328
|
|
|
329
|
+
if @ping_timer
|
|
330
|
+
@ping_timer.cancel
|
|
331
|
+
@ping_timer = nil
|
|
332
|
+
end
|
|
333
|
+
|
|
317
334
|
parser = @parser
|
|
318
335
|
|
|
319
336
|
if parser && parser.respond_to?(:max_concurrent_requests)
|
|
@@ -433,6 +450,11 @@ module HTTPX
|
|
|
433
450
|
|
|
434
451
|
def on_io_error(e)
|
|
435
452
|
on_error(e)
|
|
453
|
+
|
|
454
|
+
# do not force close if parser resets the connection.
|
|
455
|
+
# can happen i.e. when HTTP/1.1 pipelining is disabled.
|
|
456
|
+
return if @state == :idle && @pending.any?
|
|
457
|
+
|
|
436
458
|
force_close(true)
|
|
437
459
|
end
|
|
438
460
|
|
|
@@ -456,7 +478,7 @@ module HTTPX
|
|
|
456
478
|
reset
|
|
457
479
|
end
|
|
458
480
|
|
|
459
|
-
# :
|
|
481
|
+
# simplecov:disable
|
|
460
482
|
def inspect
|
|
461
483
|
"#<#{self.class}:#{object_id} " \
|
|
462
484
|
"@origin=#{@origin} " \
|
|
@@ -464,7 +486,7 @@ module HTTPX
|
|
|
464
486
|
"@pending=#{@pending.size} " \
|
|
465
487
|
"@io=#{@io}>"
|
|
466
488
|
end
|
|
467
|
-
# :
|
|
489
|
+
# simplecov:enable
|
|
468
490
|
|
|
469
491
|
private
|
|
470
492
|
|
|
@@ -490,7 +512,7 @@ module HTTPX
|
|
|
490
512
|
# * the number of pending requests
|
|
491
513
|
# * the number of inflight requests
|
|
492
514
|
# * whether the write buffer has bytes (i.e. for close handshake)
|
|
493
|
-
if
|
|
515
|
+
if no_more_requests? && @write_buffer.empty?
|
|
494
516
|
no_more_requests_loop_check if @parser && @parser.pending.any?
|
|
495
517
|
|
|
496
518
|
# terminate if an altsvc connection has been established
|
|
@@ -540,7 +562,7 @@ module HTTPX
|
|
|
540
562
|
break if @state == :closing || @state == :closed
|
|
541
563
|
|
|
542
564
|
# exit #consume altogether if all outstanding requests have been dealt with
|
|
543
|
-
if
|
|
565
|
+
if no_more_requests? && @write_buffer.empty? # rubocop:disable Style/Next
|
|
544
566
|
no_more_requests_loop_check if @parser && @parser.pending.any?
|
|
545
567
|
|
|
546
568
|
# terminate if an altsvc connection has been established
|
|
@@ -960,6 +982,7 @@ module HTTPX
|
|
|
960
982
|
ping_timeout = @options.timeout[:ping_timeout]
|
|
961
983
|
|
|
962
984
|
@ping_timer = @current_selector.after(ping_timeout) do
|
|
985
|
+
log(level: 3) { "ping timeout expired..." }
|
|
963
986
|
error = PingTimeoutError.new(ping_timeout, "Timed out after #{ping_timeout} seconds")
|
|
964
987
|
on_error(error)
|
|
965
988
|
end
|
|
@@ -986,6 +1009,11 @@ module HTTPX
|
|
|
986
1009
|
"along with debug logs"
|
|
987
1010
|
end
|
|
988
1011
|
|
|
1012
|
+
# true when there are no more pending nor inflight (in parser) requests
|
|
1013
|
+
def no_more_requests?
|
|
1014
|
+
@pending.empty? && @inflight.zero?
|
|
1015
|
+
end
|
|
1016
|
+
|
|
989
1017
|
# recover internal state and emit all relevant error responses when +error+ was raised.
|
|
990
1018
|
# this takes an optiona +request+ which may have already been handled and can be opted out
|
|
991
1019
|
# in the state recovery process.
|
|
@@ -1099,13 +1127,14 @@ module HTTPX
|
|
|
1099
1127
|
end
|
|
1100
1128
|
|
|
1101
1129
|
timer = selector.after(timeout, callback)
|
|
1102
|
-
|
|
1130
|
+
timer.label = label
|
|
1131
|
+
request.active_timeouts << timer
|
|
1103
1132
|
|
|
1104
1133
|
Array(finish_events).each do |event|
|
|
1105
1134
|
# clean up request timeouts if the connection errors out
|
|
1106
1135
|
request.set_timeout_callback(event) do
|
|
1107
1136
|
timer.cancel
|
|
1108
|
-
request.active_timeouts.delete(
|
|
1137
|
+
request.active_timeouts.delete(timer)
|
|
1109
1138
|
end
|
|
1110
1139
|
end
|
|
1111
1140
|
end
|
data/lib/httpx/headers.rb
CHANGED
|
@@ -140,12 +140,12 @@ module HTTPX
|
|
|
140
140
|
@headers.to_s
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
# :
|
|
143
|
+
# simplecov:disable
|
|
144
144
|
def inspect
|
|
145
145
|
"#<#{self.class}:#{object_id} " \
|
|
146
146
|
"#{to_hash.inspect}>"
|
|
147
147
|
end
|
|
148
|
-
# :
|
|
148
|
+
# simplecov:enable
|
|
149
149
|
|
|
150
150
|
# this is internal API and doesn't abide to other public API
|
|
151
151
|
# guarantees, like downcasing strings.
|
data/lib/httpx/io/ssl.rb
CHANGED
|
@@ -20,8 +20,9 @@ module HTTPX
|
|
|
20
20
|
super
|
|
21
21
|
|
|
22
22
|
@ssl_session = nil
|
|
23
|
-
ctx_options = TLS_OPTIONS
|
|
24
|
-
|
|
23
|
+
ctx_options = TLS_OPTIONS
|
|
24
|
+
ctx_options = ctx_options.merge(options.ssl) if options.ssl && !options.ssl.empty?
|
|
25
|
+
@sni_hostname = (ctx_options.delete(:hostname) if ctx_options.key?(:hostname)) || @hostname
|
|
25
26
|
|
|
26
27
|
if @keep_open && @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
27
28
|
# externally initiated ssl socket
|
|
@@ -29,7 +30,7 @@ module HTTPX
|
|
|
29
30
|
@state = :negotiated
|
|
30
31
|
else
|
|
31
32
|
@ctx = OpenSSL::SSL::SSLContext.new
|
|
32
|
-
@ctx.set_params(ctx_options)
|
|
33
|
+
@ctx.set_params(ctx_options)
|
|
33
34
|
unless @ctx.session_cache_mode.nil? # a dummy method on JRuby
|
|
34
35
|
@ctx.session_cache_mode =
|
|
35
36
|
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT | OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
|
|
@@ -51,24 +52,22 @@ module HTTPX
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def protocol
|
|
54
|
-
|
|
55
|
+
return super unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
56
|
+
|
|
55
57
|
@io.alpn_protocol || super
|
|
56
|
-
rescue StandardError
|
|
57
|
-
super
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
if RUBY_ENGINE == "jruby"
|
|
61
61
|
# in jruby, alpn_protocol may return ""
|
|
62
62
|
# https://github.com/jruby/jruby-openssl/issues/287
|
|
63
63
|
def protocol
|
|
64
|
-
|
|
64
|
+
return super unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
65
|
+
|
|
65
66
|
proto = @io.alpn_protocol
|
|
66
67
|
|
|
67
68
|
return super if proto.nil? || proto.empty?
|
|
68
69
|
|
|
69
70
|
proto
|
|
70
|
-
rescue StandardError
|
|
71
|
-
super
|
|
72
71
|
end
|
|
73
72
|
end
|
|
74
73
|
|
data/lib/httpx/io/tcp.rb
CHANGED
|
@@ -182,11 +182,18 @@ module HTTPX
|
|
|
182
182
|
end
|
|
183
183
|
|
|
184
184
|
def close
|
|
185
|
-
return if @keep_open
|
|
185
|
+
return if @keep_open
|
|
186
|
+
|
|
187
|
+
# mark tcp as closed, so that it can be disconnected.
|
|
188
|
+
# this bypasses the state machine API as not not allow the transition
|
|
189
|
+
# from idle to closed in normal circumstances.
|
|
190
|
+
@state = :closed if @state == :idle
|
|
191
|
+
|
|
192
|
+
return if closed?
|
|
186
193
|
|
|
187
194
|
begin
|
|
188
195
|
@io.close
|
|
189
|
-
rescue
|
|
196
|
+
rescue IOError => e
|
|
190
197
|
log { "error closing socket" }
|
|
191
198
|
log { e.full_message(highlight: false) }
|
|
192
199
|
ensure
|
|
@@ -212,7 +219,7 @@ module HTTPX
|
|
|
212
219
|
@state == :idle || @state == :closed
|
|
213
220
|
end
|
|
214
221
|
|
|
215
|
-
# :
|
|
222
|
+
# simplecov:disable
|
|
216
223
|
def inspect
|
|
217
224
|
"#<#{self.class}:#{object_id} " \
|
|
218
225
|
"#{@ip}:#{@port} " \
|
|
@@ -221,7 +228,7 @@ module HTTPX
|
|
|
221
228
|
"@addresses=#{@addresses} " \
|
|
222
229
|
"@state=#{@state}>"
|
|
223
230
|
end
|
|
224
|
-
# :
|
|
231
|
+
# simplecov:enable
|
|
225
232
|
|
|
226
233
|
private
|
|
227
234
|
|
data/lib/httpx/io/unix.rb
CHANGED
data/lib/httpx/loggable.rb
CHANGED
|
@@ -17,6 +17,12 @@ module HTTPX
|
|
|
17
17
|
|
|
18
18
|
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
|
19
19
|
|
|
20
|
+
def self.log_identifiers
|
|
21
|
+
"pid=#{Process.pid} " \
|
|
22
|
+
"tid=#{Thread.current.object_id} " \
|
|
23
|
+
"fid=#{Fiber.current.object_id}"
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
def log(
|
|
21
27
|
level: @options.debug_level,
|
|
22
28
|
color: nil,
|
|
@@ -36,10 +42,9 @@ module HTTPX
|
|
|
36
42
|
klass = klass.superclass
|
|
37
43
|
end
|
|
38
44
|
|
|
39
|
-
message = +"(time
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"self:#{class_name}##{object_id}) "
|
|
45
|
+
message = +"(time=#{Time.now.utc} " \
|
|
46
|
+
"#{Loggable.log_identifiers} " \
|
|
47
|
+
"self=#{class_name}##{object_id}) "
|
|
43
48
|
message << msg.call << "\n"
|
|
44
49
|
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
|
|
45
50
|
debug_stream << message
|
|
@@ -49,6 +54,8 @@ module HTTPX
|
|
|
49
54
|
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
|
50
55
|
end
|
|
51
56
|
|
|
57
|
+
private
|
|
58
|
+
|
|
52
59
|
def log_redact_headers(text)
|
|
53
60
|
log_redact(text, @options.debug_redact == :headers)
|
|
54
61
|
end
|
|
@@ -24,7 +24,7 @@ module HTTPX
|
|
|
24
24
|
|
|
25
25
|
def authenticate(request, authenticate)
|
|
26
26
|
"Digest #{generate_header(request.verb, request.path, authenticate)}"
|
|
27
|
-
rescue
|
|
27
|
+
rescue HTTPX::Error => e
|
|
28
28
|
response = ErrorResponse.new(request, e)
|
|
29
29
|
request.response = response
|
|
30
30
|
request.emit_response(response)
|
data/lib/httpx/plugins/auth.rb
CHANGED
|
@@ -127,8 +127,8 @@ module HTTPX
|
|
|
127
127
|
@auth_header_expires_at = if (expires_in = request.options.auth_header_expires_in)
|
|
128
128
|
Time.now.to_i + expires_in
|
|
129
129
|
elsif (expires_at = request.options.auth_header_expires_at)
|
|
130
|
-
if expires_at.respond_to?(:call)
|
|
131
|
-
expires_at = expires_at.
|
|
130
|
+
if expires_at.respond_to?(:call) && (expires_at = expires_at.call(request))
|
|
131
|
+
expires_at = expires_at.to_f
|
|
132
132
|
raise Error, "`:auth_header_expires_at` must be positive" unless expires_at.positive?
|
|
133
133
|
|
|
134
134
|
expires_at
|
|
@@ -204,6 +204,15 @@ module HTTPX
|
|
|
204
204
|
request.unauthorize!
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
def when_to_retry(request, response, *)
|
|
208
|
+
return super unless response.is_a?(Response)
|
|
209
|
+
|
|
210
|
+
# retry immediately if authentication no longer valid.
|
|
211
|
+
return if response.status == 401
|
|
212
|
+
|
|
213
|
+
super
|
|
214
|
+
end
|
|
215
|
+
|
|
207
216
|
def auth_error?(response, options)
|
|
208
217
|
response.is_a?(Response) && response.status == 401 && dynamic_auth_token?(options.auth_header_value)
|
|
209
218
|
end
|
|
@@ -122,6 +122,18 @@ module HTTPX
|
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
+
module RequestMethods
|
|
126
|
+
def drain_body
|
|
127
|
+
super.tap do |chunk|
|
|
128
|
+
emit(:body_chunk, chunk) if chunk
|
|
129
|
+
rescue StandardError => e
|
|
130
|
+
# in case an error occurs in callback code
|
|
131
|
+
@drain_error = e
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
125
137
|
module ConnectionMethods
|
|
126
138
|
private
|
|
127
139
|
|
|
@@ -51,9 +51,13 @@ module HTTPX
|
|
|
51
51
|
|
|
52
52
|
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
|
53
53
|
request.transition(:idle)
|
|
54
|
+
|
|
54
55
|
if (auth_header = digest.authenticate(request, probe_response.headers["www-authenticate"]))
|
|
55
56
|
request.authorize(auth_header)
|
|
56
57
|
end
|
|
58
|
+
|
|
59
|
+
next(request.response) if request.response
|
|
60
|
+
|
|
57
61
|
super(request)
|
|
58
62
|
else
|
|
59
63
|
probe_response
|
|
@@ -70,6 +70,12 @@ module HTTPX
|
|
|
70
70
|
)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
+
def initial_call
|
|
74
|
+
return unless current_context?
|
|
75
|
+
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
|
|
73
79
|
def interests
|
|
74
80
|
return if connecting? && @pending.none?(&:current_context?)
|
|
75
81
|
|
|
@@ -193,6 +199,12 @@ module HTTPX
|
|
|
193
199
|
end
|
|
194
200
|
|
|
195
201
|
module ResolverNativeMethods
|
|
202
|
+
def initial_call
|
|
203
|
+
return unless @queries.values.any?(&:current_context?) || @connections.any?(&:current_context?)
|
|
204
|
+
|
|
205
|
+
super
|
|
206
|
+
end
|
|
207
|
+
|
|
196
208
|
def calculate_interests
|
|
197
209
|
return if @queries.empty?
|
|
198
210
|
|
|
@@ -218,11 +230,23 @@ module HTTPX
|
|
|
218
230
|
end
|
|
219
231
|
|
|
220
232
|
module ResolverSystemMethods
|
|
233
|
+
def initial_call
|
|
234
|
+
return unless current_context?
|
|
235
|
+
|
|
236
|
+
super
|
|
237
|
+
end
|
|
238
|
+
|
|
221
239
|
def interests
|
|
222
|
-
return unless
|
|
240
|
+
return unless current_context?
|
|
223
241
|
|
|
224
242
|
super
|
|
225
243
|
end
|
|
244
|
+
|
|
245
|
+
private
|
|
246
|
+
|
|
247
|
+
def current_context?
|
|
248
|
+
@queries.any? { |_, conn| conn.current_context? }
|
|
249
|
+
end
|
|
226
250
|
end
|
|
227
251
|
|
|
228
252
|
module FiberConcurrencyH2C
|
|
@@ -55,13 +55,29 @@ module HTTPX
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
+
def reconnectable_error?(error)
|
|
59
|
+
Retries::RECONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def when_to_retry(request, response, *)
|
|
63
|
+
return super unless response.is_a?(ErrorResponse)
|
|
64
|
+
|
|
65
|
+
error = response.error
|
|
66
|
+
# allow request to be retried immediately if the request failed right after the keep alive timeout.
|
|
67
|
+
# the chances are, the request failed because the connect has been dropped by the peer server, so it's
|
|
68
|
+
# fine to reopen.
|
|
69
|
+
return if request.ping? && reconnectable_error?(error)
|
|
70
|
+
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
58
74
|
def retryable_request?(request, response, *)
|
|
59
75
|
super || begin
|
|
60
|
-
return false unless response
|
|
76
|
+
return false unless response.is_a?(ErrorResponse)
|
|
61
77
|
|
|
62
78
|
error = response.error
|
|
63
79
|
|
|
64
|
-
|
|
80
|
+
reconnectable_error?(error)
|
|
65
81
|
end
|
|
66
82
|
end
|
|
67
83
|
|
|
@@ -44,9 +44,9 @@ module HTTPX
|
|
|
44
44
|
|
|
45
45
|
def __on_promise_request(parser, stream, h)
|
|
46
46
|
log(level: 1, color: :yellow) do
|
|
47
|
-
# :
|
|
47
|
+
# simplecov:disable
|
|
48
48
|
h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
|
|
49
|
-
# :
|
|
49
|
+
# simplecov:enable
|
|
50
50
|
end
|
|
51
51
|
headers = @options.headers_class.new(h)
|
|
52
52
|
path = headers[":path"]
|
|
@@ -149,7 +149,7 @@ module HTTPX
|
|
|
149
149
|
SsrfFilter.unsafe_ip_address?(ipaddr) || @options.extra_unsafe_ranges&.any? { |r| r.include?(ipaddr) }
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
raise ServerSideRequestForgeryError, "#{@origin.host} has no
|
|
152
|
+
raise ServerSideRequestForgeryError, "#{@origin.host} has no allowed IP addresses" if addrs.empty?
|
|
153
153
|
|
|
154
154
|
super
|
|
155
155
|
end
|
data/lib/httpx/plugins/stream.rb
CHANGED
data/lib/httpx/pool.rb
CHANGED
|
@@ -173,7 +173,7 @@ module HTTPX
|
|
|
173
173
|
end
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
-
# :
|
|
176
|
+
# simplecov:disable
|
|
177
177
|
def inspect
|
|
178
178
|
"#<#{self.class}:#{object_id} " \
|
|
179
179
|
"@max_connections=#{@max_connections} " \
|
|
@@ -181,7 +181,7 @@ module HTTPX
|
|
|
181
181
|
"@pool_timeout=#{@pool_timeout} " \
|
|
182
182
|
"@connections=#{@connections.size}>"
|
|
183
183
|
end
|
|
184
|
-
# :
|
|
184
|
+
# simplecov:enable
|
|
185
185
|
|
|
186
186
|
private
|
|
187
187
|
|
data/lib/httpx/request/body.rb
CHANGED
|
@@ -114,12 +114,12 @@ module HTTPX
|
|
|
114
114
|
@headers.add("transfer-encoding", "chunked")
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
# :
|
|
117
|
+
# simplecov:disable
|
|
118
118
|
def inspect
|
|
119
119
|
"#<#{self.class}:#{object_id} " \
|
|
120
120
|
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
|
121
121
|
end
|
|
122
|
-
# :
|
|
122
|
+
# simplecov:enable
|
|
123
123
|
|
|
124
124
|
class << self
|
|
125
125
|
def initialize_body(params)
|
data/lib/httpx/request.rb
CHANGED
|
@@ -122,6 +122,7 @@ module HTTPX
|
|
|
122
122
|
|
|
123
123
|
def complete!(response = @response)
|
|
124
124
|
emit(:complete, response)
|
|
125
|
+
reset_timers(true)
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
# whether request has been buffered with a ping
|
|
@@ -268,21 +269,19 @@ module HTTPX
|
|
|
268
269
|
|
|
269
270
|
# consumes and returns the next available chunk of request body that can be sent
|
|
270
271
|
def drain_body
|
|
271
|
-
return
|
|
272
|
+
return if @body.nil?
|
|
272
273
|
|
|
273
274
|
@drainer ||= @body.each
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
emit(:body_chunk, chunk)
|
|
277
|
-
chunk
|
|
275
|
+
@drainer.next.dup
|
|
278
276
|
rescue StopIteration
|
|
279
277
|
nil
|
|
280
278
|
rescue StandardError => e
|
|
279
|
+
# in case an error occurs while emitting body chunks
|
|
281
280
|
@drain_error = e
|
|
282
281
|
nil
|
|
283
282
|
end
|
|
284
283
|
|
|
285
|
-
# :
|
|
284
|
+
# simplecov:disable
|
|
286
285
|
def inspect
|
|
287
286
|
"#<#{self.class}:#{object_id} " \
|
|
288
287
|
"#{@verb} " \
|
|
@@ -290,7 +289,7 @@ module HTTPX
|
|
|
290
289
|
"@headers=#{@headers} " \
|
|
291
290
|
"@body=#{@body}>"
|
|
292
291
|
end
|
|
293
|
-
# :
|
|
292
|
+
# simplecov:enable
|
|
294
293
|
|
|
295
294
|
# moves on to the +nextstate+ of the request state machine (when all preconditions are met)
|
|
296
295
|
def transition(nextstate)
|
|
@@ -299,7 +298,10 @@ module HTTPX
|
|
|
299
298
|
@body.rewind
|
|
300
299
|
@ping = false
|
|
301
300
|
@response = @drainer = nil
|
|
302
|
-
|
|
301
|
+
|
|
302
|
+
# request may be sent to a different connection and will be
|
|
303
|
+
# reassigned a new set of timers.
|
|
304
|
+
reset_timers(false)
|
|
303
305
|
when :headers
|
|
304
306
|
return unless @state == :idle
|
|
305
307
|
|
|
@@ -326,7 +328,7 @@ module HTTPX
|
|
|
326
328
|
return if @state == :expect
|
|
327
329
|
|
|
328
330
|
end
|
|
329
|
-
log(level: 3) { "#{@state}
|
|
331
|
+
log(level: 3) { "#{@state} -> #{nextstate}" }
|
|
330
332
|
@state = nextstate
|
|
331
333
|
emit(@state, self)
|
|
332
334
|
nil
|
|
@@ -363,6 +365,19 @@ module HTTPX
|
|
|
363
365
|
|
|
364
366
|
@on_response_arrived.call
|
|
365
367
|
end
|
|
368
|
+
|
|
369
|
+
private
|
|
370
|
+
|
|
371
|
+
def reset_timers(reset_total_request_timers)
|
|
372
|
+
timers = @active_timeouts
|
|
373
|
+
|
|
374
|
+
until (timer = timers.shift).nil?
|
|
375
|
+
next if !reset_total_request_timers && timer.label == :total_request_timeout
|
|
376
|
+
|
|
377
|
+
# cancel active timers.
|
|
378
|
+
timer.cancel
|
|
379
|
+
end
|
|
380
|
+
end
|
|
366
381
|
end
|
|
367
382
|
end
|
|
368
383
|
|
|
@@ -106,14 +106,7 @@ module HTTPX
|
|
|
106
106
|
name = entry["name"]
|
|
107
107
|
next unless name != hostname
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
case family
|
|
112
|
-
when Socket::AF_INET6
|
|
113
|
-
lookups[name] << entry
|
|
114
|
-
when Socket::AF_INET
|
|
115
|
-
lookups[name].unshift(entry)
|
|
116
|
-
end
|
|
109
|
+
_set(name, family, [entry], lookups, hostnames)
|
|
117
110
|
end
|
|
118
111
|
end
|
|
119
112
|
|
data/lib/httpx/resolver/https.rb
CHANGED
|
@@ -126,27 +126,27 @@ module HTTPX
|
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def on_response(request, response)
|
|
129
|
-
response.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
location_uri = response.uri.merge(location_uri) if location_uri.relative?
|
|
142
|
-
|
|
143
|
-
# we assume that the DNS server URI changed permanently and move on
|
|
144
|
-
@uri = location_uri
|
|
145
|
-
send_request(hostname, connection)
|
|
146
|
-
return
|
|
147
|
-
end
|
|
129
|
+
if (e = response.error)
|
|
130
|
+
hostname = @requests.delete(request)
|
|
131
|
+
connection = reset_hostname(hostname)
|
|
132
|
+
emit_resolve_error(connection, connection.peer.host, e)
|
|
133
|
+
close_or_resolve
|
|
134
|
+
else
|
|
135
|
+
# @type var response: HTTPX::Response
|
|
136
|
+
if response.status.between?(300, 399) && response.headers.key?("location")
|
|
137
|
+
hostname = @requests[request]
|
|
138
|
+
connection = @queries[hostname]
|
|
139
|
+
location_uri = URI(response.headers["location"])
|
|
140
|
+
location_uri = response.uri.merge(location_uri) if location_uri.relative?
|
|
148
141
|
|
|
149
|
-
|
|
142
|
+
# we assume that the DNS server URI changed permanently and move on
|
|
143
|
+
@uri = location_uri
|
|
144
|
+
send_request(hostname, connection)
|
|
145
|
+
return
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
parse(request, response)
|
|
149
|
+
end
|
|
150
150
|
ensure
|
|
151
151
|
@requests.delete(request)
|
|
152
152
|
end
|
|
@@ -23,6 +23,9 @@ module HTTPX
|
|
|
23
23
|
DONE = 1
|
|
24
24
|
ERROR = 2
|
|
25
25
|
|
|
26
|
+
class AddrinfoTimeoutError < StandardError
|
|
27
|
+
end
|
|
28
|
+
|
|
26
29
|
class << self
|
|
27
30
|
def multi?
|
|
28
31
|
false
|
|
@@ -227,7 +230,7 @@ module HTTPX
|
|
|
227
230
|
begin
|
|
228
231
|
addrs = if resolve_timeout
|
|
229
232
|
|
|
230
|
-
Timeout.timeout(resolve_timeout) do
|
|
233
|
+
Timeout.timeout(resolve_timeout, AddrinfoTimeoutError) do
|
|
231
234
|
__addrinfo_resolve(hostname, scheme)
|
|
232
235
|
end
|
|
233
236
|
else
|
|
@@ -246,7 +249,7 @@ module HTTPX
|
|
|
246
249
|
end
|
|
247
250
|
end
|
|
248
251
|
rescue StandardError => e
|
|
249
|
-
if e.is_a?(
|
|
252
|
+
if e.is_a?(AddrinfoTimeoutError)
|
|
250
253
|
timeouts.shift
|
|
251
254
|
retry unless timeouts.empty?
|
|
252
255
|
e = ResolveTimeoutError.new(resolve_timeout, e.message)
|
data/lib/httpx/response/body.rb
CHANGED
|
@@ -161,13 +161,13 @@ module HTTPX
|
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
-
# :
|
|
164
|
+
# simplecov:disable
|
|
165
165
|
def inspect
|
|
166
166
|
"#<#{self.class}:#{object_id} " \
|
|
167
167
|
"@state=#{@state} " \
|
|
168
168
|
"@length=#{@length}>"
|
|
169
169
|
end
|
|
170
|
-
# :
|
|
170
|
+
# simplecov:enable
|
|
171
171
|
|
|
172
172
|
# rewinds the response payload buffer.
|
|
173
173
|
def rewind
|
data/lib/httpx/response.rb
CHANGED
|
@@ -141,7 +141,7 @@ module HTTPX
|
|
|
141
141
|
bodyless? || (@request.verb == "CONNECT" && @status == 200)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
-
# :
|
|
144
|
+
# simplecov:disable
|
|
145
145
|
def inspect
|
|
146
146
|
"#<#{self.class}:#{object_id} " \
|
|
147
147
|
"HTTP/#{version} " \
|
|
@@ -149,7 +149,7 @@ module HTTPX
|
|
|
149
149
|
"@headers=#{@headers} " \
|
|
150
150
|
"@body=#{@body.bytesize}>"
|
|
151
151
|
end
|
|
152
|
-
# :
|
|
152
|
+
# simplecov:enable
|
|
153
153
|
|
|
154
154
|
# returns an instance of HTTPX::HTTPError if the response has a 4xx or 5xx
|
|
155
155
|
# status code, or nothing.
|
|
@@ -267,7 +267,6 @@ module HTTPX
|
|
|
267
267
|
# response = HTTPX.get("https://some-domain/path") #=> response is HTTPX::Response or HTTPX::ErrorResponse
|
|
268
268
|
# response.raise_for_status #=> raises if it wraps an error
|
|
269
269
|
class ErrorResponse
|
|
270
|
-
include Loggable
|
|
271
270
|
extend Forwardable
|
|
272
271
|
|
|
273
272
|
# the corresponding HTTPX::Request instance.
|
|
@@ -290,7 +289,7 @@ module HTTPX
|
|
|
290
289
|
@response = request.response if request.response.is_a?(Response)
|
|
291
290
|
@error = error
|
|
292
291
|
@options = request.options
|
|
293
|
-
log_exception(@error)
|
|
292
|
+
@request.log_exception(@error)
|
|
294
293
|
finish!
|
|
295
294
|
end
|
|
296
295
|
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -41,6 +41,11 @@ module HTTPX
|
|
|
41
41
|
@selectables.empty? && @timers.empty?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# first time the registered selectables are added, there's probably work to do.
|
|
45
|
+
def initial_call
|
|
46
|
+
@selectables.each(&:initial_call)
|
|
47
|
+
end
|
|
48
|
+
|
|
44
49
|
def next_tick
|
|
45
50
|
catch(:jump_tick) do
|
|
46
51
|
timeout = next_timeout
|
data/lib/httpx/session.rb
CHANGED
|
@@ -319,9 +319,14 @@ module HTTPX
|
|
|
319
319
|
|
|
320
320
|
waiting = false
|
|
321
321
|
|
|
322
|
-
|
|
322
|
+
requests.each do |request|
|
|
323
323
|
send_request(request, selector)
|
|
324
|
+
end
|
|
324
325
|
|
|
326
|
+
# do work first
|
|
327
|
+
selector.initial_call
|
|
328
|
+
|
|
329
|
+
responses = requests.each_with_index.map do |request, idx|
|
|
325
330
|
fetch_response(request, selector, request.options).tap do |response|
|
|
326
331
|
if response.nil?
|
|
327
332
|
pending += 1
|
|
@@ -20,11 +20,11 @@ module HTTPX
|
|
|
20
20
|
$VERBOSE = original_verbosity
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# :
|
|
23
|
+
# simplecov:disable
|
|
24
24
|
if Session.default_options.debug_level > 2
|
|
25
25
|
proxy_session = plugin(:internal_telemetry)
|
|
26
26
|
remove_const(:Session)
|
|
27
27
|
const_set(:Session, proxy_session.class)
|
|
28
28
|
end
|
|
29
|
-
# :
|
|
29
|
+
# simplecov:enable
|
|
30
30
|
end
|
data/lib/httpx/timers.rb
CHANGED
data/lib/httpx/version.rb
CHANGED
data/sig/connection/http2.rbs
CHANGED
|
@@ -16,6 +16,7 @@ module HTTPX
|
|
|
16
16
|
@max_requests: Integer
|
|
17
17
|
@drains: Hash[Request, String]
|
|
18
18
|
@pings: Array[String]
|
|
19
|
+
@streams_to_close_after_receive: Array[[HTTP2::Stream, Request, HTTPX::Error]]
|
|
19
20
|
@buffer: Buffer
|
|
20
21
|
@handshake_completed: bool
|
|
21
22
|
@wait_for_handshake: bool
|
|
@@ -106,6 +107,8 @@ module HTTPX
|
|
|
106
107
|
|
|
107
108
|
def on_pong: (string ping) -> void
|
|
108
109
|
|
|
110
|
+
def emit_stream_error: (HTTP2::Stream stream, Request request, StandardError error) -> void
|
|
111
|
+
|
|
109
112
|
def teardown: (?Request? request) -> void
|
|
110
113
|
|
|
111
114
|
class Error < ::HTTPX::Error
|
data/sig/connection.rbs
CHANGED
|
@@ -92,6 +92,8 @@ module HTTPX
|
|
|
92
92
|
|
|
93
93
|
def call: () -> void
|
|
94
94
|
|
|
95
|
+
def initial_call: () -> void
|
|
96
|
+
|
|
95
97
|
def terminate: () -> void
|
|
96
98
|
|
|
97
99
|
def close: () -> void
|
|
@@ -122,7 +124,7 @@ module HTTPX
|
|
|
122
124
|
|
|
123
125
|
def on_error: (HTTPX::TimeoutError | Error | StandardError error, ?Request? request) -> void
|
|
124
126
|
|
|
125
|
-
def on_io_error: (IOError error) -> void
|
|
127
|
+
def on_io_error: (SocketError | IOError | TLSError error) -> void
|
|
126
128
|
|
|
127
129
|
def on_connect_error: (Exception error) -> void
|
|
128
130
|
|
|
@@ -164,6 +166,8 @@ module HTTPX
|
|
|
164
166
|
|
|
165
167
|
def no_more_requests_loop_check: () -> void
|
|
166
168
|
|
|
169
|
+
def no_more_requests?: () -> bool
|
|
170
|
+
|
|
167
171
|
def handle_error: (StandardError error, ?Request? request) -> void
|
|
168
172
|
|
|
169
173
|
def force_purge: () -> void
|
data/sig/loggable.rbs
CHANGED
|
@@ -12,10 +12,14 @@ module HTTPX
|
|
|
12
12
|
|
|
13
13
|
COLORS: Hash[Symbol, Integer]
|
|
14
14
|
|
|
15
|
+
def self.log_identifiers: () -> String
|
|
16
|
+
|
|
15
17
|
def log: (?level: Integer?, ?color: Symbol?, ?debug_level: Integer, ?debug: _IOLogger?) { () -> String } -> void
|
|
16
18
|
|
|
17
19
|
def log_exception: (Exception error, ?level: Integer, ?color: Symbol, ?debug_level: Integer, ?debug: _IOLogger?) -> void
|
|
18
20
|
|
|
21
|
+
private
|
|
22
|
+
|
|
19
23
|
def log_redact_headers: (_ToS text) -> String
|
|
20
24
|
|
|
21
25
|
def log_redact_body: (_ToS text) -> String
|
data/sig/plugins/auth.rbs
CHANGED
|
@@ -7,6 +7,10 @@ module HTTPX
|
|
|
7
7
|
def auth_header_value: () -> auth_header_value_type?
|
|
8
8
|
|
|
9
9
|
def auth_header_type: () -> String?
|
|
10
|
+
|
|
11
|
+
def auth_header_expires_at: () -> (^(Request request) -> _ToF?)?
|
|
12
|
+
|
|
13
|
+
def auth_header_expires_in: () -> Float?
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
module InstanceMethods
|
data/sig/plugins/persistent.rbs
CHANGED
data/sig/request.rbs
CHANGED
|
@@ -17,7 +17,7 @@ module HTTPX
|
|
|
17
17
|
attr_reader response: response?
|
|
18
18
|
attr_reader http2_stream_options: { ?dependency: Integer, ?exclusive: bool, ?weight: Integer }
|
|
19
19
|
attr_reader drain_error: StandardError?
|
|
20
|
-
attr_reader active_timeouts: Array[
|
|
20
|
+
attr_reader active_timeouts: Array[Timers::Timer]
|
|
21
21
|
|
|
22
22
|
attr_writer connection: Connection?
|
|
23
23
|
attr_writer on_response_arrived: (^() -> void)?
|
|
@@ -93,7 +93,6 @@ module HTTPX
|
|
|
93
93
|
|
|
94
94
|
private
|
|
95
95
|
|
|
96
|
-
def
|
|
97
|
-
|
|
96
|
+
def reset_timers: (bool reset_total_request_timers) -> void
|
|
98
97
|
end
|
|
99
98
|
end
|
data/sig/resolver/system.rbs
CHANGED
data/sig/selector.rbs
CHANGED
data/sig/timers.rbs
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: httpx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.
|
|
4
|
+
version: 1.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -169,6 +169,7 @@ extra_rdoc_files:
|
|
|
169
169
|
- doc/release_notes/1_7_7.md
|
|
170
170
|
- doc/release_notes/1_7_8.md
|
|
171
171
|
- doc/release_notes/1_8_0.md
|
|
172
|
+
- doc/release_notes/1_8_1.md
|
|
172
173
|
files:
|
|
173
174
|
- LICENSE.txt
|
|
174
175
|
- README.md
|
|
@@ -310,6 +311,7 @@ files:
|
|
|
310
311
|
- doc/release_notes/1_7_7.md
|
|
311
312
|
- doc/release_notes/1_7_8.md
|
|
312
313
|
- doc/release_notes/1_8_0.md
|
|
314
|
+
- doc/release_notes/1_8_1.md
|
|
313
315
|
- lib/httpx.rb
|
|
314
316
|
- lib/httpx/adapters/datadog.rb
|
|
315
317
|
- lib/httpx/adapters/faraday.rb
|