httpx 1.2.6 → 1.4.4
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/README.md +1 -2
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/doc/release_notes/1_3_2.md +6 -0
- data/doc/release_notes/1_3_3.md +5 -0
- data/doc/release_notes/1_3_4.md +6 -0
- data/doc/release_notes/1_4_0.md +43 -0
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/doc/release_notes/1_4_3.md +11 -0
- data/doc/release_notes/1_4_4.md +14 -0
- data/lib/httpx/adapters/datadog.rb +56 -80
- data/lib/httpx/adapters/faraday.rb +5 -2
- data/lib/httpx/adapters/webmock.rb +24 -8
- data/lib/httpx/callbacks.rb +2 -7
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http1.rb +11 -7
- data/lib/httpx/connection/http2.rb +57 -34
- data/lib/httpx/connection.rb +270 -71
- data/lib/httpx/errors.rb +15 -4
- data/lib/httpx/io/ssl.rb +6 -3
- data/lib/httpx/io/tcp.rb +1 -1
- data/lib/httpx/io/unix.rb +1 -1
- data/lib/httpx/loggable.rb +17 -10
- data/lib/httpx/options.rb +30 -23
- data/lib/httpx/plugins/aws_sdk_authentication.rb +3 -0
- data/lib/httpx/plugins/aws_sigv4.rb +36 -17
- data/lib/httpx/plugins/callbacks.rb +13 -2
- data/lib/httpx/plugins/circuit_breaker.rb +11 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/cookies.rb +9 -6
- data/lib/httpx/plugins/digest_auth.rb +3 -0
- data/lib/httpx/plugins/expect.rb +10 -4
- data/lib/httpx/plugins/follow_redirects.rb +68 -33
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +48 -1
- data/lib/httpx/plugins/oauth.rb +1 -1
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +19 -16
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +96 -85
- data/lib/httpx/plugins/retries.rb +28 -10
- data/lib/httpx/plugins/ssrf_filter.rb +4 -1
- data/lib/httpx/plugins/stream.rb +42 -18
- data/lib/httpx/plugins/upgrade.rb +5 -10
- data/lib/httpx/plugins/webdav.rb +6 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pool.rb +73 -244
- data/lib/httpx/request/body.rb +50 -55
- data/lib/httpx/request.rb +77 -14
- data/lib/httpx/resolver/https.rb +17 -20
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +140 -61
- data/lib/httpx/resolver/resolver.rb +64 -19
- data/lib/httpx/resolver/system.rb +32 -16
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response/body.rb +12 -1
- data/lib/httpx/response.rb +16 -9
- data/lib/httpx/selector.rb +170 -91
- data/lib/httpx/session.rb +282 -139
- data/lib/httpx/timers.rb +17 -2
- data/lib/httpx/transcoder/body.rb +15 -29
- data/lib/httpx/transcoder/form.rb +2 -0
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +16 -2
- data/lib/httpx/transcoder/multipart/encoder.rb +11 -2
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +20 -21
- data/sig/callbacks.rbs +2 -3
- data/sig/chainable.rbs +6 -2
- data/sig/connection/http1.rbs +2 -2
- data/sig/connection/http2.rbs +22 -18
- data/sig/connection.rbs +40 -9
- data/sig/errors.rbs +9 -3
- data/sig/httpx.rbs +3 -3
- data/sig/io/tcp.rbs +1 -1
- data/sig/io/unix.rbs +1 -1
- data/sig/loggable.rbs +4 -2
- data/sig/options.rbs +8 -13
- data/sig/plugins/aws_sigv4.rbs +8 -2
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +9 -0
- data/sig/plugins/follow_redirects.rbs +1 -1
- data/sig/plugins/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -9
- data/sig/plugins/push_promise.rbs +6 -3
- data/sig/plugins/rate_limiter.rbs +2 -0
- data/sig/plugins/retries.rbs +1 -1
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/stream.rbs +3 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +27 -33
- data/sig/request/body.rbs +4 -10
- data/sig/request.rbs +14 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +6 -3
- data/sig/resolver/resolver.rbs +22 -3
- data/sig/resolver.rbs +5 -1
- data/sig/response/body.rbs +2 -2
- data/sig/response/buffer.rbs +2 -2
- data/sig/response.rbs +9 -4
- data/sig/selector.rbs +31 -4
- data/sig/session.rbs +54 -20
- data/sig/timers.rbs +15 -4
- data/sig/transcoder/body.rbs +2 -4
- data/sig/transcoder/chunker.rbs +1 -1
- data/sig/transcoder/deflate.rbs +1 -0
- data/sig/transcoder/form.rbs +8 -0
- data/sig/transcoder/gzip.rbs +4 -1
- data/sig/transcoder/json.rbs +1 -1
- data/sig/transcoder/multipart.rbs +6 -4
- data/sig/transcoder/utils/body_reader.rbs +3 -3
- data/sig/transcoder/utils/deflater.rbs +2 -3
- metadata +32 -14
- data/lib/httpx/session2.rb +0 -23
- data/lib/httpx/transcoder/utils/inflater.rb +0 -19
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/utils/inflater.rbs +0 -12
- data/sig/transcoder/xml.rbs +0 -22
data/lib/httpx/connection.rb
CHANGED
@@ -41,13 +41,18 @@ module HTTPX
|
|
41
41
|
|
42
42
|
def_delegator :@write_buffer, :empty?
|
43
43
|
|
44
|
-
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session
|
44
|
+
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session, :sibling
|
45
45
|
|
46
|
-
attr_writer :
|
46
|
+
attr_writer :current_selector
|
47
47
|
|
48
|
-
attr_accessor :family
|
48
|
+
attr_accessor :current_session, :family
|
49
|
+
|
50
|
+
protected :sibling
|
49
51
|
|
50
52
|
def initialize(uri, options)
|
53
|
+
@current_session = @current_selector = @sibling = @coalesced_connection = nil
|
54
|
+
@exhausted = @cloned = @main_sibling = false
|
55
|
+
|
51
56
|
@options = Options.new(options)
|
52
57
|
@type = initialize_type(uri, @options)
|
53
58
|
@origins = [uri.origin]
|
@@ -56,6 +61,7 @@ module HTTPX
|
|
56
61
|
@read_buffer = Buffer.new(@options.buffer_size)
|
57
62
|
@write_buffer = Buffer.new(@options.buffer_size)
|
58
63
|
@pending = []
|
64
|
+
|
59
65
|
on(:error, &method(:on_error))
|
60
66
|
if @options.io
|
61
67
|
# if there's an already open IO, get its
|
@@ -66,15 +72,42 @@ module HTTPX
|
|
66
72
|
else
|
67
73
|
transition(:idle)
|
68
74
|
end
|
75
|
+
on(:close) do
|
76
|
+
next if @exhausted # it'll reset
|
77
|
+
|
78
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
79
|
+
# next unless @current_session
|
80
|
+
|
81
|
+
next unless @current_session
|
82
|
+
|
83
|
+
@current_session.deselect_connection(self, @current_selector, @cloned)
|
84
|
+
end
|
85
|
+
on(:terminate) do
|
86
|
+
next if @exhausted # it'll reset
|
87
|
+
|
88
|
+
current_session = @current_session
|
89
|
+
current_selector = @current_selector
|
90
|
+
|
91
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
92
|
+
next unless current_session && current_selector
|
93
|
+
|
94
|
+
current_session.deselect_connection(self, current_selector)
|
95
|
+
end
|
96
|
+
|
97
|
+
on(:altsvc) do |alt_origin, origin, alt_params|
|
98
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
99
|
+
end
|
69
100
|
|
70
101
|
@inflight = 0
|
71
102
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
72
103
|
|
73
|
-
@intervals = []
|
74
|
-
|
75
104
|
self.addresses = @options.addresses if @options.addresses
|
76
105
|
end
|
77
106
|
|
107
|
+
def peer
|
108
|
+
@origin
|
109
|
+
end
|
110
|
+
|
78
111
|
# this is a semi-private method, to be used by the resolver
|
79
112
|
# to initiate the io object.
|
80
113
|
def addresses=(addrs)
|
@@ -161,12 +194,23 @@ module HTTPX
|
|
161
194
|
end
|
162
195
|
end
|
163
196
|
|
197
|
+
def io_connected?
|
198
|
+
return @coalesced_connection.io_connected? if @coalesced_connection
|
199
|
+
|
200
|
+
@io && @io.state == :connected
|
201
|
+
end
|
202
|
+
|
164
203
|
def connecting?
|
165
204
|
@state == :idle
|
166
205
|
end
|
167
206
|
|
168
207
|
def inflight?
|
169
|
-
@parser &&
|
208
|
+
@parser && (
|
209
|
+
# parser may be dealing with other requests (possibly started from a different fiber)
|
210
|
+
!@parser.empty? ||
|
211
|
+
# connection may be doing connection termination handshake
|
212
|
+
!@write_buffer.empty?
|
213
|
+
)
|
170
214
|
end
|
171
215
|
|
172
216
|
def interests
|
@@ -182,6 +226,9 @@ module HTTPX
|
|
182
226
|
|
183
227
|
return @parser.interests if @parser
|
184
228
|
|
229
|
+
nil
|
230
|
+
rescue StandardError => e
|
231
|
+
emit(:error, e)
|
185
232
|
nil
|
186
233
|
end
|
187
234
|
|
@@ -203,6 +250,9 @@ module HTTPX
|
|
203
250
|
consume
|
204
251
|
end
|
205
252
|
nil
|
253
|
+
rescue StandardError => e
|
254
|
+
emit(:error, e)
|
255
|
+
raise e
|
206
256
|
end
|
207
257
|
|
208
258
|
def close
|
@@ -219,8 +269,9 @@ module HTTPX
|
|
219
269
|
|
220
270
|
# bypasses the state machine to force closing of connections still connecting.
|
221
271
|
# **only** used for Happy Eyeballs v2.
|
222
|
-
def force_reset
|
272
|
+
def force_reset(cloned = false)
|
223
273
|
@state = :closing
|
274
|
+
@cloned = cloned
|
224
275
|
transition(:closed)
|
225
276
|
end
|
226
277
|
|
@@ -233,6 +284,8 @@ module HTTPX
|
|
233
284
|
end
|
234
285
|
|
235
286
|
def send(request)
|
287
|
+
return @coalesced_connection.send(request) if @coalesced_connection
|
288
|
+
|
236
289
|
if @parser && !@write_buffer.full?
|
237
290
|
if @response_received_at && @keep_alive_timeout &&
|
238
291
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
@@ -243,6 +296,7 @@ module HTTPX
|
|
243
296
|
@pending << request
|
244
297
|
transition(:active) if @state == :inactive
|
245
298
|
parser.ping
|
299
|
+
request.ping!
|
246
300
|
return
|
247
301
|
end
|
248
302
|
|
@@ -253,6 +307,8 @@ module HTTPX
|
|
253
307
|
end
|
254
308
|
|
255
309
|
def timeout
|
310
|
+
return if @state == :closed || @state == :inactive
|
311
|
+
|
256
312
|
return @timeout if @timeout
|
257
313
|
|
258
314
|
return @options.timeout[:connect_timeout] if @state == :idle
|
@@ -280,17 +336,46 @@ module HTTPX
|
|
280
336
|
end
|
281
337
|
|
282
338
|
def handle_socket_timeout(interval)
|
283
|
-
|
339
|
+
error = OperationTimeoutError.new(interval, "timed out while waiting on select")
|
340
|
+
error.set_backtrace(caller)
|
341
|
+
on_error(error)
|
342
|
+
end
|
284
343
|
|
285
|
-
|
286
|
-
|
344
|
+
def coalesced_connection=(connection)
|
345
|
+
@coalesced_connection = connection
|
287
346
|
|
288
|
-
|
289
|
-
|
347
|
+
close_sibling
|
348
|
+
connection.merge(self)
|
349
|
+
end
|
290
350
|
|
291
|
-
|
292
|
-
|
293
|
-
|
351
|
+
def sibling=(connection)
|
352
|
+
@sibling = connection
|
353
|
+
|
354
|
+
return unless connection
|
355
|
+
|
356
|
+
@main_sibling = connection.sibling.nil?
|
357
|
+
|
358
|
+
return unless @main_sibling
|
359
|
+
|
360
|
+
connection.sibling = self
|
361
|
+
end
|
362
|
+
|
363
|
+
def handle_connect_error(error)
|
364
|
+
@connect_error = error
|
365
|
+
|
366
|
+
return handle_error(error) unless @sibling && @sibling.connecting?
|
367
|
+
|
368
|
+
@sibling.merge(self)
|
369
|
+
|
370
|
+
force_reset(true)
|
371
|
+
end
|
372
|
+
|
373
|
+
def disconnect
|
374
|
+
return unless @current_session && @current_selector
|
375
|
+
|
376
|
+
emit(:close)
|
377
|
+
@current_session = nil
|
378
|
+
@current_selector = nil
|
294
379
|
end
|
295
380
|
|
296
381
|
private
|
@@ -337,8 +422,10 @@ module HTTPX
|
|
337
422
|
#
|
338
423
|
loop do
|
339
424
|
siz = @io.read(@window_size, @read_buffer)
|
340
|
-
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes..." }
|
425
|
+
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes... (wsize: #{@window_size}, rbuffer: #{@read_buffer.bytesize})" }
|
341
426
|
unless siz
|
427
|
+
@write_buffer.clear
|
428
|
+
|
342
429
|
ex = EOFError.new("descriptor closed")
|
343
430
|
ex.set_backtrace(caller)
|
344
431
|
on_error(ex)
|
@@ -393,6 +480,8 @@ module HTTPX
|
|
393
480
|
end
|
394
481
|
log(level: 3, color: :cyan) { "IO WRITE: #{siz} bytes..." }
|
395
482
|
unless siz
|
483
|
+
@write_buffer.clear
|
484
|
+
|
396
485
|
ex = EOFError.new("descriptor closed")
|
397
486
|
ex.set_backtrace(caller)
|
398
487
|
on_error(ex)
|
@@ -473,8 +562,27 @@ module HTTPX
|
|
473
562
|
request.emit(:promise, parser, stream)
|
474
563
|
end
|
475
564
|
parser.on(:exhausted) do
|
476
|
-
@
|
477
|
-
|
565
|
+
@exhausted = true
|
566
|
+
current_session = @current_session
|
567
|
+
current_selector = @current_selector
|
568
|
+
begin
|
569
|
+
parser.close
|
570
|
+
@pending.concat(parser.pending)
|
571
|
+
ensure
|
572
|
+
@current_session = current_session
|
573
|
+
@current_selector = current_selector
|
574
|
+
end
|
575
|
+
|
576
|
+
case @state
|
577
|
+
when :closed
|
578
|
+
idling
|
579
|
+
@exhausted = false
|
580
|
+
when :closing
|
581
|
+
once(:closed) do
|
582
|
+
idling
|
583
|
+
@exhausted = false
|
584
|
+
end
|
585
|
+
end
|
478
586
|
end
|
479
587
|
parser.on(:origin) do |origin|
|
480
588
|
@origins |= [origin]
|
@@ -490,8 +598,14 @@ module HTTPX
|
|
490
598
|
end
|
491
599
|
parser.on(:reset) do
|
492
600
|
@pending.concat(parser.pending) unless parser.empty?
|
601
|
+
current_session = @current_session
|
602
|
+
current_selector = @current_selector
|
493
603
|
reset
|
494
|
-
|
604
|
+
unless @pending.empty?
|
605
|
+
idling
|
606
|
+
@current_session = current_session
|
607
|
+
@current_selector = current_selector
|
608
|
+
end
|
495
609
|
end
|
496
610
|
parser.on(:current_timeout) do
|
497
611
|
@current_timeout = @timeout = parser.timeout
|
@@ -499,15 +613,28 @@ module HTTPX
|
|
499
613
|
parser.on(:timeout) do |tout|
|
500
614
|
@timeout = tout
|
501
615
|
end
|
502
|
-
parser.on(:error) do |request,
|
503
|
-
case
|
504
|
-
when
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
616
|
+
parser.on(:error) do |request, error|
|
617
|
+
case error
|
618
|
+
when :http_1_1_required
|
619
|
+
current_session = @current_session
|
620
|
+
current_selector = @current_selector
|
621
|
+
parser.close
|
622
|
+
|
623
|
+
other_connection = current_session.find_connection(@origin, current_selector,
|
624
|
+
@options.merge(ssl: { alpn_protocols: %w[http/1.1] }))
|
625
|
+
other_connection.merge(self)
|
626
|
+
request.transition(:idle)
|
627
|
+
other_connection.send(request)
|
628
|
+
next
|
629
|
+
when OperationTimeoutError
|
630
|
+
# request level timeouts should take precedence
|
631
|
+
next unless request.active_timeouts.empty?
|
510
632
|
end
|
633
|
+
|
634
|
+
@inflight -= 1
|
635
|
+
response = ErrorResponse.new(request, error)
|
636
|
+
request.response = response
|
637
|
+
request.emit(:response, response)
|
511
638
|
end
|
512
639
|
end
|
513
640
|
|
@@ -527,15 +654,17 @@ module HTTPX
|
|
527
654
|
# connect errors, exit gracefully
|
528
655
|
error = ConnectionError.new(e.message)
|
529
656
|
error.set_backtrace(e.backtrace)
|
530
|
-
|
657
|
+
handle_connect_error(error) if connecting?
|
531
658
|
@state = :closed
|
532
|
-
|
533
|
-
|
659
|
+
purge_after_closed
|
660
|
+
disconnect
|
661
|
+
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
534
662
|
# connect errors, exit gracefully
|
535
663
|
handle_error(e)
|
536
|
-
|
664
|
+
handle_connect_error(e) if connecting?
|
537
665
|
@state = :closed
|
538
|
-
|
666
|
+
purge_after_closed
|
667
|
+
disconnect
|
539
668
|
end
|
540
669
|
|
541
670
|
def handle_transition(nextstate)
|
@@ -543,12 +672,12 @@ module HTTPX
|
|
543
672
|
when :idle
|
544
673
|
@timeout = @current_timeout = @options.timeout[:connect_timeout]
|
545
674
|
|
546
|
-
@connected_at = nil
|
675
|
+
@connected_at = @response_received_at = nil
|
547
676
|
when :open
|
548
677
|
return if @state == :closed
|
549
678
|
|
550
679
|
@io.connect
|
551
|
-
|
680
|
+
close_sibling if @io.state == :connected
|
552
681
|
|
553
682
|
return unless @io.connected?
|
554
683
|
|
@@ -560,6 +689,9 @@ module HTTPX
|
|
560
689
|
emit(:open)
|
561
690
|
when :inactive
|
562
691
|
return unless @state == :open
|
692
|
+
|
693
|
+
# do not deactivate connection in use
|
694
|
+
return if @inflight.positive?
|
563
695
|
when :closing
|
564
696
|
return unless @state == :idle || @state == :open
|
565
697
|
|
@@ -577,7 +709,8 @@ module HTTPX
|
|
577
709
|
return unless @write_buffer.empty?
|
578
710
|
|
579
711
|
purge_after_closed
|
580
|
-
|
712
|
+
disconnect if @pending.empty?
|
713
|
+
|
581
714
|
when :already_open
|
582
715
|
nextstate = :open
|
583
716
|
# the first check for given io readiness must still use a timeout.
|
@@ -588,11 +721,29 @@ module HTTPX
|
|
588
721
|
return unless @state == :inactive
|
589
722
|
|
590
723
|
nextstate = :open
|
591
|
-
|
724
|
+
|
725
|
+
# activate
|
726
|
+
@current_session.select_connection(self, @current_selector)
|
592
727
|
end
|
593
728
|
@state = nextstate
|
594
729
|
end
|
595
730
|
|
731
|
+
def close_sibling
|
732
|
+
return unless @sibling
|
733
|
+
|
734
|
+
if @sibling.io_connected?
|
735
|
+
reset
|
736
|
+
# TODO: transition connection to closed
|
737
|
+
end
|
738
|
+
|
739
|
+
unless @sibling.state == :closed
|
740
|
+
merge(@sibling) unless @main_sibling
|
741
|
+
@sibling.force_reset(true)
|
742
|
+
end
|
743
|
+
|
744
|
+
@sibling = nil
|
745
|
+
end
|
746
|
+
|
596
747
|
def purge_after_closed
|
597
748
|
@io.close if @io
|
598
749
|
@read_buffer.clear
|
@@ -612,12 +763,40 @@ module HTTPX
|
|
612
763
|
end
|
613
764
|
end
|
614
765
|
|
766
|
+
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
767
|
+
def build_altsvc_connection(alt_origin, origin, alt_params)
|
768
|
+
# do not allow security downgrades on altsvc negotiation
|
769
|
+
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
770
|
+
|
771
|
+
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
772
|
+
|
773
|
+
# altsvc already exists, somehow it wasn't advertised, probably noop
|
774
|
+
return unless altsvc
|
775
|
+
|
776
|
+
alt_options = @options.merge(ssl: @options.ssl.merge(hostname: URI(origin).host))
|
777
|
+
|
778
|
+
connection = @current_session.find_connection(alt_origin, @current_selector, alt_options)
|
779
|
+
|
780
|
+
# advertised altsvc is the same origin being used, ignore
|
781
|
+
return if connection == self
|
782
|
+
|
783
|
+
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
784
|
+
|
785
|
+
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
786
|
+
|
787
|
+
connection.merge(self)
|
788
|
+
terminate
|
789
|
+
rescue UnsupportedSchemeError
|
790
|
+
altsvc["noop"] = true
|
791
|
+
nil
|
792
|
+
end
|
793
|
+
|
615
794
|
def build_socket(addrs = nil)
|
616
795
|
case @type
|
617
796
|
when "tcp"
|
618
|
-
TCP.new(
|
797
|
+
TCP.new(peer, addrs, @options)
|
619
798
|
when "ssl"
|
620
|
-
SSL.new(
|
799
|
+
SSL.new(peer, addrs, @options) do |sock|
|
621
800
|
sock.ssl_session = @ssl_session
|
622
801
|
sock.session_new_cb do |sess|
|
623
802
|
@ssl_session = sess
|
@@ -630,14 +809,14 @@ module HTTPX
|
|
630
809
|
|
631
810
|
path = String(path) if path
|
632
811
|
|
633
|
-
UNIX.new(
|
812
|
+
UNIX.new(peer, path, @options)
|
634
813
|
else
|
635
814
|
raise Error, "unsupported transport (#{@type})"
|
636
815
|
end
|
637
816
|
end
|
638
817
|
|
639
|
-
def on_error(error)
|
640
|
-
if error.
|
818
|
+
def on_error(error, request = nil)
|
819
|
+
if error.is_a?(OperationTimeoutError)
|
641
820
|
|
642
821
|
# inactive connections do not contribute to the select loop, therefore
|
643
822
|
# they should not fail due to such errors.
|
@@ -650,39 +829,60 @@ module HTTPX
|
|
650
829
|
|
651
830
|
error = error.to_connection_error if connecting?
|
652
831
|
end
|
653
|
-
handle_error(error)
|
832
|
+
handle_error(error, request)
|
654
833
|
reset
|
655
834
|
end
|
656
835
|
|
657
|
-
def handle_error(error)
|
658
|
-
parser.handle_error(error) if @parser && parser.respond_to?(:handle_error)
|
659
|
-
while (
|
660
|
-
|
661
|
-
|
662
|
-
|
836
|
+
def handle_error(error, request = nil)
|
837
|
+
parser.handle_error(error, request) if @parser && parser.respond_to?(:handle_error)
|
838
|
+
while (req = @pending.shift)
|
839
|
+
next if request && req == request
|
840
|
+
|
841
|
+
response = ErrorResponse.new(req, error)
|
842
|
+
req.response = response
|
843
|
+
req.emit(:response, response)
|
663
844
|
end
|
845
|
+
|
846
|
+
return unless request
|
847
|
+
|
848
|
+
@inflight -= 1
|
849
|
+
response = ErrorResponse.new(request, error)
|
850
|
+
request.response = response
|
851
|
+
request.emit(:response, response)
|
664
852
|
end
|
665
853
|
|
666
854
|
def set_request_timeouts(request)
|
667
|
-
|
855
|
+
set_request_write_timeout(request)
|
856
|
+
set_request_read_timeout(request)
|
857
|
+
set_request_request_timeout(request)
|
858
|
+
end
|
859
|
+
|
860
|
+
def set_request_read_timeout(request)
|
668
861
|
read_timeout = request.read_timeout
|
669
|
-
request_timeout = request.request_timeout
|
670
862
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
863
|
+
return if read_timeout.nil? || read_timeout.infinite?
|
864
|
+
|
865
|
+
set_request_timeout(:read_timeout, request, read_timeout, :done, :response) do
|
866
|
+
read_timeout_callback(request, read_timeout)
|
675
867
|
end
|
868
|
+
end
|
676
869
|
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
870
|
+
def set_request_write_timeout(request)
|
871
|
+
write_timeout = request.write_timeout
|
872
|
+
|
873
|
+
return if write_timeout.nil? || write_timeout.infinite?
|
874
|
+
|
875
|
+
set_request_timeout(:write_timeout, request, write_timeout, :headers, %i[done response]) do
|
876
|
+
write_timeout_callback(request, write_timeout)
|
681
877
|
end
|
878
|
+
end
|
879
|
+
|
880
|
+
def set_request_request_timeout(request)
|
881
|
+
request_timeout = request.request_timeout
|
682
882
|
|
683
883
|
return if request_timeout.nil? || request_timeout.infinite?
|
684
884
|
|
685
|
-
set_request_timeout(request, request_timeout, :headers, :
|
885
|
+
set_request_timeout(:request_timeout, request, request_timeout, :headers, :complete) do
|
686
886
|
read_timeout_callback(request, request_timeout, RequestTimeoutError)
|
687
887
|
end
|
688
888
|
end
|
@@ -692,7 +892,8 @@ module HTTPX
|
|
692
892
|
|
693
893
|
@write_buffer.clear
|
694
894
|
error = WriteTimeoutError.new(request, nil, write_timeout)
|
695
|
-
|
895
|
+
|
896
|
+
on_error(error, request)
|
696
897
|
end
|
697
898
|
|
698
899
|
def read_timeout_callback(request, read_timeout, error_type = ReadTimeoutError)
|
@@ -702,24 +903,22 @@ module HTTPX
|
|
702
903
|
|
703
904
|
@write_buffer.clear
|
704
905
|
error = error_type.new(request, request.response, read_timeout)
|
705
|
-
|
906
|
+
|
907
|
+
on_error(error, request)
|
706
908
|
end
|
707
909
|
|
708
|
-
def set_request_timeout(request, timeout, start_event, finish_events, &callback)
|
709
|
-
request.
|
710
|
-
|
910
|
+
def set_request_timeout(label, request, timeout, start_event, finish_events, &callback)
|
911
|
+
request.set_timeout_callback(start_event) do
|
912
|
+
timer = @current_selector.after(timeout, callback)
|
913
|
+
request.active_timeouts << label
|
711
914
|
|
712
915
|
Array(finish_events).each do |event|
|
713
916
|
# clean up request timeouts if the connection errors out
|
714
|
-
request.
|
715
|
-
|
716
|
-
|
717
|
-
@intervals.delete(interval) if interval.no_callbacks?
|
718
|
-
end
|
917
|
+
request.set_timeout_callback(event) do
|
918
|
+
timer.cancel
|
919
|
+
request.active_timeouts.delete(label)
|
719
920
|
end
|
720
921
|
end
|
721
|
-
|
722
|
-
@intervals << interval
|
723
922
|
end
|
724
923
|
end
|
725
924
|
|
data/lib/httpx/errors.rb
CHANGED
@@ -29,6 +29,18 @@ module HTTPX
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
# Raise when it can't acquire a connection for a given origin.
|
33
|
+
class PoolTimeoutError < TimeoutError
|
34
|
+
attr_reader :origin
|
35
|
+
|
36
|
+
# initializes the +origin+ it refers to, and the
|
37
|
+
# +timeout+ causing the error.
|
38
|
+
def initialize(origin, timeout)
|
39
|
+
@origin = origin
|
40
|
+
super(timeout, "Timed out after #{timeout} seconds while waiting for a connection to #{origin}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
32
44
|
# Error raised when there was a timeout establishing the connection to a server.
|
33
45
|
# This may be raised due to timeouts during TCP and TLS (when applicable) connection
|
34
46
|
# establishment.
|
@@ -65,6 +77,9 @@ module HTTPX
|
|
65
77
|
# Error raised when there was a timeout while resolving a domain to an IP.
|
66
78
|
class ResolveTimeoutError < TimeoutError; end
|
67
79
|
|
80
|
+
# Error raise when there was a timeout waiting for readiness of the socket the request is related to.
|
81
|
+
class OperationTimeoutError < TimeoutError; end
|
82
|
+
|
68
83
|
# Error raised when there was an error while resolving a domain to an IP.
|
69
84
|
class ResolveError < Error; end
|
70
85
|
|
@@ -100,8 +115,4 @@ module HTTPX
|
|
100
115
|
@response.status
|
101
116
|
end
|
102
117
|
end
|
103
|
-
|
104
|
-
# error raised when a request was sent a server which can't reproduce a response, and
|
105
|
-
# has therefore returned an HTTP response using the 421 status code.
|
106
|
-
class MisdirectedRequestError < HTTPError; end
|
107
118
|
end
|
data/lib/httpx/io/ssl.rb
CHANGED
@@ -92,9 +92,12 @@ module HTTPX
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def connect
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
return if @state == :negotiated
|
96
|
+
|
97
|
+
unless @state == :connected
|
98
|
+
super
|
99
|
+
return unless @state == :connected
|
100
|
+
end
|
98
101
|
|
99
102
|
unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
100
103
|
if (hostname_is_ip = (@ip == @sni_hostname))
|
data/lib/httpx/io/tcp.rb
CHANGED
data/lib/httpx/io/unix.rb
CHANGED
data/lib/httpx/loggable.rb
CHANGED
@@ -13,22 +13,29 @@ module HTTPX
|
|
13
13
|
white: 37,
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
|
17
|
-
return unless @options.debug
|
18
|
-
return unless @options.debug_level >= level
|
16
|
+
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
19
17
|
|
20
|
-
|
18
|
+
def log(level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug, &msg)
|
19
|
+
return unless debug_level >= level
|
21
20
|
|
22
|
-
|
21
|
+
debug_stream = debug || ($stderr if USE_DEBUG_LOG)
|
22
|
+
|
23
|
+
return unless debug_stream
|
24
|
+
|
25
|
+
klass = self.class
|
26
|
+
|
27
|
+
until (class_name = klass.name)
|
28
|
+
klass = klass.superclass
|
29
|
+
end
|
30
|
+
|
31
|
+
message = +"(pid:#{Process.pid} tid:#{Thread.current.object_id}, self:#{class_name}##{object_id}) "
|
32
|
+
message << msg.call << "\n"
|
23
33
|
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
|
24
34
|
debug_stream << message
|
25
35
|
end
|
26
36
|
|
27
|
-
def log_exception(ex, level: @options.debug_level, color: nil)
|
28
|
-
|
29
|
-
return unless @options.debug_level >= level
|
30
|
-
|
31
|
-
log(level: level, color: color) { ex.full_message }
|
37
|
+
def log_exception(ex, level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug)
|
38
|
+
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
32
39
|
end
|
33
40
|
end
|
34
41
|
end
|