httpx 1.8.4 → 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_9_0.md +75 -0
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/altsvc.rb +29 -15
- data/lib/httpx/chainable.rb +4 -1
- data/lib/httpx/connection/http1.rb +11 -19
- data/lib/httpx/connection/http2.rb +48 -14
- data/lib/httpx/connection.rb +171 -47
- 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/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 +1 -1
- 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 +44 -10
- data/lib/httpx/plugins/proxy/ssh.rb +30 -11
- data/lib/httpx/plugins/proxy.rb +62 -41
- data/lib/httpx/plugins/push_promise.rb +4 -2
- data/lib/httpx/plugins/retries.rb +2 -2
- data/lib/httpx/plugins/server_sent_events.rb +1 -0
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/tracing.rb +58 -11
- 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 +5 -5
- data/lib/httpx/resolver.rb +5 -1
- data/lib/httpx/response.rb +15 -7
- data/lib/httpx/selector.rb +3 -3
- data/lib/httpx/session.rb +36 -10
- 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 +15 -15
- data/sig/connection.rbs +18 -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 +6 -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/resolver/entry.rbs +2 -2
- data/sig/response.rbs +1 -2
- data/sig/selector.rbs +2 -2
- data/sig/session.rbs +7 -5
- metadata +5 -1
data/lib/httpx/connection.rb
CHANGED
|
@@ -52,9 +52,10 @@ module HTTPX
|
|
|
52
52
|
@io = @ssl_session = @timeout = @connected_at = @response_received_at = nil
|
|
53
53
|
|
|
54
54
|
@exhausted = @cloned = @main_sibling =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
58
59
|
|
|
59
60
|
@options = Options.new(options)
|
|
60
61
|
@type = initialize_type(uri, @options)
|
|
@@ -65,7 +66,7 @@ module HTTPX
|
|
|
65
66
|
@write_buffer = Buffer.new(@options.buffer_size)
|
|
66
67
|
@pending = []
|
|
67
68
|
@inflight = 0
|
|
68
|
-
@keep_alive_timeout = @options.timeout
|
|
69
|
+
@keep_alive_timeout = @options.timeout.keep_alive_timeout
|
|
69
70
|
@no_more_requests_counter = 0
|
|
70
71
|
|
|
71
72
|
if @options.io
|
|
@@ -77,7 +78,14 @@ module HTTPX
|
|
|
77
78
|
else
|
|
78
79
|
transition(:idle)
|
|
79
80
|
end
|
|
80
|
-
|
|
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
|
|
81
89
|
end
|
|
82
90
|
|
|
83
91
|
# dupped initialization
|
|
@@ -187,6 +195,14 @@ module HTTPX
|
|
|
187
195
|
end
|
|
188
196
|
end
|
|
189
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
|
+
|
|
190
206
|
def purge_pending(&block)
|
|
191
207
|
if @parser
|
|
192
208
|
pending = @parser.pending
|
|
@@ -224,14 +240,12 @@ module HTTPX
|
|
|
224
240
|
def interests
|
|
225
241
|
# connecting
|
|
226
242
|
if connecting?
|
|
227
|
-
|
|
243
|
+
try_connect
|
|
228
244
|
|
|
229
245
|
return @io.interests if connecting?
|
|
230
246
|
end
|
|
231
247
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
nil
|
|
248
|
+
@parser&.interests
|
|
235
249
|
rescue Error => e
|
|
236
250
|
on_error(e)
|
|
237
251
|
nil
|
|
@@ -242,11 +256,16 @@ module HTTPX
|
|
|
242
256
|
end
|
|
243
257
|
|
|
244
258
|
def call
|
|
259
|
+
if @reset_timeout_on_call
|
|
260
|
+
@timeout = nil
|
|
261
|
+
@reset_timeout_on_call = false
|
|
262
|
+
end
|
|
263
|
+
|
|
245
264
|
case @state
|
|
246
265
|
when :idle
|
|
247
266
|
return if no_more_requests?
|
|
248
267
|
|
|
249
|
-
|
|
268
|
+
try_connect
|
|
250
269
|
|
|
251
270
|
# when opening the tcp or ssl socket fails
|
|
252
271
|
return if @state == :closed
|
|
@@ -300,7 +319,7 @@ module HTTPX
|
|
|
300
319
|
def close
|
|
301
320
|
transition(:active) if @state == :inactive
|
|
302
321
|
|
|
303
|
-
@parser
|
|
322
|
+
@parser&.close
|
|
304
323
|
end
|
|
305
324
|
|
|
306
325
|
def terminate
|
|
@@ -330,10 +349,9 @@ module HTTPX
|
|
|
330
349
|
@pending.clear
|
|
331
350
|
elsif (parser = @parser)
|
|
332
351
|
enqueue_pending_requests_from_parser(parser)
|
|
352
|
+
return unless @pending.empty?
|
|
333
353
|
end
|
|
334
354
|
|
|
335
|
-
return unless @pending.empty?
|
|
336
|
-
|
|
337
355
|
disconnect
|
|
338
356
|
emit(:force_closed, delete_pending)
|
|
339
357
|
end
|
|
@@ -353,10 +371,7 @@ module HTTPX
|
|
|
353
371
|
# (example: HTTP/1 parser disabling pipelining)
|
|
354
372
|
return if @state == :idle && @pending.any?
|
|
355
373
|
|
|
356
|
-
|
|
357
|
-
@ping_timer.cancel
|
|
358
|
-
@ping_timer = nil
|
|
359
|
-
end
|
|
374
|
+
reset_ping_timer
|
|
360
375
|
|
|
361
376
|
parser = @parser
|
|
362
377
|
|
|
@@ -374,6 +389,8 @@ module HTTPX
|
|
|
374
389
|
def send(request)
|
|
375
390
|
return @coalesced_connection.send(request) if @coalesced_connection
|
|
376
391
|
|
|
392
|
+
request.connection = self
|
|
393
|
+
|
|
377
394
|
if @parser && !@write_buffer.full?
|
|
378
395
|
if @response_received_at && @keep_alive_timeout &&
|
|
379
396
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
|
@@ -390,6 +407,8 @@ module HTTPX
|
|
|
390
407
|
|
|
391
408
|
send_request_to_parser(request)
|
|
392
409
|
else
|
|
410
|
+
set_connection_timeouts(request) if connecting?
|
|
411
|
+
|
|
393
412
|
@pending << request
|
|
394
413
|
end
|
|
395
414
|
end
|
|
@@ -399,9 +418,7 @@ module HTTPX
|
|
|
399
418
|
|
|
400
419
|
return @timeout if @timeout
|
|
401
420
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
@options.timeout[:operation_timeout]
|
|
421
|
+
@options.timeout.operation_timeout
|
|
405
422
|
end
|
|
406
423
|
|
|
407
424
|
def idling
|
|
@@ -413,7 +430,7 @@ module HTTPX
|
|
|
413
430
|
transition(:idle)
|
|
414
431
|
return unless @parser
|
|
415
432
|
|
|
416
|
-
enqueue_pending_requests_from_parser(parser)
|
|
433
|
+
enqueue_pending_requests_from_parser(@parser)
|
|
417
434
|
@parser = nil
|
|
418
435
|
end
|
|
419
436
|
|
|
@@ -486,16 +503,20 @@ module HTTPX
|
|
|
486
503
|
|
|
487
504
|
def on_error(error, request = nil)
|
|
488
505
|
if error.is_a?(OperationTimeoutError)
|
|
489
|
-
|
|
490
506
|
# inactive connections do not contribute to the select loop, therefore
|
|
491
507
|
# they should not fail due to such errors.
|
|
492
508
|
return if @state == :inactive
|
|
493
509
|
|
|
494
|
-
if @timeout
|
|
495
|
-
@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
|
|
496
516
|
return unless @timeout <= 0
|
|
497
517
|
|
|
498
518
|
@timeout = nil
|
|
519
|
+
@reset_timeout_on_call = false
|
|
499
520
|
end
|
|
500
521
|
|
|
501
522
|
error = error.to_connection_error if connecting?
|
|
@@ -516,6 +537,11 @@ module HTTPX
|
|
|
516
537
|
|
|
517
538
|
private
|
|
518
539
|
|
|
540
|
+
def try_connect
|
|
541
|
+
emit(:connecting)
|
|
542
|
+
connect
|
|
543
|
+
end
|
|
544
|
+
|
|
519
545
|
def connect
|
|
520
546
|
transition(:open)
|
|
521
547
|
end
|
|
@@ -739,6 +765,9 @@ module HTTPX
|
|
|
739
765
|
on_error(error)
|
|
740
766
|
next
|
|
741
767
|
else
|
|
768
|
+
# in case the error came while waiting for a PING ack.
|
|
769
|
+
reset_ping_timer
|
|
770
|
+
|
|
742
771
|
@previously_exhausted_with_error = true
|
|
743
772
|
end
|
|
744
773
|
end
|
|
@@ -787,11 +816,11 @@ module HTTPX
|
|
|
787
816
|
current_selector = @current_selector
|
|
788
817
|
parser.close
|
|
789
818
|
|
|
790
|
-
other_connection = current_session.find_connection(@origin, current_selector,
|
|
791
|
-
@options.merge(ssl: { alpn_protocols: %w[http/1.1] }))
|
|
792
|
-
other_connection.merge(self)
|
|
793
819
|
request.transition(:idle)
|
|
794
|
-
|
|
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
|
|
795
824
|
next
|
|
796
825
|
when OperationTimeoutError
|
|
797
826
|
# request level timeouts should take precedence
|
|
@@ -834,7 +863,7 @@ module HTTPX
|
|
|
834
863
|
def handle_transition(nextstate)
|
|
835
864
|
case nextstate
|
|
836
865
|
when :idle
|
|
837
|
-
@timeout = @current_timeout = @options.timeout
|
|
866
|
+
@timeout = @current_timeout = @options.timeout.connect_timeout
|
|
838
867
|
|
|
839
868
|
@connected_at = @response_received_at = nil
|
|
840
869
|
when :open
|
|
@@ -872,13 +901,15 @@ module HTTPX
|
|
|
872
901
|
nextstate = :open
|
|
873
902
|
# the first check for given io readiness must still use a timeout.
|
|
874
903
|
# connect is the reasonable choice in such a case.
|
|
875
|
-
@timeout = @options.timeout
|
|
904
|
+
@timeout = @options.timeout.connect_timeout
|
|
876
905
|
send_pending
|
|
877
906
|
when :active
|
|
878
907
|
return unless @state == :inactive
|
|
879
908
|
|
|
880
909
|
nextstate = :open
|
|
881
910
|
|
|
911
|
+
@timeout = @current_timeout = parser.timeout
|
|
912
|
+
|
|
882
913
|
# activate
|
|
883
914
|
@current_session.select_connection(self, @current_selector)
|
|
884
915
|
end
|
|
@@ -916,6 +947,13 @@ module HTTPX
|
|
|
916
947
|
end
|
|
917
948
|
end
|
|
918
949
|
|
|
950
|
+
def reset_ping_timer
|
|
951
|
+
return unless @ping_timer
|
|
952
|
+
|
|
953
|
+
@ping_timer.cancel
|
|
954
|
+
@ping_timer = nil
|
|
955
|
+
end
|
|
956
|
+
|
|
919
957
|
def close_sibling
|
|
920
958
|
sibling = @sibling
|
|
921
959
|
|
|
@@ -963,8 +1001,6 @@ module HTTPX
|
|
|
963
1001
|
|
|
964
1002
|
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
|
965
1003
|
def build_altsvc_connection(alt_origin, origin, alt_params)
|
|
966
|
-
return if @altsvc_connection
|
|
967
|
-
|
|
968
1004
|
# do not allow security downgrades on altsvc negotiation
|
|
969
1005
|
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
|
970
1006
|
|
|
@@ -973,6 +1009,8 @@ module HTTPX
|
|
|
973
1009
|
# altsvc already exists, somehow it wasn't advertised, probably noop
|
|
974
1010
|
return unless altsvc
|
|
975
1011
|
|
|
1012
|
+
return if @altsvc_connection
|
|
1013
|
+
|
|
976
1014
|
alt_options = @options.merge(ssl: @options.ssl.merge(hostname: URI(origin).host))
|
|
977
1015
|
|
|
978
1016
|
connection = @current_session.find_connection(alt_origin, @current_selector, alt_options)
|
|
@@ -1023,7 +1061,7 @@ module HTTPX
|
|
|
1023
1061
|
|
|
1024
1062
|
return unless parser.waiting_for_ping?
|
|
1025
1063
|
|
|
1026
|
-
ping_timeout = @options.timeout
|
|
1064
|
+
ping_timeout = @options.timeout.ping_timeout
|
|
1027
1065
|
|
|
1028
1066
|
@ping_timer = @current_selector.after(ping_timeout) do
|
|
1029
1067
|
log(level: 3) { "ping timeout expired..." }
|
|
@@ -1035,22 +1073,67 @@ module HTTPX
|
|
|
1035
1073
|
end
|
|
1036
1074
|
|
|
1037
1075
|
def pong
|
|
1038
|
-
|
|
1039
|
-
@ping_timer = nil
|
|
1076
|
+
reset_ping_timer
|
|
1040
1077
|
@response_received_at = Utils.now
|
|
1041
1078
|
@no_more_requests_counter = 0
|
|
1042
1079
|
send_pending
|
|
1043
1080
|
end
|
|
1044
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
|
+
|
|
1045
1095
|
def no_more_requests_loop_check
|
|
1046
1096
|
log(level: 3) { "NO MORE REQUESTS..." }
|
|
1047
1097
|
@no_more_requests_counter += 1
|
|
1048
1098
|
|
|
1049
1099
|
return if @no_more_requests_counter < 50
|
|
1050
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
|
+
|
|
1051
1124
|
raise Error, "connection corrupted, aborted after looping for a while, " \
|
|
1052
1125
|
"please report this https://gitlab.com/os85/httpx/-/work_items " \
|
|
1053
|
-
"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
|
+
")"
|
|
1054
1137
|
end
|
|
1055
1138
|
|
|
1056
1139
|
# true when there are no more pending nor inflight (in parser) requests
|
|
@@ -1088,13 +1171,17 @@ module HTTPX
|
|
|
1088
1171
|
end
|
|
1089
1172
|
|
|
1090
1173
|
def set_request_timeouts(request)
|
|
1091
|
-
request.connection = self
|
|
1092
1174
|
set_request_write_timeout(request)
|
|
1093
1175
|
set_request_read_timeout(request)
|
|
1094
1176
|
set_request_request_timeout(request)
|
|
1095
1177
|
set_request_total_request_timeout(request)
|
|
1096
1178
|
end
|
|
1097
1179
|
|
|
1180
|
+
def set_connection_timeouts(request)
|
|
1181
|
+
set_connecting_request_timeout(request)
|
|
1182
|
+
set_connecting_total_request_timeout(request)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1098
1185
|
def set_request_read_timeout(request)
|
|
1099
1186
|
read_timeout = request.read_timeout
|
|
1100
1187
|
|
|
@@ -1158,19 +1245,52 @@ module HTTPX
|
|
|
1158
1245
|
end
|
|
1159
1246
|
|
|
1160
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
|
+
|
|
1161
1250
|
request.set_timeout_callback(start_event) do
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
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
|
|
1171
1263
|
end
|
|
1264
|
+
end
|
|
1265
|
+
end
|
|
1266
|
+
|
|
1267
|
+
def set_connecting_request_timeout(request)
|
|
1268
|
+
request_timeout = request.request_timeout
|
|
1172
1269
|
|
|
1173
|
-
|
|
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)
|
|
1174
1294
|
timer.label = label
|
|
1175
1295
|
request.active_timeouts << timer
|
|
1176
1296
|
|
|
@@ -1182,6 +1302,10 @@ module HTTPX
|
|
|
1182
1302
|
end
|
|
1183
1303
|
end
|
|
1184
1304
|
end
|
|
1305
|
+
|
|
1306
|
+
request.once(:idle) do
|
|
1307
|
+
callbacks(start_event).delete(cb)
|
|
1308
|
+
end
|
|
1185
1309
|
end
|
|
1186
1310
|
|
|
1187
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
|