httpx 1.8.3 → 1.9.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.
- checksums.yaml +4 -4
- data/doc/release_notes/1_8_4.md +13 -0
- data/doc/release_notes/1_9_0.md +75 -0
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/altsvc.rb +29 -15
- data/lib/httpx/callbacks.rb +2 -2
- data/lib/httpx/chainable.rb +6 -3
- data/lib/httpx/connection/http1.rb +11 -19
- data/lib/httpx/connection/http2.rb +112 -27
- data/lib/httpx/connection.rb +184 -46
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/tcp.rb +18 -14
- data/lib/httpx/options.rb +86 -35
- data/lib/httpx/parser/http1.rb +3 -6
- data/lib/httpx/plugins/auth/digest.rb +1 -2
- data/lib/httpx/plugins/auth.rb +11 -2
- data/lib/httpx/plugins/aws_sdk_authentication.rb +7 -3
- data/lib/httpx/plugins/aws_sigv4.rb +1 -0
- data/lib/httpx/plugins/basic_auth.rb +2 -0
- data/lib/httpx/plugins/callbacks.rb +7 -6
- data/lib/httpx/plugins/circuit_breaker.rb +1 -0
- data/lib/httpx/plugins/cookies/cookie.rb +1 -1
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +2 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +6 -0
- data/lib/httpx/plugins/follow_redirects.rb +8 -6
- data/lib/httpx/plugins/grpc/call.rb +4 -4
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -2
- data/lib/httpx/plugins/grpc.rb +21 -0
- data/lib/httpx/plugins/hedging.rb +284 -0
- data/lib/httpx/plugins/ntlm_auth.rb +1 -0
- data/lib/httpx/plugins/ntlm_v2_auth.rb +1 -0
- data/lib/httpx/plugins/oauth.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +71 -16
- data/lib/httpx/plugins/proxy/ssh.rb +30 -11
- data/lib/httpx/plugins/proxy.rb +66 -45
- data/lib/httpx/plugins/push_promise.rb +4 -2
- data/lib/httpx/plugins/retries.rb +13 -3
- data/lib/httpx/plugins/server_sent_events.rb +2 -1
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/stream.rb +2 -2
- data/lib/httpx/plugins/tracing.rb +61 -14
- data/lib/httpx/plugins/upgrade/h2.rb +1 -1
- data/lib/httpx/plugins/upgrade.rb +2 -0
- data/lib/httpx/plugins/webdav.rb +8 -0
- data/lib/httpx/request/body.rb +2 -1
- data/lib/httpx/request.rb +10 -7
- data/lib/httpx/resolver/multi.rb +2 -2
- data/lib/httpx/resolver/resolver.rb +2 -2
- data/lib/httpx/resolver.rb +5 -1
- data/lib/httpx/response/body.rb +2 -2
- data/lib/httpx/response.rb +19 -11
- data/lib/httpx/selector.rb +3 -3
- data/lib/httpx/session.rb +38 -11
- data/lib/httpx/transcoder/chunker.rb +2 -2
- data/lib/httpx/transcoder/json.rb +7 -7
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +3 -3
- data/sig/callbacks.rbs +1 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +1 -1
- data/sig/connection/http2.rbs +30 -16
- data/sig/connection.rbs +19 -1
- data/sig/headers.rbs +1 -1
- data/sig/httpx.rbs +2 -0
- data/sig/options.rbs +43 -12
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/auth/digest.rbs +0 -1
- data/sig/plugins/auth.rbs +12 -5
- data/sig/plugins/hedging.rbs +44 -0
- data/sig/plugins/proxy/http.rbs +8 -1
- data/sig/plugins/proxy.rbs +3 -2
- data/sig/plugins/retries.rbs +2 -2
- data/sig/plugins/tracing.rbs +14 -5
- data/sig/request/body.rbs +1 -1
- data/sig/request.rbs +2 -1
- data/sig/resolver/entry.rbs +2 -2
- data/sig/response.rbs +1 -2
- data/sig/selector.rbs +2 -2
- data/sig/session.rbs +7 -5
- metadata +7 -1
data/lib/httpx/connection.rb
CHANGED
|
@@ -51,7 +51,11 @@ module HTTPX
|
|
|
51
51
|
@callbacks = @ping_timer = @family =
|
|
52
52
|
@io = @ssl_session = @timeout = @connected_at = @response_received_at = nil
|
|
53
53
|
|
|
54
|
-
@exhausted = @cloned = @main_sibling =
|
|
54
|
+
@exhausted = @cloned = @main_sibling =
|
|
55
|
+
@reset_timeout_on_call =
|
|
56
|
+
# variable used to gate against a potential endless loop where the peer continuously closes the connection with
|
|
57
|
+
# GOAWAY frames without ever processing a request.
|
|
58
|
+
@previously_exhausted_with_error = false
|
|
55
59
|
|
|
56
60
|
@options = Options.new(options)
|
|
57
61
|
@type = initialize_type(uri, @options)
|
|
@@ -62,7 +66,7 @@ module HTTPX
|
|
|
62
66
|
@write_buffer = Buffer.new(@options.buffer_size)
|
|
63
67
|
@pending = []
|
|
64
68
|
@inflight = 0
|
|
65
|
-
@keep_alive_timeout = @options.timeout
|
|
69
|
+
@keep_alive_timeout = @options.timeout.keep_alive_timeout
|
|
66
70
|
@no_more_requests_counter = 0
|
|
67
71
|
|
|
68
72
|
if @options.io
|
|
@@ -74,7 +78,14 @@ module HTTPX
|
|
|
74
78
|
else
|
|
75
79
|
transition(:idle)
|
|
76
80
|
end
|
|
77
|
-
|
|
81
|
+
|
|
82
|
+
case (addresses = @options.addresses)
|
|
83
|
+
when Array
|
|
84
|
+
self.addresses = addresses
|
|
85
|
+
when Hash
|
|
86
|
+
# TODO: && @origin.host not an IP
|
|
87
|
+
self.addresses = addresses[@origin.host]
|
|
88
|
+
end
|
|
78
89
|
end
|
|
79
90
|
|
|
80
91
|
# dupped initialization
|
|
@@ -184,6 +195,14 @@ module HTTPX
|
|
|
184
195
|
end
|
|
185
196
|
end
|
|
186
197
|
|
|
198
|
+
# returns the number of pending requests both in the connection as well as parser
|
|
199
|
+
# pending queues
|
|
200
|
+
def pending_count
|
|
201
|
+
count = @pending.size
|
|
202
|
+
count += @parser.pending.size if @parser.respond_to?(:pending)
|
|
203
|
+
count
|
|
204
|
+
end
|
|
205
|
+
|
|
187
206
|
def purge_pending(&block)
|
|
188
207
|
if @parser
|
|
189
208
|
pending = @parser.pending
|
|
@@ -221,14 +240,12 @@ module HTTPX
|
|
|
221
240
|
def interests
|
|
222
241
|
# connecting
|
|
223
242
|
if connecting?
|
|
224
|
-
|
|
243
|
+
try_connect
|
|
225
244
|
|
|
226
245
|
return @io.interests if connecting?
|
|
227
246
|
end
|
|
228
247
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
nil
|
|
248
|
+
@parser&.interests
|
|
232
249
|
rescue Error => e
|
|
233
250
|
on_error(e)
|
|
234
251
|
nil
|
|
@@ -239,11 +256,16 @@ module HTTPX
|
|
|
239
256
|
end
|
|
240
257
|
|
|
241
258
|
def call
|
|
259
|
+
if @reset_timeout_on_call
|
|
260
|
+
@timeout = nil
|
|
261
|
+
@reset_timeout_on_call = false
|
|
262
|
+
end
|
|
263
|
+
|
|
242
264
|
case @state
|
|
243
265
|
when :idle
|
|
244
266
|
return if no_more_requests?
|
|
245
267
|
|
|
246
|
-
|
|
268
|
+
try_connect
|
|
247
269
|
|
|
248
270
|
# when opening the tcp or ssl socket fails
|
|
249
271
|
return if @state == :closed
|
|
@@ -297,7 +319,7 @@ module HTTPX
|
|
|
297
319
|
def close
|
|
298
320
|
transition(:active) if @state == :inactive
|
|
299
321
|
|
|
300
|
-
@parser
|
|
322
|
+
@parser&.close
|
|
301
323
|
end
|
|
302
324
|
|
|
303
325
|
def terminate
|
|
@@ -327,10 +349,9 @@ module HTTPX
|
|
|
327
349
|
@pending.clear
|
|
328
350
|
elsif (parser = @parser)
|
|
329
351
|
enqueue_pending_requests_from_parser(parser)
|
|
352
|
+
return unless @pending.empty?
|
|
330
353
|
end
|
|
331
354
|
|
|
332
|
-
return unless @pending.empty?
|
|
333
|
-
|
|
334
355
|
disconnect
|
|
335
356
|
emit(:force_closed, delete_pending)
|
|
336
357
|
end
|
|
@@ -350,10 +371,7 @@ module HTTPX
|
|
|
350
371
|
# (example: HTTP/1 parser disabling pipelining)
|
|
351
372
|
return if @state == :idle && @pending.any?
|
|
352
373
|
|
|
353
|
-
|
|
354
|
-
@ping_timer.cancel
|
|
355
|
-
@ping_timer = nil
|
|
356
|
-
end
|
|
374
|
+
reset_ping_timer
|
|
357
375
|
|
|
358
376
|
parser = @parser
|
|
359
377
|
|
|
@@ -371,6 +389,8 @@ module HTTPX
|
|
|
371
389
|
def send(request)
|
|
372
390
|
return @coalesced_connection.send(request) if @coalesced_connection
|
|
373
391
|
|
|
392
|
+
request.connection = self
|
|
393
|
+
|
|
374
394
|
if @parser && !@write_buffer.full?
|
|
375
395
|
if @response_received_at && @keep_alive_timeout &&
|
|
376
396
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
|
@@ -387,6 +407,8 @@ module HTTPX
|
|
|
387
407
|
|
|
388
408
|
send_request_to_parser(request)
|
|
389
409
|
else
|
|
410
|
+
set_connection_timeouts(request) if connecting?
|
|
411
|
+
|
|
390
412
|
@pending << request
|
|
391
413
|
end
|
|
392
414
|
end
|
|
@@ -396,9 +418,7 @@ module HTTPX
|
|
|
396
418
|
|
|
397
419
|
return @timeout if @timeout
|
|
398
420
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
@options.timeout[:operation_timeout]
|
|
421
|
+
@options.timeout.operation_timeout
|
|
402
422
|
end
|
|
403
423
|
|
|
404
424
|
def idling
|
|
@@ -410,7 +430,7 @@ module HTTPX
|
|
|
410
430
|
transition(:idle)
|
|
411
431
|
return unless @parser
|
|
412
432
|
|
|
413
|
-
enqueue_pending_requests_from_parser(parser)
|
|
433
|
+
enqueue_pending_requests_from_parser(@parser)
|
|
414
434
|
@parser = nil
|
|
415
435
|
end
|
|
416
436
|
|
|
@@ -483,16 +503,20 @@ module HTTPX
|
|
|
483
503
|
|
|
484
504
|
def on_error(error, request = nil)
|
|
485
505
|
if error.is_a?(OperationTimeoutError)
|
|
486
|
-
|
|
487
506
|
# inactive connections do not contribute to the select loop, therefore
|
|
488
507
|
# they should not fail due to such errors.
|
|
489
508
|
return if @state == :inactive
|
|
490
509
|
|
|
491
|
-
if @timeout
|
|
492
|
-
@timeout
|
|
510
|
+
if (current_timeout = @timeout || timeout)
|
|
511
|
+
# if @timeout isn't set, it's not a completion-based timeout, i.e.
|
|
512
|
+
# operation timeout; in such a case, we have to reset it once it piggybacks.
|
|
513
|
+
@reset_timeout_on_call ||= @timeout.nil?
|
|
514
|
+
|
|
515
|
+
@timeout = current_timeout - error.timeout
|
|
493
516
|
return unless @timeout <= 0
|
|
494
517
|
|
|
495
518
|
@timeout = nil
|
|
519
|
+
@reset_timeout_on_call = false
|
|
496
520
|
end
|
|
497
521
|
|
|
498
522
|
error = error.to_connection_error if connecting?
|
|
@@ -513,6 +537,11 @@ module HTTPX
|
|
|
513
537
|
|
|
514
538
|
private
|
|
515
539
|
|
|
540
|
+
def try_connect
|
|
541
|
+
emit(:connecting)
|
|
542
|
+
connect
|
|
543
|
+
end
|
|
544
|
+
|
|
516
545
|
def connect
|
|
517
546
|
transition(:open)
|
|
518
547
|
end
|
|
@@ -711,6 +740,7 @@ module HTTPX
|
|
|
711
740
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
|
712
741
|
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
713
742
|
end
|
|
743
|
+
@previously_exhausted_with_error = false
|
|
714
744
|
@response_received_at = Utils.now
|
|
715
745
|
@no_more_requests_counter = 0
|
|
716
746
|
@inflight -= 1
|
|
@@ -726,9 +756,22 @@ module HTTPX
|
|
|
726
756
|
parser.on(:promise) do |request, stream|
|
|
727
757
|
request.emit(:promise, parser, stream)
|
|
728
758
|
end
|
|
729
|
-
parser.on(:exhausted) do
|
|
759
|
+
parser.on(:exhausted) do |error|
|
|
730
760
|
enqueue_pending_requests_from_parser(parser)
|
|
731
761
|
|
|
762
|
+
if error
|
|
763
|
+
if @previously_exhausted_with_error
|
|
764
|
+
@previously_exhausted_with_error = false
|
|
765
|
+
on_error(error)
|
|
766
|
+
next
|
|
767
|
+
else
|
|
768
|
+
# in case the error came while waiting for a PING ack.
|
|
769
|
+
reset_ping_timer
|
|
770
|
+
|
|
771
|
+
@previously_exhausted_with_error = true
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
732
775
|
@exhausted = true
|
|
733
776
|
parser.close
|
|
734
777
|
|
|
@@ -773,11 +816,11 @@ module HTTPX
|
|
|
773
816
|
current_selector = @current_selector
|
|
774
817
|
parser.close
|
|
775
818
|
|
|
776
|
-
other_connection = current_session.find_connection(@origin, current_selector,
|
|
777
|
-
@options.merge(ssl: { alpn_protocols: %w[http/1.1] }))
|
|
778
|
-
other_connection.merge(self)
|
|
779
819
|
request.transition(:idle)
|
|
780
|
-
|
|
820
|
+
current_session.__send__(:send_request, request, current_selector,
|
|
821
|
+
@options.merge(ssl: { alpn_protocols: %w[http/1.1] })) do |other_conn|
|
|
822
|
+
other_conn.merge(self)
|
|
823
|
+
end
|
|
781
824
|
next
|
|
782
825
|
when OperationTimeoutError
|
|
783
826
|
# request level timeouts should take precedence
|
|
@@ -820,7 +863,7 @@ module HTTPX
|
|
|
820
863
|
def handle_transition(nextstate)
|
|
821
864
|
case nextstate
|
|
822
865
|
when :idle
|
|
823
|
-
@timeout = @current_timeout = @options.timeout
|
|
866
|
+
@timeout = @current_timeout = @options.timeout.connect_timeout
|
|
824
867
|
|
|
825
868
|
@connected_at = @response_received_at = nil
|
|
826
869
|
when :open
|
|
@@ -858,13 +901,15 @@ module HTTPX
|
|
|
858
901
|
nextstate = :open
|
|
859
902
|
# the first check for given io readiness must still use a timeout.
|
|
860
903
|
# connect is the reasonable choice in such a case.
|
|
861
|
-
@timeout = @options.timeout
|
|
904
|
+
@timeout = @options.timeout.connect_timeout
|
|
862
905
|
send_pending
|
|
863
906
|
when :active
|
|
864
907
|
return unless @state == :inactive
|
|
865
908
|
|
|
866
909
|
nextstate = :open
|
|
867
910
|
|
|
911
|
+
@timeout = @current_timeout = parser.timeout
|
|
912
|
+
|
|
868
913
|
# activate
|
|
869
914
|
@current_session.select_connection(self, @current_selector)
|
|
870
915
|
end
|
|
@@ -902,6 +947,13 @@ module HTTPX
|
|
|
902
947
|
end
|
|
903
948
|
end
|
|
904
949
|
|
|
950
|
+
def reset_ping_timer
|
|
951
|
+
return unless @ping_timer
|
|
952
|
+
|
|
953
|
+
@ping_timer.cancel
|
|
954
|
+
@ping_timer = nil
|
|
955
|
+
end
|
|
956
|
+
|
|
905
957
|
def close_sibling
|
|
906
958
|
sibling = @sibling
|
|
907
959
|
|
|
@@ -949,8 +1001,6 @@ module HTTPX
|
|
|
949
1001
|
|
|
950
1002
|
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
|
951
1003
|
def build_altsvc_connection(alt_origin, origin, alt_params)
|
|
952
|
-
return if @altsvc_connection
|
|
953
|
-
|
|
954
1004
|
# do not allow security downgrades on altsvc negotiation
|
|
955
1005
|
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
|
956
1006
|
|
|
@@ -959,6 +1009,8 @@ module HTTPX
|
|
|
959
1009
|
# altsvc already exists, somehow it wasn't advertised, probably noop
|
|
960
1010
|
return unless altsvc
|
|
961
1011
|
|
|
1012
|
+
return if @altsvc_connection
|
|
1013
|
+
|
|
962
1014
|
alt_options = @options.merge(ssl: @options.ssl.merge(hostname: URI(origin).host))
|
|
963
1015
|
|
|
964
1016
|
connection = @current_session.find_connection(alt_origin, @current_selector, alt_options)
|
|
@@ -1009,7 +1061,7 @@ module HTTPX
|
|
|
1009
1061
|
|
|
1010
1062
|
return unless parser.waiting_for_ping?
|
|
1011
1063
|
|
|
1012
|
-
ping_timeout = @options.timeout
|
|
1064
|
+
ping_timeout = @options.timeout.ping_timeout
|
|
1013
1065
|
|
|
1014
1066
|
@ping_timer = @current_selector.after(ping_timeout) do
|
|
1015
1067
|
log(level: 3) { "ping timeout expired..." }
|
|
@@ -1021,22 +1073,67 @@ module HTTPX
|
|
|
1021
1073
|
end
|
|
1022
1074
|
|
|
1023
1075
|
def pong
|
|
1024
|
-
|
|
1025
|
-
@ping_timer = nil
|
|
1076
|
+
reset_ping_timer
|
|
1026
1077
|
@response_received_at = Utils.now
|
|
1027
1078
|
@no_more_requests_counter = 0
|
|
1028
1079
|
send_pending
|
|
1029
1080
|
end
|
|
1030
1081
|
|
|
1082
|
+
def no_more_selector_check
|
|
1083
|
+
return if @current_selector
|
|
1084
|
+
|
|
1085
|
+
raise Error, "request has been resend to an out-of-session connection, and this " \
|
|
1086
|
+
"should never happen!!! Please report this error! " \
|
|
1087
|
+
"(state:#{@state}, " \
|
|
1088
|
+
"parser?:#{!!@parser}, " \
|
|
1089
|
+
"bytes in write buffer?:#{!@write_buffer.empty?}, " \
|
|
1090
|
+
"cloned?:#{@cloned}, " \
|
|
1091
|
+
"sibling?:#{!!@sibling}, " \
|
|
1092
|
+
"coalesced?:#{coalesced?})"
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1031
1095
|
def no_more_requests_loop_check
|
|
1032
1096
|
log(level: 3) { "NO MORE REQUESTS..." }
|
|
1033
1097
|
@no_more_requests_counter += 1
|
|
1034
1098
|
|
|
1035
1099
|
return if @no_more_requests_counter < 50
|
|
1036
1100
|
|
|
1101
|
+
per_request_state = @parser.pending.map do |req|
|
|
1102
|
+
"[state:#{req.state},ping?:#{req.ping?}]"
|
|
1103
|
+
end.join(",")
|
|
1104
|
+
|
|
1105
|
+
parser_state =
|
|
1106
|
+
case parser
|
|
1107
|
+
when HTTP2
|
|
1108
|
+
parser_pending_requests = parser.pending.map do |r|
|
|
1109
|
+
num_retries = r.respond_to(:retries) ? r.retries : nil
|
|
1110
|
+
req_conn = r.instance_variable_get(:@connection)
|
|
1111
|
+
"id:#{r.object_id},num_retries:#{num_retries}," \
|
|
1112
|
+
"is-same-conn:#{req_conn.equal?(self)},started?:#{r.started?}"
|
|
1113
|
+
end.join(",")
|
|
1114
|
+
+"parser-streams:#{parser.streams.size}," \
|
|
1115
|
+
"parser-handshake-completed:#{parser.instance_variable_get(:@handshake_completed)}," \
|
|
1116
|
+
"parser-wait-for-handshake:#{parser.instance_variable_get(:@wait_for_handshake)}," \
|
|
1117
|
+
"parser-pending:[#{parser_pending_requests}],"
|
|
1118
|
+
else
|
|
1119
|
+
+""
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
parser_state << "parser-requests:#{per_request_state}"
|
|
1123
|
+
|
|
1037
1124
|
raise Error, "connection corrupted, aborted after looping for a while, " \
|
|
1038
1125
|
"please report this https://gitlab.com/os85/httpx/-/work_items " \
|
|
1039
|
-
"along with debug logs"
|
|
1126
|
+
"along with debug logs\n(" \
|
|
1127
|
+
"state:#{@state},inflight:#{@inflight},pending:#{@pending.size}," \
|
|
1128
|
+
"exhausted:#{@exhausted}," \
|
|
1129
|
+
"previously-exhausted-with-error:#{@previously_exhausted_with_error}," \
|
|
1130
|
+
"connected_for_secs:#{Utils.elapsed_time(@connected_at) if @connected_at}," \
|
|
1131
|
+
"num-origins:#{@origins.size}," \
|
|
1132
|
+
"last_response_received:#{Utils.elapsed_time(@response_received_at) if @response_received_at}," \
|
|
1133
|
+
"#{parser_state}," \
|
|
1134
|
+
"in-selector:#{!!@current_selector}," \
|
|
1135
|
+
"in-session:#{!!@current_session}" \
|
|
1136
|
+
")"
|
|
1040
1137
|
end
|
|
1041
1138
|
|
|
1042
1139
|
# true when there are no more pending nor inflight (in parser) requests
|
|
@@ -1074,13 +1171,17 @@ module HTTPX
|
|
|
1074
1171
|
end
|
|
1075
1172
|
|
|
1076
1173
|
def set_request_timeouts(request)
|
|
1077
|
-
request.connection = self
|
|
1078
1174
|
set_request_write_timeout(request)
|
|
1079
1175
|
set_request_read_timeout(request)
|
|
1080
1176
|
set_request_request_timeout(request)
|
|
1081
1177
|
set_request_total_request_timeout(request)
|
|
1082
1178
|
end
|
|
1083
1179
|
|
|
1180
|
+
def set_connection_timeouts(request)
|
|
1181
|
+
set_connecting_request_timeout(request)
|
|
1182
|
+
set_connecting_total_request_timeout(request)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1084
1185
|
def set_request_read_timeout(request)
|
|
1085
1186
|
read_timeout = request.read_timeout
|
|
1086
1187
|
|
|
@@ -1144,19 +1245,52 @@ module HTTPX
|
|
|
1144
1245
|
end
|
|
1145
1246
|
|
|
1146
1247
|
def set_request_timeout(label, request, timeout, start_event, finish_events, &callback)
|
|
1248
|
+
return if request.active_timeouts.any? { |timer| timer.label == label }
|
|
1249
|
+
|
|
1147
1250
|
request.set_timeout_callback(start_event) do
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1251
|
+
no_more_selector_check
|
|
1252
|
+
|
|
1253
|
+
timer = @current_selector.after(timeout, callback)
|
|
1254
|
+
timer.label = label
|
|
1255
|
+
request.active_timeouts << timer
|
|
1256
|
+
|
|
1257
|
+
Array(finish_events).each do |event|
|
|
1258
|
+
# clean up request timeouts if the connection errors out
|
|
1259
|
+
request.set_timeout_callback(event) do
|
|
1260
|
+
timer.cancel
|
|
1261
|
+
request.active_timeouts.delete(timer)
|
|
1262
|
+
end
|
|
1157
1263
|
end
|
|
1264
|
+
end
|
|
1265
|
+
end
|
|
1158
1266
|
|
|
1159
|
-
|
|
1267
|
+
def set_connecting_request_timeout(request)
|
|
1268
|
+
request_timeout = request.request_timeout
|
|
1269
|
+
|
|
1270
|
+
return if request_timeout.nil? || request_timeout.infinite?
|
|
1271
|
+
|
|
1272
|
+
set_connection_timeout(:request_timeout, request, request_timeout, :connecting, :complete) do
|
|
1273
|
+
read_timeout_callback(request, request_timeout, RequestTimeoutError)
|
|
1274
|
+
end
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
def set_connecting_total_request_timeout(request)
|
|
1278
|
+
return if request.started?
|
|
1279
|
+
|
|
1280
|
+
total_request_timeout = request.total_request_timeout
|
|
1281
|
+
|
|
1282
|
+
return if total_request_timeout.nil? || total_request_timeout.infinite?
|
|
1283
|
+
|
|
1284
|
+
set_connection_timeout(:total_request_timeout, request, total_request_timeout, :connecting, :complete) do
|
|
1285
|
+
read_timeout_callback(request, total_request_timeout, TotalRequestTimeoutError)
|
|
1286
|
+
end
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
def set_connection_timeout(label, request, timeout, start_event, finish_events, &callback)
|
|
1290
|
+
cb = once(start_event) do
|
|
1291
|
+
no_more_selector_check
|
|
1292
|
+
|
|
1293
|
+
timer = @current_selector.after(timeout, callback)
|
|
1160
1294
|
timer.label = label
|
|
1161
1295
|
request.active_timeouts << timer
|
|
1162
1296
|
|
|
@@ -1168,6 +1302,10 @@ module HTTPX
|
|
|
1168
1302
|
end
|
|
1169
1303
|
end
|
|
1170
1304
|
end
|
|
1305
|
+
|
|
1306
|
+
request.once(:idle) do
|
|
1307
|
+
callbacks(start_event).delete(cb)
|
|
1308
|
+
end
|
|
1171
1309
|
end
|
|
1172
1310
|
|
|
1173
1311
|
def parser_type(protocol)
|
data/lib/httpx/headers.rb
CHANGED
|
@@ -159,8 +159,8 @@ module HTTPX
|
|
|
159
159
|
# This method is more internal, and for this reason doesn't try
|
|
160
160
|
# to "correct" the user input, i.e. it doesn't downcase the key.
|
|
161
161
|
#
|
|
162
|
-
def get(
|
|
163
|
-
@headers[
|
|
162
|
+
def get(downcased_key)
|
|
163
|
+
@headers[downcased_key] || EMPTY
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
private
|
data/lib/httpx/io/tcp.rb
CHANGED
|
@@ -132,7 +132,7 @@ module HTTPX
|
|
|
132
132
|
rescue Errno::ETIMEDOUT => e
|
|
133
133
|
@ip_index -= 1
|
|
134
134
|
|
|
135
|
-
raise ConnectTimeoutError.new(@options.timeout
|
|
135
|
+
raise ConnectTimeoutError.new(@options.timeout.connect_timeout, e.message) if @ip_index.negative?
|
|
136
136
|
|
|
137
137
|
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
|
138
138
|
|
|
@@ -159,26 +159,29 @@ module HTTPX
|
|
|
159
159
|
private :try_connect
|
|
160
160
|
|
|
161
161
|
def read(size, buffer)
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
siz = @io.read_nonblock(size, buffer, exception: false)
|
|
163
|
+
|
|
164
|
+
case siz
|
|
165
|
+
when String
|
|
166
|
+
log { "READ: #{buffer.bytesize} bytes..." }
|
|
167
|
+
buffer.bytesize
|
|
168
|
+
when :wait_readable, :wait_writable
|
|
164
169
|
buffer.clear
|
|
165
|
-
|
|
170
|
+
0
|
|
166
171
|
end
|
|
167
|
-
return if ret.nil?
|
|
168
|
-
|
|
169
|
-
log { "READ: #{buffer.bytesize} bytes..." }
|
|
170
|
-
buffer.bytesize
|
|
171
172
|
end
|
|
172
173
|
|
|
173
174
|
def write(buffer)
|
|
174
175
|
siz = @io.write_nonblock(buffer, exception: false)
|
|
175
|
-
return 0 if siz == :wait_writable
|
|
176
|
-
return if siz.nil?
|
|
177
176
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
case siz
|
|
178
|
+
when Integer
|
|
179
|
+
log { "WRITE: #{siz} bytes..." }
|
|
180
|
+
buffer.shift!(siz)
|
|
181
|
+
siz
|
|
182
|
+
when :wait_readable, :wait_writable
|
|
183
|
+
0
|
|
184
|
+
end
|
|
182
185
|
end
|
|
183
186
|
|
|
184
187
|
def close
|
|
@@ -255,6 +258,7 @@ module HTTPX
|
|
|
255
258
|
|
|
256
259
|
def log_transition_state(nextstate)
|
|
257
260
|
label = host
|
|
261
|
+
label = "#{label}:#{@port}" if label
|
|
258
262
|
label = "#{label}(##{@io.fileno})" if nextstate == :connected
|
|
259
263
|
"#{label} #{@state} -> #{nextstate}"
|
|
260
264
|
end
|