httpx 1.3.4 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/1_4_0.md +43 -0
- data/lib/httpx/adapters/faraday.rb +2 -0
- data/lib/httpx/adapters/webmock.rb +11 -5
- data/lib/httpx/callbacks.rb +0 -5
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http2.rb +11 -7
- data/lib/httpx/connection.rb +128 -16
- data/lib/httpx/errors.rb +12 -0
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +26 -16
- data/lib/httpx/plugins/aws_sigv4.rb +31 -16
- data/lib/httpx/plugins/callbacks.rb +12 -2
- data/lib/httpx/plugins/circuit_breaker.rb +0 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/expect.rb +4 -3
- data/lib/httpx/plugins/follow_redirects.rb +7 -8
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +27 -0
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +17 -19
- data/lib/httpx/plugins/proxy.rb +91 -93
- data/lib/httpx/plugins/retries.rb +5 -8
- 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 +16 -12
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/https.rb +12 -19
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +36 -13
- data/lib/httpx/resolver/resolver.rb +49 -11
- data/lib/httpx/resolver/system.rb +29 -11
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response.rb +5 -3
- data/lib/httpx/selector.rb +164 -95
- data/lib/httpx/session.rb +296 -139
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +14 -2
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder/utils/inflater.rb +2 -0
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +19 -20
- data/sig/callbacks.rbs +0 -1
- data/sig/chainable.rbs +4 -0
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +14 -3
- data/sig/errors.rbs +6 -0
- data/sig/loggable.rbs +2 -0
- data/sig/options.rbs +7 -0
- 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/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -11
- data/sig/plugins/push_promise.rbs +3 -0
- 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/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +25 -33
- data/sig/request/body.rbs +5 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +21 -2
- data/sig/resolver.rbs +5 -1
- data/sig/response/buffer.rbs +1 -1
- data/sig/selector.rbs +30 -4
- data/sig/session.rbs +45 -18
- data/sig/transcoder/body.rbs +1 -1
- 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/utils/body_reader.rbs +2 -2
- data/sig/transcoder/utils/deflater.rbs +2 -2
- metadata +10 -4
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/xml.rbs +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7edc783221b5d919a1c788bf360f0cc2d43ee1556eb893c6b5d377d1bd3b4241
|
4
|
+
data.tar.gz: 14dc4593ae5c46acb33a4f242a0058e75eddec70bd22fdf75c05496240de3abc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 181bc9e6155708c2d1cdaee3dc7c5c15cd5e2b1ba8d7911b6dd5a79e1cebe38558766ec89d8446483d434c7387e330874a70633b2ed5fb8f6a9d8a3401a4abf4
|
7
|
+
data.tar.gz: 49f57c406a2a5c5be83c018c2151ef2e842c266e93d9609dff15684e662bb25580e7c84e2e96a32d1a45094c9baf8ad18f89dbab1e8235127f4d40429f733572
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# 1.4.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### `:content_digest` plugin
|
6
|
+
|
7
|
+
The `:content_digest` can be used to calculate the digest of request payloads and set them in the `"content-digest"` header; it can also validate the integrity of responses which declare the same `"content-digest"` header.
|
8
|
+
|
9
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Content-Digest
|
10
|
+
|
11
|
+
## Per-session connection pools
|
12
|
+
|
13
|
+
This architectural changes moves away from per-thread shared connection pools, and into per-session (also thread-safe) connection pools. Unlike before, this enables connections from a session to be reused across threads, as well as limiting the number of connections that can be open on a given origin peer. This fixes long-standing issues, such as reusing connections under a fiber scheduler loop (such as the one from the gem `async`).
|
14
|
+
|
15
|
+
A new `:pool_options` option is introduced, which can be passed an hash with the following sub-options:
|
16
|
+
|
17
|
+
* `:max_connections_per_origin`: maximum number of connections a pool allows (unbounded by default, for backwards compatibility).
|
18
|
+
* `:pool_timeout`: the number of seconds a session will wait for a connection to be checked out (default: 5)
|
19
|
+
|
20
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Connection-Pools
|
21
|
+
|
22
|
+
|
23
|
+
## Improvements
|
24
|
+
|
25
|
+
* `:aws_sigv4` plugin: improved digest calculation on compressed request bodies by buffering content to a tempfile.
|
26
|
+
* `HTTPX::Response#json` will parse payload from extended json MIME types (like `application/ld+json`, `application/hal+json`, ...).
|
27
|
+
|
28
|
+
## Bugfixes
|
29
|
+
|
30
|
+
* `:aws_sigv4` plugin: do not try to rewind a request body which yields chunks.
|
31
|
+
* fixed request encoding when `:json` param is passed, and the `oj` gem is used (by using the `:compat` flag).
|
32
|
+
* native resolver: on message truncation, bubble up tcp handshake errors as resolve errors.
|
33
|
+
* allow `HTTPX::Response#json` to accept extended JSON mime types (such as responses with `content-type: application/ld+json`)
|
34
|
+
|
35
|
+
## Chore
|
36
|
+
|
37
|
+
* default options are now fully frozen (in case anyone relies on overriding them).
|
38
|
+
|
39
|
+
### `:xml` plugin
|
40
|
+
|
41
|
+
XML encoding/decoding (via `:xml` request param, and `HTTPX::Response#xml`) is now available via the `:xml` plugin.
|
42
|
+
|
43
|
+
Using `HTTPX::Response#xml` without the plugin will issue a deprecation warning.
|
@@ -52,16 +52,18 @@ module WebMock
|
|
52
52
|
end
|
53
53
|
|
54
54
|
module InstanceMethods
|
55
|
-
|
56
|
-
|
55
|
+
private
|
56
|
+
|
57
|
+
def do_init_connection(connection, selector)
|
58
|
+
super
|
59
|
+
|
57
60
|
connection.once(:unmock_connection) do
|
58
61
|
unless connection.addresses
|
59
62
|
connection.__send__(:callbacks)[:connect_error].clear
|
60
|
-
|
63
|
+
deselect_connection(connection, selector)
|
61
64
|
end
|
62
|
-
|
65
|
+
resolve_connection(connection, selector)
|
63
66
|
end
|
64
|
-
connection
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -100,6 +102,10 @@ module WebMock
|
|
100
102
|
super
|
101
103
|
end
|
102
104
|
|
105
|
+
def terminate
|
106
|
+
force_reset
|
107
|
+
end
|
108
|
+
|
103
109
|
def send(request)
|
104
110
|
request_signature = Plugin.build_webmock_request_signature(request)
|
105
111
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
data/lib/httpx/callbacks.rb
CHANGED
data/lib/httpx/chainable.rb
CHANGED
@@ -73,7 +73,7 @@ module HTTPX
|
|
73
73
|
].include?(callback)
|
74
74
|
|
75
75
|
warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
|
76
|
-
"Use HTTPX.plugin(:callbacks).#{meth} instead."
|
76
|
+
"Use `HTTPX.plugin(:callbacks).#{meth}` instead."
|
77
77
|
|
78
78
|
plugin(:callbacks).__send__(meth, *args, **options, &blk)
|
79
79
|
else
|
@@ -101,4 +101,6 @@ module HTTPX
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
|
+
|
105
|
+
extend Chainable
|
104
106
|
end
|
@@ -98,10 +98,10 @@ module HTTPX
|
|
98
98
|
@streams.size < @max_requests
|
99
99
|
end
|
100
100
|
|
101
|
-
def send(request)
|
101
|
+
def send(request, head = false)
|
102
102
|
unless can_buffer_more_requests?
|
103
|
-
@pending << request
|
104
|
-
return
|
103
|
+
head ? @pending.unshift(request) : @pending << request
|
104
|
+
return false
|
105
105
|
end
|
106
106
|
unless (stream = @streams[request])
|
107
107
|
stream = @connection.new_stream
|
@@ -113,6 +113,7 @@ module HTTPX
|
|
113
113
|
true
|
114
114
|
rescue ::HTTP2::Error::StreamLimitExceeded
|
115
115
|
@pending.unshift(request)
|
116
|
+
false
|
116
117
|
end
|
117
118
|
|
118
119
|
def consume
|
@@ -154,8 +155,7 @@ module HTTPX
|
|
154
155
|
|
155
156
|
def send_pending
|
156
157
|
while (request = @pending.shift)
|
157
|
-
|
158
|
-
break unless send(request)
|
158
|
+
break unless send(request, true)
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -327,10 +327,14 @@ module HTTPX
|
|
327
327
|
end
|
328
328
|
end
|
329
329
|
send(@pending.shift) unless @pending.empty?
|
330
|
+
|
330
331
|
return unless @streams.empty? && exhausted?
|
331
332
|
|
332
|
-
|
333
|
-
|
333
|
+
if @pending.empty?
|
334
|
+
close
|
335
|
+
else
|
336
|
+
emit(:exhausted)
|
337
|
+
end
|
334
338
|
end
|
335
339
|
|
336
340
|
def on_frame(bytes)
|
data/lib/httpx/connection.rb
CHANGED
@@ -43,13 +43,14 @@ module HTTPX
|
|
43
43
|
|
44
44
|
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session
|
45
45
|
|
46
|
-
attr_writer :
|
46
|
+
attr_writer :current_selector, :coalesced_connection
|
47
47
|
|
48
|
-
attr_accessor :family
|
48
|
+
attr_accessor :current_session, :family
|
49
49
|
|
50
50
|
def initialize(uri, options)
|
51
|
-
@
|
52
|
-
@
|
51
|
+
@current_session = @current_selector = @coalesced_connection = nil
|
52
|
+
@exhausted = @cloned = false
|
53
|
+
|
53
54
|
@options = Options.new(options)
|
54
55
|
@type = initialize_type(uri, @options)
|
55
56
|
@origins = [uri.origin]
|
@@ -58,6 +59,7 @@ module HTTPX
|
|
58
59
|
@read_buffer = Buffer.new(@options.buffer_size)
|
59
60
|
@write_buffer = Buffer.new(@options.buffer_size)
|
60
61
|
@pending = []
|
62
|
+
|
61
63
|
on(:error, &method(:on_error))
|
62
64
|
if @options.io
|
63
65
|
# if there's an already open IO, get its
|
@@ -68,6 +70,31 @@ module HTTPX
|
|
68
70
|
else
|
69
71
|
transition(:idle)
|
70
72
|
end
|
73
|
+
on(:activate) do
|
74
|
+
@current_session.select_connection(self, @current_selector)
|
75
|
+
end
|
76
|
+
on(:close) do
|
77
|
+
next if @exhausted # it'll reset
|
78
|
+
|
79
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
80
|
+
# next unless @current_session
|
81
|
+
|
82
|
+
next unless @current_session
|
83
|
+
|
84
|
+
@current_session.deselect_connection(self, @current_selector, @cloned)
|
85
|
+
end
|
86
|
+
on(:terminate) do
|
87
|
+
next if @exhausted # it'll reset
|
88
|
+
|
89
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
90
|
+
next unless @current_session
|
91
|
+
|
92
|
+
@current_session.deselect_connection(self, @current_selector)
|
93
|
+
end
|
94
|
+
|
95
|
+
on(:altsvc) do |alt_origin, origin, alt_params|
|
96
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
97
|
+
end
|
71
98
|
|
72
99
|
@inflight = 0
|
73
100
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
@@ -77,6 +104,10 @@ module HTTPX
|
|
77
104
|
self.addresses = @options.addresses if @options.addresses
|
78
105
|
end
|
79
106
|
|
107
|
+
def peer
|
108
|
+
@origin
|
109
|
+
end
|
110
|
+
|
80
111
|
# this is a semi-private method, to be used by the resolver
|
81
112
|
# to initiate the io object.
|
82
113
|
def addresses=(addrs)
|
@@ -168,7 +199,12 @@ module HTTPX
|
|
168
199
|
end
|
169
200
|
|
170
201
|
def inflight?
|
171
|
-
@parser &&
|
202
|
+
@parser && (
|
203
|
+
# parser may be dealing with other requests (possibly started from a different fiber)
|
204
|
+
!@parser.empty? ||
|
205
|
+
# connection may be doing connection termination handshake
|
206
|
+
!@write_buffer.empty?
|
207
|
+
)
|
172
208
|
end
|
173
209
|
|
174
210
|
def interests
|
@@ -184,6 +220,9 @@ module HTTPX
|
|
184
220
|
|
185
221
|
return @parser.interests if @parser
|
186
222
|
|
223
|
+
nil
|
224
|
+
rescue StandardError => e
|
225
|
+
emit(:error, e)
|
187
226
|
nil
|
188
227
|
end
|
189
228
|
|
@@ -205,6 +244,9 @@ module HTTPX
|
|
205
244
|
consume
|
206
245
|
end
|
207
246
|
nil
|
247
|
+
rescue StandardError => e
|
248
|
+
emit(:error, e)
|
249
|
+
raise e
|
208
250
|
end
|
209
251
|
|
210
252
|
def close
|
@@ -221,8 +263,9 @@ module HTTPX
|
|
221
263
|
|
222
264
|
# bypasses the state machine to force closing of connections still connecting.
|
223
265
|
# **only** used for Happy Eyeballs v2.
|
224
|
-
def force_reset
|
266
|
+
def force_reset(cloned = false)
|
225
267
|
@state = :closing
|
268
|
+
@cloned = cloned
|
226
269
|
transition(:closed)
|
227
270
|
end
|
228
271
|
|
@@ -235,6 +278,8 @@ module HTTPX
|
|
235
278
|
end
|
236
279
|
|
237
280
|
def send(request)
|
281
|
+
return @coalesced_connection.send(request) if @coalesced_connection
|
282
|
+
|
238
283
|
if @parser && !@write_buffer.full?
|
239
284
|
if @response_received_at && @keep_alive_timeout &&
|
240
285
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
@@ -255,6 +300,8 @@ module HTTPX
|
|
255
300
|
end
|
256
301
|
|
257
302
|
def timeout
|
303
|
+
return if @state == :closed || @state == :inactive
|
304
|
+
|
258
305
|
return @timeout if @timeout
|
259
306
|
|
260
307
|
return @options.timeout[:connect_timeout] if @state == :idle
|
@@ -301,6 +348,12 @@ module HTTPX
|
|
301
348
|
transition(:open)
|
302
349
|
end
|
303
350
|
|
351
|
+
def disconnect
|
352
|
+
emit(:close)
|
353
|
+
@current_session = nil
|
354
|
+
@current_selector = nil
|
355
|
+
end
|
356
|
+
|
304
357
|
def consume
|
305
358
|
return unless @io
|
306
359
|
|
@@ -475,8 +528,25 @@ module HTTPX
|
|
475
528
|
request.emit(:promise, parser, stream)
|
476
529
|
end
|
477
530
|
parser.on(:exhausted) do
|
531
|
+
@exhausted = true
|
532
|
+
current_session = @current_session
|
533
|
+
current_selector = @current_selector
|
534
|
+
parser.close
|
478
535
|
@pending.concat(parser.pending)
|
479
|
-
|
536
|
+
case @state
|
537
|
+
when :closed
|
538
|
+
idling
|
539
|
+
@exhausted = false
|
540
|
+
@current_session = current_session
|
541
|
+
@current_selector = current_selector
|
542
|
+
when :closing
|
543
|
+
once(:close) do
|
544
|
+
idling
|
545
|
+
@exhausted = false
|
546
|
+
@current_session = current_session
|
547
|
+
@current_selector = current_selector
|
548
|
+
end
|
549
|
+
end
|
480
550
|
end
|
481
551
|
parser.on(:origin) do |origin|
|
482
552
|
@origins |= [origin]
|
@@ -492,8 +562,14 @@ module HTTPX
|
|
492
562
|
end
|
493
563
|
parser.on(:reset) do
|
494
564
|
@pending.concat(parser.pending) unless parser.empty?
|
565
|
+
current_session = @current_session
|
566
|
+
current_selector = @current_selector
|
495
567
|
reset
|
496
|
-
|
568
|
+
unless @pending.empty?
|
569
|
+
idling
|
570
|
+
@current_session = current_session
|
571
|
+
@current_selector = current_selector
|
572
|
+
end
|
497
573
|
end
|
498
574
|
parser.on(:current_timeout) do
|
499
575
|
@current_timeout = @timeout = parser.timeout
|
@@ -504,7 +580,15 @@ module HTTPX
|
|
504
580
|
parser.on(:error) do |request, ex|
|
505
581
|
case ex
|
506
582
|
when MisdirectedRequestError
|
507
|
-
|
583
|
+
current_session = @current_session
|
584
|
+
current_selector = @current_selector
|
585
|
+
parser.close
|
586
|
+
|
587
|
+
other_connection = current_session.find_connection(@origin, current_selector,
|
588
|
+
@options.merge(ssl: { alpn_protocols: %w[http/1.1] }))
|
589
|
+
other_connection.merge(self)
|
590
|
+
request.transition(:idle)
|
591
|
+
other_connection.send(request)
|
508
592
|
else
|
509
593
|
response = ErrorResponse.new(request, ex)
|
510
594
|
request.response = response
|
@@ -531,13 +615,13 @@ module HTTPX
|
|
531
615
|
error.set_backtrace(e.backtrace)
|
532
616
|
connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, error) : handle_error(error)
|
533
617
|
@state = :closed
|
534
|
-
|
618
|
+
disconnect
|
535
619
|
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
536
620
|
# connect errors, exit gracefully
|
537
621
|
handle_error(e)
|
538
622
|
connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, e) : handle_error(e)
|
539
623
|
@state = :closed
|
540
|
-
|
624
|
+
disconnect
|
541
625
|
end
|
542
626
|
|
543
627
|
def handle_transition(nextstate)
|
@@ -582,7 +666,7 @@ module HTTPX
|
|
582
666
|
return unless @write_buffer.empty?
|
583
667
|
|
584
668
|
purge_after_closed
|
585
|
-
|
669
|
+
disconnect if @pending.empty?
|
586
670
|
when :already_open
|
587
671
|
nextstate = :open
|
588
672
|
# the first check for given io readiness must still use a timeout.
|
@@ -617,12 +701,40 @@ module HTTPX
|
|
617
701
|
end
|
618
702
|
end
|
619
703
|
|
704
|
+
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
705
|
+
def build_altsvc_connection(alt_origin, origin, alt_params)
|
706
|
+
# do not allow security downgrades on altsvc negotiation
|
707
|
+
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
708
|
+
|
709
|
+
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
710
|
+
|
711
|
+
# altsvc already exists, somehow it wasn't advertised, probably noop
|
712
|
+
return unless altsvc
|
713
|
+
|
714
|
+
alt_options = @options.merge(ssl: @options.ssl.merge(hostname: URI(origin).host))
|
715
|
+
|
716
|
+
connection = @current_session.find_connection(alt_origin, @current_selector, alt_options)
|
717
|
+
|
718
|
+
# advertised altsvc is the same origin being used, ignore
|
719
|
+
return if connection == self
|
720
|
+
|
721
|
+
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
722
|
+
|
723
|
+
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
724
|
+
|
725
|
+
connection.merge(self)
|
726
|
+
terminate
|
727
|
+
rescue UnsupportedSchemeError
|
728
|
+
altsvc["noop"] = true
|
729
|
+
nil
|
730
|
+
end
|
731
|
+
|
620
732
|
def build_socket(addrs = nil)
|
621
733
|
case @type
|
622
734
|
when "tcp"
|
623
|
-
TCP.new(
|
735
|
+
TCP.new(peer, addrs, @options)
|
624
736
|
when "ssl"
|
625
|
-
SSL.new(
|
737
|
+
SSL.new(peer, addrs, @options) do |sock|
|
626
738
|
sock.ssl_session = @ssl_session
|
627
739
|
sock.session_new_cb do |sess|
|
628
740
|
@ssl_session = sess
|
@@ -635,7 +747,7 @@ module HTTPX
|
|
635
747
|
|
636
748
|
path = String(path) if path
|
637
749
|
|
638
|
-
UNIX.new(
|
750
|
+
UNIX.new(peer, path, @options)
|
639
751
|
else
|
640
752
|
raise Error, "unsupported transport (#{@type})"
|
641
753
|
end
|
@@ -734,7 +846,7 @@ module HTTPX
|
|
734
846
|
|
735
847
|
def set_request_timeout(request, timeout, start_event, finish_events, &callback)
|
736
848
|
request.once(start_event) do
|
737
|
-
interval = @
|
849
|
+
interval = @current_selector.after(timeout, callback)
|
738
850
|
|
739
851
|
Array(finish_events).each do |event|
|
740
852
|
# clean up request timeouts if the connection errors out
|
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.
|
data/lib/httpx/loggable.rb
CHANGED
@@ -13,11 +13,14 @@ module HTTPX
|
|
13
13
|
white: 37,
|
14
14
|
}.freeze
|
15
15
|
|
16
|
+
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
17
|
+
|
16
18
|
def log(level: @options.debug_level, color: nil, &msg)
|
17
|
-
return unless @options.debug
|
18
19
|
return unless @options.debug_level >= level
|
19
20
|
|
20
|
-
debug_stream = @options.debug
|
21
|
+
debug_stream = @options.debug || ($stderr if USE_DEBUG_LOG)
|
22
|
+
|
23
|
+
return unless debug_stream
|
21
24
|
|
22
25
|
message = (+"" << msg.call << "\n")
|
23
26
|
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
|
@@ -25,9 +28,6 @@ module HTTPX
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def log_exception(ex, level: @options.debug_level, color: nil)
|
28
|
-
return unless @options.debug
|
29
|
-
return unless @options.debug_level >= level
|
30
|
-
|
31
31
|
log(level: level, color: color) { ex.full_message }
|
32
32
|
end
|
33
33
|
end
|
data/lib/httpx/options.rb
CHANGED
@@ -25,14 +25,14 @@ module HTTPX
|
|
25
25
|
end
|
26
26
|
rescue NotImplementedError
|
27
27
|
[Socket::AF_INET]
|
28
|
-
end
|
28
|
+
end.freeze
|
29
29
|
|
30
30
|
DEFAULT_OPTIONS = {
|
31
31
|
:max_requests => Float::INFINITY,
|
32
|
-
:debug =>
|
32
|
+
:debug => nil,
|
33
33
|
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
34
|
-
:ssl =>
|
35
|
-
:http2_settings => { settings_enable_push: 0 },
|
34
|
+
:ssl => EMPTY_HASH,
|
35
|
+
:http2_settings => { settings_enable_push: 0 }.freeze,
|
36
36
|
:fallback_protocol => "http/1.1",
|
37
37
|
:supported_compression_formats => %w[gzip deflate],
|
38
38
|
:decompress_response_body => true,
|
@@ -56,13 +56,15 @@ module HTTPX
|
|
56
56
|
:response_class => Class.new(Response),
|
57
57
|
:request_body_class => Class.new(Request::Body),
|
58
58
|
:response_body_class => Class.new(Response::Body),
|
59
|
+
:pool_class => Class.new(Pool),
|
59
60
|
:connection_class => Class.new(Connection),
|
60
61
|
:options_class => Class.new(self),
|
61
62
|
:transport => nil,
|
62
63
|
:addresses => nil,
|
63
64
|
:persistent => false,
|
64
65
|
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
65
|
-
:resolver_options => { cache: true },
|
66
|
+
:resolver_options => { cache: true }.freeze,
|
67
|
+
:pool_options => EMPTY_HASH,
|
66
68
|
:ip_families => ip_address_families,
|
67
69
|
}.freeze
|
68
70
|
|
@@ -110,6 +112,7 @@ module HTTPX
|
|
110
112
|
# :request_body_class :: class used to instantiate a request body
|
111
113
|
# :response_body_class :: class used to instantiate a response body
|
112
114
|
# :connection_class :: class used to instantiate connections
|
115
|
+
# :pool_class :: class used to instantiate the session connection pool
|
113
116
|
# :options_class :: class used to instantiate options
|
114
117
|
# :transport :: type of transport to use (set to "unix" for UNIX sockets)
|
115
118
|
# :addresses :: bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses;
|
@@ -118,7 +121,8 @@ module HTTPX
|
|
118
121
|
# :persistent :: whether to persist connections in between requests (defaults to <tt>true</tt>)
|
119
122
|
# :resolver_class :: which resolver to use (defaults to <tt>:native</tt>, can also be <tt>:system<tt> for
|
120
123
|
# using getaddrinfo or <tt>:https</tt> for DoH resolver, or a custom class)
|
121
|
-
# :resolver_options :: hash of options passed to the resolver
|
124
|
+
# :resolver_options :: hash of options passed to the resolver. Accepted keys depend on the resolver type.
|
125
|
+
# :pool_options :: hash of options passed to the connection pool (See Pool#initialize).
|
122
126
|
# :ip_families :: which socket families are supported (system-dependent)
|
123
127
|
# :origin :: HTTP origin to set on requests with relative path (ex: "https://api.serv.com")
|
124
128
|
# :base_path :: path to prefix given relative paths with (ex: "/v2")
|
@@ -215,6 +219,7 @@ module HTTPX
|
|
215
219
|
ssl http2_settings
|
216
220
|
request_class response_class headers_class request_body_class
|
217
221
|
response_body_class connection_class options_class
|
222
|
+
pool_class pool_options
|
218
223
|
io fallback_protocol debug debug_level resolver_class resolver_options
|
219
224
|
compress_request_body decompress_response_body
|
220
225
|
persistent
|
@@ -246,14 +251,6 @@ module HTTPX
|
|
246
251
|
end
|
247
252
|
end
|
248
253
|
|
249
|
-
OTHER_LOOKUP = ->(obj, k, ivar_map) {
|
250
|
-
case obj
|
251
|
-
when Hash
|
252
|
-
obj[ivar_map[k]]
|
253
|
-
else
|
254
|
-
obj.instance_variable_get(k)
|
255
|
-
end
|
256
|
-
}
|
257
254
|
def merge(other)
|
258
255
|
ivar_map = nil
|
259
256
|
other_ivars = case other
|
@@ -266,12 +263,12 @@ module HTTPX
|
|
266
263
|
|
267
264
|
return self if other_ivars.empty?
|
268
265
|
|
269
|
-
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) ==
|
266
|
+
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == access_option(other, ivar, ivar_map) }
|
270
267
|
|
271
268
|
opts = dup
|
272
269
|
|
273
270
|
other_ivars.each do |ivar|
|
274
|
-
v =
|
271
|
+
v = access_option(other, ivar, ivar_map)
|
275
272
|
|
276
273
|
unless v
|
277
274
|
opts.instance_variable_set(ivar, v)
|
@@ -322,6 +319,10 @@ module HTTPX
|
|
322
319
|
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
323
320
|
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
324
321
|
end
|
322
|
+
if defined?(pl::PoolMethods)
|
323
|
+
@pool_class = @pool_class.dup
|
324
|
+
@pool_class.__send__(:include, pl::PoolMethods)
|
325
|
+
end
|
325
326
|
if defined?(pl::ConnectionMethods)
|
326
327
|
@connection_class = @connection_class.dup
|
327
328
|
@connection_class.__send__(:include, pl::ConnectionMethods)
|
@@ -346,5 +347,14 @@ module HTTPX
|
|
346
347
|
instance_variable_set(:"@#{k}", value)
|
347
348
|
end
|
348
349
|
end
|
350
|
+
|
351
|
+
def access_option(obj, k, ivar_map)
|
352
|
+
case obj
|
353
|
+
when Hash
|
354
|
+
obj[ivar_map[k]]
|
355
|
+
else
|
356
|
+
obj.instance_variable_get(k)
|
357
|
+
end
|
358
|
+
end
|
349
359
|
end
|
350
360
|
end
|