httpx 0.11.3 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/doc/release_notes/0_11_1.md +5 -1
- data/doc/release_notes/0_12_0.md +55 -0
- data/lib/httpx.rb +2 -1
- data/lib/httpx/adapters/faraday.rb +4 -6
- data/lib/httpx/altsvc.rb +1 -0
- data/lib/httpx/connection.rb +63 -15
- data/lib/httpx/connection/http1.rb +8 -7
- data/lib/httpx/connection/http2.rb +32 -25
- data/lib/httpx/io.rb +16 -3
- data/lib/httpx/io/ssl.rb +7 -9
- data/lib/httpx/io/tcp.rb +9 -8
- data/lib/httpx/io/tls.rb +218 -0
- data/lib/httpx/io/tls/box.rb +365 -0
- data/lib/httpx/io/tls/context.rb +199 -0
- data/lib/httpx/io/tls/ffi.rb +390 -0
- data/lib/httpx/parser/http1.rb +4 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +81 -0
- data/lib/httpx/plugins/aws_sigv4.rb +218 -0
- data/lib/httpx/plugins/compression/deflate.rb +2 -5
- data/lib/httpx/plugins/internal_telemetry.rb +93 -0
- data/lib/httpx/plugins/multipart.rb +2 -0
- data/lib/httpx/plugins/multipart/encoder.rb +4 -9
- data/lib/httpx/plugins/proxy.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +1 -1
- data/lib/httpx/plugins/proxy/socks4.rb +8 -0
- data/lib/httpx/plugins/proxy/socks5.rb +8 -0
- data/lib/httpx/plugins/push_promise.rb +3 -2
- data/lib/httpx/plugins/retries.rb +1 -1
- data/lib/httpx/plugins/stream.rb +3 -5
- data/lib/httpx/pool.rb +0 -1
- data/lib/httpx/registry.rb +1 -7
- data/lib/httpx/request.rb +11 -1
- data/lib/httpx/resolver/https.rb +3 -11
- data/lib/httpx/response.rb +9 -2
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +25 -2
- data/lib/httpx/transcoder/body.rb +3 -5
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http1.rbs +2 -2
- data/sig/connection/http2.rbs +5 -3
- data/sig/plugins/aws_sdk_authentication.rbs +17 -0
- data/sig/plugins/aws_sigv4.rbs +65 -0
- data/sig/plugins/push_promise.rbs +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5770fadc8d4604ccb0fb274c7fc157802315f68c749a83973ee5596fee8effb1
|
4
|
+
data.tar.gz: 4f24cca053093be31636405dcb740f5c6b2599197d9ebdfb90cbf127e86a5ffc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48fe64207a82e2b8db91b73cc6252ff48ea624e57dcbcbfa30e5f6986e1936e61bbdc83cece34c470dd6e318f41845a306a57e2b5c8710084444b6193786a1eb
|
7
|
+
data.tar.gz: 70b43fc624a8452187a730e39f55d9e92d16438babbd76aa2346c5b3dd0c2cad7f8e5e787f7c4ab1917c52ed750c7656f2f6c5e92a2ec0fce9cc0a76e5994d1e
|
data/README.md
CHANGED
@@ -113,7 +113,7 @@ The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/h
|
|
113
113
|
|
114
114
|
## Supported Rubies
|
115
115
|
|
116
|
-
All Rubies greater or equal to 2.1, and always latest JRuby.
|
116
|
+
All Rubies greater or equal to 2.1, and always latest JRuby and Truffleruby.
|
117
117
|
|
118
118
|
**Note**: This gem is tested against all latest patch versions, i.e. if you're using 2.2.0 and you experience some issue, please test it against 2.2.10 (latest patch version of 2.2) before creating an issue.
|
119
119
|
|
@@ -133,7 +133,7 @@ All Rubies greater or equal to 2.1, and always latest JRuby.
|
|
133
133
|
|
134
134
|
If your requirement is to run requests over HTTP/2 and TLS, make sure you run a version of the gem which compiles OpenSSL 1.0.2 (Ruby 2.3 and higher are guaranteed to).
|
135
135
|
|
136
|
-
|
136
|
+
In order to use HTTP/2 under JRuby, [check this link](https://gitlab.com/honeyryderchuck/httpx/-/wikis/JRuby-Truffleruby-Other-Rubies) to know what to do.
|
137
137
|
|
138
138
|
### Known bugs
|
139
139
|
|
data/doc/release_notes/0_11_1.md
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
# 0.12.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### AWS Sigv4 Authentication Plugin
|
6
|
+
|
7
|
+
A new plugin, `:aws_sigv4`, is now shipped with `httpx`. It implements the [AWS Signature Version 4 request signing process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html), a well documented way of authenticating requests to AWS services, which has since been adopted by other cloud providers, such as Google Cloud Storage.
|
8
|
+
|
9
|
+
See how to use it here: https://gitlab.com/honeyryderchuck/httpx/-/wikis/AWS-Sigv4#sessionaws_sigv4_authentication
|
10
|
+
|
11
|
+
For convenience, there's a derivative plugin, `:aws_sdk_authentication`, which builds on top of `:aws_sigv4`, and integrates with the `aws-sdk-core` gem, maintained by AWS, to resolve the authentication credentials (p.ex. if you support ephemeral access keys).
|
12
|
+
|
13
|
+
See how to use it here: https://gitlab.com/honeyryderchuck/httpx/-/wikis/AWS-Sigv4#sessionaws_sdk_authentication
|
14
|
+
|
15
|
+
Other FAQ: https://gitlab.com/honeyryderchuck/httpx/-/wikis/AWS-Sigv4#faqs
|
16
|
+
|
17
|
+
### HTTP/2 support for JRuby
|
18
|
+
|
19
|
+
`jruby-openssl` doesn't support ALPN protocol negotiation, nor are there plans to implement, which limited the seamless HTTP/2 usage in `httpx`. A new connection adapter was therefore added specifically for JRuby, where ssl/tls connections will be handled using ffi-based openssl bindings, provided you bundle `ffi-compiler` and `concurrent-ruby`, and install a TLS/1.2-compatible `openssl` package.
|
20
|
+
|
21
|
+
See how to use it here: https://gitlab.com/honeyryderchuck/httpx/-/wikis/JRuby-Truffleruby-Other-Rubies#http2
|
22
|
+
|
23
|
+
## Improvements
|
24
|
+
|
25
|
+
|
26
|
+
### truffleruby support
|
27
|
+
|
28
|
+
`httpx` supports and tests against `truffleruby` (known to run tests since v20.3, passing all tests since v21).
|
29
|
+
|
30
|
+
### Performance
|
31
|
+
|
32
|
+
Several optimizations were introduced:
|
33
|
+
|
34
|
+
* Reduction in read/write system calls;
|
35
|
+
* more usage of `String#byteslice` in parsing (instead of string mutation);
|
36
|
+
* Avoid selection on connections with no outstanding requests;
|
37
|
+
|
38
|
+
They all contributed to a massive performance improvement, itself reflected in test runs, which need half the time they used to to complete.
|
39
|
+
|
40
|
+
### APIs
|
41
|
+
|
42
|
+
* `HTTPX::ErrorResponse#to_s` now uses the exception full message, instead of just the backtrace.
|
43
|
+
|
44
|
+
## Bugfixes
|
45
|
+
|
46
|
+
* HTTP/2 stream protocol errors do not cause the process to hang (instead, error responnses are yielded);
|
47
|
+
* Fixed body stream bugs on retries when error causing retry would happen mid-transfer;
|
48
|
+
* Fixed `:multipart` plugin body rewind on retries to start the transfer from the beginning;
|
49
|
+
* Fixed auto-load of `:proxy` plugin when `HTTPS_PROXY` or `HTTP_PROXY` is set;
|
50
|
+
* Errno::EPIPE errors mid transfer now cause `httpx` to read from the server and get the appropriate HTTP error response;
|
51
|
+
* Make sure that all requests have an error responnse if the error happens early;
|
52
|
+
* Fixed TCP handshake Errno::INPROGRESS handling inside TLS connnections, which was causing the process to hang in a high handshake contention scenario;
|
53
|
+
* Do not call the event loop if there's nothing to listen on (the DoH resolver was being listened on even if there was nothing to be request);
|
54
|
+
* Fixed double event registry for DoH resolvers;
|
55
|
+
*
|
data/lib/httpx.rb
CHANGED
@@ -19,7 +19,6 @@ require "httpx/headers"
|
|
19
19
|
require "httpx/request"
|
20
20
|
require "httpx/response"
|
21
21
|
require "httpx/chainable"
|
22
|
-
require "httpx/session"
|
23
22
|
|
24
23
|
# Top-Level Namespace
|
25
24
|
#
|
@@ -59,3 +58,5 @@ module HTTPX
|
|
59
58
|
|
60
59
|
extend Chainable
|
61
60
|
end
|
61
|
+
|
62
|
+
require "httpx/session"
|
@@ -123,11 +123,9 @@ module Faraday
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def method_missing(meth, *args, &blk)
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
super
|
130
|
-
end
|
126
|
+
return super unless @env && @env.respond_to?(meth)
|
127
|
+
|
128
|
+
@env.__send__(meth, *args, &blk)
|
131
129
|
end
|
132
130
|
end
|
133
131
|
|
@@ -197,7 +195,7 @@ module Faraday
|
|
197
195
|
response_headers.merge!(response.headers)
|
198
196
|
end
|
199
197
|
@app.call(env)
|
200
|
-
rescue
|
198
|
+
rescue ::HTTPX::TLSError => e
|
201
199
|
raise SSL_ERROR, e
|
202
200
|
rescue Errno::ECONNABORTED,
|
203
201
|
Errno::ECONNREFUSED,
|
data/lib/httpx/altsvc.rb
CHANGED
data/lib/httpx/connection.rb
CHANGED
@@ -170,7 +170,7 @@ module HTTPX
|
|
170
170
|
end
|
171
171
|
|
172
172
|
# if the write buffer is full, we drain it
|
173
|
-
return :w
|
173
|
+
return :w unless @write_buffer.empty?
|
174
174
|
|
175
175
|
return @parser.interests if @parser
|
176
176
|
|
@@ -251,11 +251,18 @@ module HTTPX
|
|
251
251
|
|
252
252
|
def consume
|
253
253
|
catch(:called) do
|
254
|
+
epiped = false
|
254
255
|
loop do
|
255
256
|
parser.consume
|
256
257
|
|
257
|
-
# we exit if there's no more
|
258
|
-
|
258
|
+
# we exit if there's no more requests to process
|
259
|
+
#
|
260
|
+
# this condition takes into account:
|
261
|
+
#
|
262
|
+
# * the number of inflight requests
|
263
|
+
# * the number of pending requests
|
264
|
+
# * whether the write buffer has bytes (i.e. for close handshake)
|
265
|
+
if @pending.size.zero? && @inflight.zero? && @write_buffer.empty?
|
259
266
|
log(level: 3) { "NO MORE REQUESTS..." }
|
260
267
|
return
|
261
268
|
end
|
@@ -265,9 +272,17 @@ module HTTPX
|
|
265
272
|
read_drained = false
|
266
273
|
write_drained = nil
|
267
274
|
|
268
|
-
#
|
275
|
+
#
|
276
|
+
# tight read loop.
|
277
|
+
#
|
278
|
+
# read as much of the socket as possible.
|
279
|
+
#
|
280
|
+
# this tight loop reads all the data it can from the socket and pipes it to
|
281
|
+
# its parser.
|
282
|
+
#
|
269
283
|
loop do
|
270
284
|
siz = @io.read(@window_size, @read_buffer)
|
285
|
+
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes..." }
|
271
286
|
unless siz
|
272
287
|
ex = EOFError.new("descriptor closed")
|
273
288
|
ex.set_backtrace(caller)
|
@@ -275,27 +290,53 @@ module HTTPX
|
|
275
290
|
return
|
276
291
|
end
|
277
292
|
|
293
|
+
# socket has been drained. mark and exit the read loop.
|
278
294
|
if siz.zero?
|
279
295
|
read_drained = @read_buffer.empty?
|
296
|
+
epiped = false
|
280
297
|
break
|
281
298
|
end
|
282
299
|
|
283
300
|
parser << @read_buffer.to_s
|
284
301
|
|
302
|
+
# continue reading if possible.
|
303
|
+
break if interests == :w && !epiped
|
304
|
+
|
305
|
+
# exit the read loop if connection is preparing to be closed
|
285
306
|
break if @state == :closing || @state == :closed
|
286
307
|
|
287
|
-
#
|
288
|
-
|
308
|
+
# exit #consume altogether if all outstanding requests have been dealt with
|
309
|
+
return if @pending.size.zero? && @inflight.zero?
|
310
|
+
end unless (interests == :w || @state == :closing) && !epiped
|
289
311
|
|
290
|
-
#
|
312
|
+
#
|
313
|
+
# tight write loop.
|
314
|
+
#
|
315
|
+
# flush as many bytes as the sockets allow.
|
316
|
+
#
|
291
317
|
loop do
|
318
|
+
# buffer has been drainned, mark and exit the write loop.
|
292
319
|
if @write_buffer.empty?
|
293
320
|
# we only mark as drained on the first loop
|
294
321
|
write_drained = write_drained.nil? && @inflight.positive?
|
322
|
+
|
295
323
|
break
|
296
324
|
end
|
297
325
|
|
298
|
-
|
326
|
+
begin
|
327
|
+
siz = @io.write(@write_buffer)
|
328
|
+
rescue Errno::EPIPE
|
329
|
+
# this can happen if we still have bytes in the buffer to send to the server, but
|
330
|
+
# the server wants to respond immediately with some message, or an error. An example is
|
331
|
+
# when one's uploading a big file to an unintended endpoint, and the server stops the
|
332
|
+
# consumption, and responds immediately with an authorization of even method not allowed error.
|
333
|
+
# at this point, we have to let the connection switch to read-mode.
|
334
|
+
log(level: 2) { "pipe broken, could not flush buffer..." }
|
335
|
+
epiped = true
|
336
|
+
read_drained = false
|
337
|
+
break
|
338
|
+
end
|
339
|
+
log(level: 3, color: :cyan) { "IO WRITE: #{siz} bytes..." }
|
299
340
|
unless siz
|
300
341
|
ex = EOFError.new("descriptor closed")
|
301
342
|
ex.set_backtrace(caller)
|
@@ -303,21 +344,28 @@ module HTTPX
|
|
303
344
|
return
|
304
345
|
end
|
305
346
|
|
347
|
+
# socket closed for writing. mark and exit the write loop.
|
306
348
|
if siz.zero?
|
307
349
|
write_drained = !@write_buffer.empty?
|
308
350
|
break
|
309
351
|
end
|
310
352
|
|
311
|
-
|
353
|
+
# exit write loop if marked to consume from peer, or is closing.
|
354
|
+
break if interests == :r || @state == :closing || @state == :closed
|
312
355
|
|
313
356
|
write_drained = false
|
314
|
-
end
|
357
|
+
end unless interests == :r
|
315
358
|
|
316
359
|
# return if socket is drained
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
360
|
+
next unless (interests != :r || read_drained) &&
|
361
|
+
(interests != :w || write_drained)
|
362
|
+
|
363
|
+
# gotta go back to the event loop. It happens when:
|
364
|
+
#
|
365
|
+
# * the socket is drained of bytes or it's not the interest of the conn to read;
|
366
|
+
# * theres nothing more to write, or it's not in the interest of the conn to write;
|
367
|
+
log(level: 3) { "(#{interests}): WAITING FOR EVENTS..." }
|
368
|
+
return
|
321
369
|
end
|
322
370
|
end
|
323
371
|
end
|
@@ -444,7 +492,7 @@ module HTTPX
|
|
444
492
|
throw(:jump_tick)
|
445
493
|
rescue Errno::ECONNREFUSED,
|
446
494
|
Errno::EADDRNOTAVAIL,
|
447
|
-
|
495
|
+
TLSError => e
|
448
496
|
# connect errors, exit gracefully
|
449
497
|
handle_error(e)
|
450
498
|
@state = :closed
|
@@ -69,14 +69,16 @@ module HTTPX
|
|
69
69
|
|
70
70
|
return if @requests.include?(request)
|
71
71
|
|
72
|
+
request.once(:headers, &method(:set_protocol_headers))
|
72
73
|
@requests << request
|
73
74
|
@pipelining = true if @requests.size > 1
|
74
75
|
end
|
75
76
|
|
76
77
|
def consume
|
77
|
-
requests_limit = [@
|
78
|
+
requests_limit = [@max_requests, @requests.size].min
|
79
|
+
concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min
|
78
80
|
@requests.each_with_index do |request, idx|
|
79
|
-
break if idx >=
|
81
|
+
break if idx >= concurrent_requests_limit
|
80
82
|
next if request.state == :done
|
81
83
|
|
82
84
|
request.headers["connection"] ||= request.options.persistent || idx < requests_limit - 1 ? "keep-alive" : "close"
|
@@ -115,7 +117,7 @@ module HTTPX
|
|
115
117
|
response = @request.response
|
116
118
|
log(level: 2) { "trailer headers received" }
|
117
119
|
|
118
|
-
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
120
|
+
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v.join(", ")}" }.join("\n") }
|
119
121
|
response.merge_headers(h)
|
120
122
|
end
|
121
123
|
|
@@ -161,13 +163,13 @@ module HTTPX
|
|
161
163
|
end
|
162
164
|
|
163
165
|
def handle_error(ex)
|
164
|
-
if ex.is_a?(EOFError) && @request && @request.response &&
|
166
|
+
if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
|
165
167
|
!@request.response.headers.key?("content-length") &&
|
166
168
|
!@request.response.headers.key?("transfer-encoding")
|
167
169
|
# if the response does not contain a content-length header, the server closing the
|
168
170
|
# connnection is the indicator of response consumed.
|
169
171
|
# https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4
|
170
|
-
on_complete
|
172
|
+
catch(:called) { on_complete }
|
171
173
|
return
|
172
174
|
end
|
173
175
|
|
@@ -250,7 +252,7 @@ module HTTPX
|
|
250
252
|
@pipelining = false
|
251
253
|
end
|
252
254
|
|
253
|
-
def
|
255
|
+
def set_protocol_headers(request)
|
254
256
|
request.headers["host"] ||= request.authority
|
255
257
|
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
256
258
|
if !request.headers.key?("content-length") &&
|
@@ -264,7 +266,6 @@ module HTTPX
|
|
264
266
|
end
|
265
267
|
|
266
268
|
def handle(request)
|
267
|
-
set_request_headers(request)
|
268
269
|
catch(:buffer_full) do
|
269
270
|
request.transition(:headers)
|
270
271
|
join_headers(request) if request.state == :headers
|
@@ -42,11 +42,15 @@ module HTTPX
|
|
42
42
|
return @buffer.empty? ? :r : :rw
|
43
43
|
end
|
44
44
|
|
45
|
-
return :w
|
45
|
+
return :w if !@pending.empty? && can_buffer_more_requests?
|
46
46
|
|
47
47
|
return :w if @streams.each_key.any? { |r| r.interests == :w }
|
48
48
|
|
49
|
-
|
49
|
+
if @buffer.empty?
|
50
|
+
return if @streams.empty? && @pings.empty?
|
51
|
+
|
52
|
+
return :r
|
53
|
+
end
|
50
54
|
|
51
55
|
:rw
|
52
56
|
end
|
@@ -70,10 +74,14 @@ module HTTPX
|
|
70
74
|
@connection << data
|
71
75
|
end
|
72
76
|
|
77
|
+
def can_buffer_more_requests?
|
78
|
+
@handshake_completed &&
|
79
|
+
@streams.size < @max_concurrent_requests &&
|
80
|
+
@streams.size < @max_requests
|
81
|
+
end
|
82
|
+
|
73
83
|
def send(request)
|
74
|
-
|
75
|
-
@streams.size >= @max_concurrent_requests ||
|
76
|
-
@streams.size >= @max_requests
|
84
|
+
unless can_buffer_more_requests?
|
77
85
|
@pending << request
|
78
86
|
return
|
79
87
|
end
|
@@ -83,6 +91,7 @@ module HTTPX
|
|
83
91
|
@streams[request] = stream
|
84
92
|
@max_requests -= 1
|
85
93
|
end
|
94
|
+
request.once(:headers, &method(:set_protocol_headers))
|
86
95
|
handle(request, stream)
|
87
96
|
true
|
88
97
|
rescue HTTP2Next::Error::StreamLimitExceeded
|
@@ -126,8 +135,6 @@ module HTTPX
|
|
126
135
|
request.path
|
127
136
|
end
|
128
137
|
|
129
|
-
def set_request_headers(request); end
|
130
|
-
|
131
138
|
def handle(request, stream)
|
132
139
|
catch(:buffer_full) do
|
133
140
|
request.transition(:headers)
|
@@ -172,18 +179,18 @@ module HTTPX
|
|
172
179
|
stream.on(:data, &method(:on_stream_data).curry(3)[stream, request])
|
173
180
|
end
|
174
181
|
|
182
|
+
def set_protocol_headers(request)
|
183
|
+
request.headers[":scheme"] = request.scheme
|
184
|
+
request.headers[":method"] = request.verb.to_s.upcase
|
185
|
+
request.headers[":path"] = headline_uri(request)
|
186
|
+
request.headers[":authority"] = request.authority
|
187
|
+
end
|
188
|
+
|
175
189
|
def join_headers(stream, request)
|
176
|
-
set_request_headers(request)
|
177
|
-
headers = {}
|
178
|
-
headers[":scheme"] = request.scheme
|
179
|
-
headers[":method"] = request.verb.to_s.upcase
|
180
|
-
headers[":path"] = headline_uri(request)
|
181
|
-
headers[":authority"] = request.authority
|
182
|
-
headers = headers.merge(request.headers)
|
183
190
|
log(level: 1, color: :yellow) do
|
184
|
-
headers.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
191
|
+
request.headers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
185
192
|
end
|
186
|
-
stream.headers(headers, end_stream: request.empty?)
|
193
|
+
stream.headers(request.headers.each, end_stream: request.empty?)
|
187
194
|
end
|
188
195
|
|
189
196
|
def join_body(stream, request)
|
@@ -227,10 +234,15 @@ module HTTPX
|
|
227
234
|
end
|
228
235
|
|
229
236
|
def on_stream_close(stream, request, error)
|
237
|
+
log(level: 2) { "#{stream.id}: closing stream" }
|
238
|
+
@drains.delete(request)
|
239
|
+
@streams.delete(request)
|
240
|
+
|
230
241
|
if error && error != :no_error
|
231
242
|
ex = Error.new(stream.id, error)
|
232
243
|
ex.set_backtrace(caller)
|
233
|
-
|
244
|
+
response = ErrorResponse.new(request, ex, request.options)
|
245
|
+
emit(:response, request, response)
|
234
246
|
else
|
235
247
|
response = request.response
|
236
248
|
if response.status == 421
|
@@ -241,9 +253,6 @@ module HTTPX
|
|
241
253
|
emit(:response, request, response)
|
242
254
|
end
|
243
255
|
end
|
244
|
-
log(level: 2) { "#{stream.id}: closing stream" }
|
245
|
-
|
246
|
-
@streams.delete(request)
|
247
256
|
send(@pending.shift) unless @pending.empty?
|
248
257
|
return unless @streams.empty? && exhausted?
|
249
258
|
|
@@ -328,11 +337,9 @@ module HTTPX
|
|
328
337
|
end
|
329
338
|
|
330
339
|
def method_missing(meth, *args, &blk)
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
super
|
335
|
-
end
|
340
|
+
return super unless @connection.respond_to?(meth)
|
341
|
+
|
342
|
+
@connection.__send__(meth, *args, &blk)
|
336
343
|
end
|
337
344
|
end
|
338
345
|
Connection.register "h2", Connection::HTTP2
|