httpx 0.11.1 → 0.13.1
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_11_2.md +5 -0
- data/doc/release_notes/0_11_3.md +5 -0
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- data/doc/release_notes/0_13_1.md +5 -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/chainable.rb +2 -2
- data/lib/httpx/connection.rb +80 -28
- data/lib/httpx/connection/http1.rb +19 -6
- data/lib/httpx/connection/http2.rb +32 -25
- data/lib/httpx/io.rb +16 -3
- data/lib/httpx/io/ssl.rb +35 -24
- data/lib/httpx/io/tcp.rb +50 -28
- 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/io/unix.rb +27 -12
- data/lib/httpx/options.rb +11 -23
- 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.rb +20 -8
- data/lib/httpx/plugins/compression/brotli.rb +8 -6
- data/lib/httpx/plugins/compression/deflate.rb +4 -7
- data/lib/httpx/plugins/compression/gzip.rb +2 -2
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +1 -1
- data/lib/httpx/plugins/digest_authentication.rb +1 -1
- data/lib/httpx/plugins/follow_redirects.rb +1 -1
- data/lib/httpx/plugins/h2c.rb +43 -58
- 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 +2 -2
- data/lib/httpx/plugins/stream.rb +6 -6
- data/lib/httpx/plugins/upgrade.rb +83 -0
- data/lib/httpx/plugins/upgrade/h2.rb +54 -0
- data/lib/httpx/pool.rb +14 -6
- 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 +14 -7
- 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/chainable.rbs +2 -1
- data/sig/connection/http1.rbs +3 -2
- data/sig/connection/http2.rbs +5 -3
- data/sig/options.rbs +7 -20
- data/sig/plugins/aws_sdk_authentication.rbs +17 -0
- data/sig/plugins/aws_sigv4.rbs +64 -0
- data/sig/plugins/compression.rbs +5 -3
- data/sig/plugins/compression/brotli.rbs +1 -1
- data/sig/plugins/compression/deflate.rbs +1 -1
- data/sig/plugins/compression/gzip.rbs +1 -1
- data/sig/plugins/cookies.rbs +0 -1
- data/sig/plugins/digest_authentication.rbs +0 -1
- data/sig/plugins/expect.rbs +0 -2
- data/sig/plugins/follow_redirects.rbs +0 -2
- data/sig/plugins/h2c.rbs +5 -10
- data/sig/plugins/persistent.rbs +0 -1
- data/sig/plugins/proxy.rbs +0 -1
- data/sig/plugins/push_promise.rbs +1 -1
- data/sig/plugins/retries.rbs +0 -4
- data/sig/plugins/upgrade.rbs +23 -0
- data/sig/response.rbs +3 -1
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3859c516c1cdddc64f8105fafd5425da26074582b1590cc87f65401a909e0e2
|
4
|
+
data.tar.gz: 209b5c1fba921d69f9aa2167ccb0f4b6b45a1a28667364eefef8026104004f53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3862b4879291aa944b92c50d232abc6413a170f2bcd501d6f33a1ca55a99a1411dd0d4f2b6a50b92a8b1465fa42b654ef658c15a91154205bfd60bcdc983c5a5
|
7
|
+
data.tar.gz: 6b2f721cc2bb8e0fe7267517dcd146fe50f448676bcec64e9744d5f358232d8d9c364acd6717e0df3a72bfcb1d154a7e4b74252324cf9a248f1fe062242eb4fe
|
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,5 @@
|
|
1
|
+
# 0.11.3
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
`EOFError` was being thrown when HTTP/1.1 non-chunked responses which don't contain a "content-length" header. The RFC mandates that the socket should be consumed until the server closes it, so a patch was implemented, to return the available response in such cases.
|
@@ -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
|
+
*
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# 0.13.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Upgrade plugin
|
6
|
+
|
7
|
+
A new plugin, `:upgrade`, is now available. This plugin allows one to "hook" on HTTP/1.1's protocol upgrade mechanism (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism), which is the mechanism that browsers use to initiate websockets (there is an example of how to use `httpx` to start a websocket client connection [in the tests](https://gitlab.com/honeyryderchuck/httpx/-/blob/master/test/support/requests/plugins/upgrade.rb))
|
8
|
+
|
9
|
+
You can read more about the `:upgrade` plugin in the [wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Connection-Upgrade).
|
10
|
+
|
11
|
+
It's the basis of two plugins:
|
12
|
+
|
13
|
+
#### `:h2c`
|
14
|
+
|
15
|
+
This plugin was been rewritten on top of the `:upgrade` plugin, and handles upgrading a plaintext (non-"https") HTTP/1.1 connection, into an HTTP/2 connection.
|
16
|
+
|
17
|
+
https://honeyryderchuck.gitlab.io/httpx/wiki/Connection-Upgrade#h2c
|
18
|
+
|
19
|
+
#### `:upgrade/h2`
|
20
|
+
|
21
|
+
This plugin handles when a server responds to a request with an `Upgrade: h2` header, does the following requests to the same origin via HTTP/2 prior knowledge (bypassing the necessity for ALPN negotiation, which is the whole point of the feature).
|
22
|
+
|
23
|
+
https://honeyryderchuck.gitlab.io/httpx/wiki/Connection-Upgrade#h2
|
24
|
+
|
25
|
+
### `:addresses` option
|
26
|
+
|
27
|
+
The `:addresses` option is now available. You can use it to pass a list of IPs to connect to:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# will not resolve example.com, and instead connect to one of the IPs passed.
|
31
|
+
HTTPX.get("http://example.com", addresses: %w[172.5.3.1 172.5.3.2]))
|
32
|
+
```
|
33
|
+
|
34
|
+
You should also use it to connect to HTTP servers bound to a UNIX socket, in which case you'll have to provide a path:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
HTTPX.get("http://example.com", addresses: %w[/path/to/usocket]))
|
38
|
+
```
|
39
|
+
|
40
|
+
The `:transport_options` are therefore deprecated, and will be moved in a major version.
|
41
|
+
|
42
|
+
## Improvements
|
43
|
+
|
44
|
+
Some internal improvements that allow certainn plugins not to "leak" globally, such as the `:compression` plugin, which used to enable compression for all the `httpx` sessions from the same process. It doesn't anymore.
|
45
|
+
|
46
|
+
Using exceptionless nonblocking connect calls in the supported rubies.
|
47
|
+
|
48
|
+
Removed unneeded APIs around the Options object (`with_` methods, or the defined options list).
|
49
|
+
|
50
|
+
## Bugfixes
|
51
|
+
|
52
|
+
HTTP/1.1 persistent connections were closing after each request after the max requests was reached. It's fixed, and the new connection will also be persistent.
|
53
|
+
|
54
|
+
When passing open IO objects for origins (the `:io` option), `httpx` was still trying to resolve the origin's domain. This not only didn't make sense, it broke if the domain is unresolvable. It has been fixed.
|
55
|
+
|
56
|
+
Fixed usage of `:io` option when passed an "authority/io" hash.
|
57
|
+
|
58
|
+
Fixing some issues around trying to connnect to the next available IPAddress when the previous one was unreachable or ETIMEDOUT.
|
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/chainable.rb
CHANGED
@@ -17,12 +17,12 @@ module HTTPX
|
|
17
17
|
# :nocov:
|
18
18
|
def timeout(**args)
|
19
19
|
warn ":#{__method__} is deprecated, use :with_timeout instead"
|
20
|
-
|
20
|
+
with(timeout: args)
|
21
21
|
end
|
22
22
|
|
23
23
|
def headers(headers)
|
24
24
|
warn ":#{__method__} is deprecated, use :with_headers instead"
|
25
|
-
|
25
|
+
with(headers: headers)
|
26
26
|
end
|
27
27
|
# :nocov:
|
28
28
|
|
data/lib/httpx/connection.rb
CHANGED
@@ -71,6 +71,8 @@ module HTTPX
|
|
71
71
|
@inflight = 0
|
72
72
|
@keep_alive_timeout = options.timeout.keep_alive_timeout
|
73
73
|
@keep_alive_timer = nil
|
74
|
+
|
75
|
+
self.addresses = options.addresses if options.addresses
|
74
76
|
end
|
75
77
|
|
76
78
|
# this is a semi-private method, to be used by the resolver
|
@@ -105,6 +107,8 @@ module HTTPX
|
|
105
107
|
|
106
108
|
return false if exhausted?
|
107
109
|
|
110
|
+
return false unless connection.addresses
|
111
|
+
|
108
112
|
!(@io.addresses & connection.addresses).empty? && @options == connection.options
|
109
113
|
end
|
110
114
|
|
@@ -170,7 +174,7 @@ module HTTPX
|
|
170
174
|
end
|
171
175
|
|
172
176
|
# if the write buffer is full, we drain it
|
173
|
-
return :w
|
177
|
+
return :w unless @write_buffer.empty?
|
174
178
|
|
175
179
|
return @parser.interests if @parser
|
176
180
|
|
@@ -251,11 +255,18 @@ module HTTPX
|
|
251
255
|
|
252
256
|
def consume
|
253
257
|
catch(:called) do
|
258
|
+
epiped = false
|
254
259
|
loop do
|
255
260
|
parser.consume
|
256
261
|
|
257
|
-
# we exit if there's no more
|
258
|
-
|
262
|
+
# we exit if there's no more requests to process
|
263
|
+
#
|
264
|
+
# this condition takes into account:
|
265
|
+
#
|
266
|
+
# * the number of inflight requests
|
267
|
+
# * the number of pending requests
|
268
|
+
# * whether the write buffer has bytes (i.e. for close handshake)
|
269
|
+
if @pending.size.zero? && @inflight.zero? && @write_buffer.empty?
|
259
270
|
log(level: 3) { "NO MORE REQUESTS..." }
|
260
271
|
return
|
261
272
|
end
|
@@ -265,9 +276,17 @@ module HTTPX
|
|
265
276
|
read_drained = false
|
266
277
|
write_drained = nil
|
267
278
|
|
268
|
-
#
|
279
|
+
#
|
280
|
+
# tight read loop.
|
281
|
+
#
|
282
|
+
# read as much of the socket as possible.
|
283
|
+
#
|
284
|
+
# this tight loop reads all the data it can from the socket and pipes it to
|
285
|
+
# its parser.
|
286
|
+
#
|
269
287
|
loop do
|
270
288
|
siz = @io.read(@window_size, @read_buffer)
|
289
|
+
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes..." }
|
271
290
|
unless siz
|
272
291
|
ex = EOFError.new("descriptor closed")
|
273
292
|
ex.set_backtrace(caller)
|
@@ -275,27 +294,53 @@ module HTTPX
|
|
275
294
|
return
|
276
295
|
end
|
277
296
|
|
297
|
+
# socket has been drained. mark and exit the read loop.
|
278
298
|
if siz.zero?
|
279
299
|
read_drained = @read_buffer.empty?
|
300
|
+
epiped = false
|
280
301
|
break
|
281
302
|
end
|
282
303
|
|
283
304
|
parser << @read_buffer.to_s
|
284
305
|
|
306
|
+
# continue reading if possible.
|
307
|
+
break if interests == :w && !epiped
|
308
|
+
|
309
|
+
# exit the read loop if connection is preparing to be closed
|
285
310
|
break if @state == :closing || @state == :closed
|
286
311
|
|
287
|
-
#
|
288
|
-
|
312
|
+
# exit #consume altogether if all outstanding requests have been dealt with
|
313
|
+
return if @pending.size.zero? && @inflight.zero?
|
314
|
+
end unless (interests == :w || @state == :closing) && !epiped
|
289
315
|
|
290
|
-
#
|
316
|
+
#
|
317
|
+
# tight write loop.
|
318
|
+
#
|
319
|
+
# flush as many bytes as the sockets allow.
|
320
|
+
#
|
291
321
|
loop do
|
322
|
+
# buffer has been drainned, mark and exit the write loop.
|
292
323
|
if @write_buffer.empty?
|
293
324
|
# we only mark as drained on the first loop
|
294
325
|
write_drained = write_drained.nil? && @inflight.positive?
|
326
|
+
|
295
327
|
break
|
296
328
|
end
|
297
329
|
|
298
|
-
|
330
|
+
begin
|
331
|
+
siz = @io.write(@write_buffer)
|
332
|
+
rescue Errno::EPIPE
|
333
|
+
# this can happen if we still have bytes in the buffer to send to the server, but
|
334
|
+
# the server wants to respond immediately with some message, or an error. An example is
|
335
|
+
# when one's uploading a big file to an unintended endpoint, and the server stops the
|
336
|
+
# consumption, and responds immediately with an authorization of even method not allowed error.
|
337
|
+
# at this point, we have to let the connection switch to read-mode.
|
338
|
+
log(level: 2) { "pipe broken, could not flush buffer..." }
|
339
|
+
epiped = true
|
340
|
+
read_drained = false
|
341
|
+
break
|
342
|
+
end
|
343
|
+
log(level: 3, color: :cyan) { "IO WRITE: #{siz} bytes..." }
|
299
344
|
unless siz
|
300
345
|
ex = EOFError.new("descriptor closed")
|
301
346
|
ex.set_backtrace(caller)
|
@@ -303,21 +348,28 @@ module HTTPX
|
|
303
348
|
return
|
304
349
|
end
|
305
350
|
|
351
|
+
# socket closed for writing. mark and exit the write loop.
|
306
352
|
if siz.zero?
|
307
353
|
write_drained = !@write_buffer.empty?
|
308
354
|
break
|
309
355
|
end
|
310
356
|
|
311
|
-
|
357
|
+
# exit write loop if marked to consume from peer, or is closing.
|
358
|
+
break if interests == :r || @state == :closing || @state == :closed
|
312
359
|
|
313
360
|
write_drained = false
|
314
|
-
end
|
361
|
+
end unless interests == :r
|
315
362
|
|
316
363
|
# return if socket is drained
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
364
|
+
next unless (interests != :r || read_drained) &&
|
365
|
+
(interests != :w || write_drained)
|
366
|
+
|
367
|
+
# gotta go back to the event loop. It happens when:
|
368
|
+
#
|
369
|
+
# * the socket is drained of bytes or it's not the interest of the conn to read;
|
370
|
+
# * theres nothing more to write, or it's not in the interest of the conn to write;
|
371
|
+
log(level: 3) { "(#{interests}): WAITING FOR EVENTS..." }
|
372
|
+
return
|
321
373
|
end
|
322
374
|
end
|
323
375
|
end
|
@@ -424,33 +476,33 @@ module HTTPX
|
|
424
476
|
remove_instance_variable(:@total_timeout)
|
425
477
|
end
|
426
478
|
|
427
|
-
|
428
|
-
@read_buffer.clear
|
429
|
-
if @keep_alive_timer
|
430
|
-
@keep_alive_timer.cancel
|
431
|
-
remove_instance_variable(:@keep_alive_timer)
|
432
|
-
end
|
433
|
-
|
434
|
-
remove_instance_variable(:@timeout) if defined?(@timeout)
|
479
|
+
purge_after_closed
|
435
480
|
when :already_open
|
436
481
|
nextstate = :open
|
437
482
|
send_pending
|
438
483
|
end
|
439
484
|
@state = nextstate
|
440
|
-
rescue Errno::EHOSTUNREACH
|
441
|
-
# at this point, all addresses from the IO object have failed
|
442
|
-
reset
|
443
|
-
emit(:unreachable)
|
444
|
-
throw(:jump_tick)
|
445
485
|
rescue Errno::ECONNREFUSED,
|
446
486
|
Errno::EADDRNOTAVAIL,
|
447
|
-
|
487
|
+
Errno::EHOSTUNREACH,
|
488
|
+
TLSError => e
|
448
489
|
# connect errors, exit gracefully
|
449
490
|
handle_error(e)
|
450
491
|
@state = :closed
|
451
492
|
emit(:close)
|
452
493
|
end
|
453
494
|
|
495
|
+
def purge_after_closed
|
496
|
+
@io.close if @io
|
497
|
+
@read_buffer.clear
|
498
|
+
if @keep_alive_timer
|
499
|
+
@keep_alive_timer.cancel
|
500
|
+
remove_instance_variable(:@keep_alive_timer)
|
501
|
+
end
|
502
|
+
|
503
|
+
remove_instance_variable(:@timeout) if defined?(@timeout)
|
504
|
+
end
|
505
|
+
|
454
506
|
def handle_response
|
455
507
|
@inflight -= 1
|
456
508
|
return unless @inflight.zero?
|
@@ -10,7 +10,7 @@ module HTTPX
|
|
10
10
|
MAX_REQUESTS = 100
|
11
11
|
CRLF = "\r\n"
|
12
12
|
|
13
|
-
attr_reader :pending
|
13
|
+
attr_reader :pending, :requests
|
14
14
|
|
15
15
|
def initialize(buffer, options)
|
16
16
|
@options = Options.new(options)
|
@@ -74,9 +74,10 @@ module HTTPX
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def consume
|
77
|
-
requests_limit = [@
|
77
|
+
requests_limit = [@max_requests, @requests.size].min
|
78
|
+
concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min
|
78
79
|
@requests.each_with_index do |request, idx|
|
79
|
-
break if idx >=
|
80
|
+
break if idx >= concurrent_requests_limit
|
80
81
|
next if request.state == :done
|
81
82
|
|
82
83
|
request.headers["connection"] ||= request.options.persistent || idx < requests_limit - 1 ? "keep-alive" : "close"
|
@@ -115,7 +116,7 @@ module HTTPX
|
|
115
116
|
response = @request.response
|
116
117
|
log(level: 2) { "trailer headers received" }
|
117
118
|
|
118
|
-
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
119
|
+
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v.join(", ")}" }.join("\n") }
|
119
120
|
response.merge_headers(h)
|
120
121
|
end
|
121
122
|
|
@@ -161,6 +162,16 @@ module HTTPX
|
|
161
162
|
end
|
162
163
|
|
163
164
|
def handle_error(ex)
|
165
|
+
if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
|
166
|
+
!@request.response.headers.key?("content-length") &&
|
167
|
+
!@request.response.headers.key?("transfer-encoding")
|
168
|
+
# if the response does not contain a content-length header, the server closing the
|
169
|
+
# connnection is the indicator of response consumed.
|
170
|
+
# https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4
|
171
|
+
catch(:called) { on_complete }
|
172
|
+
return
|
173
|
+
end
|
174
|
+
|
164
175
|
if @pipelining
|
165
176
|
disable
|
166
177
|
else
|
@@ -224,6 +235,8 @@ module HTTPX
|
|
224
235
|
|
225
236
|
def disable_pipelining
|
226
237
|
return if @requests.empty?
|
238
|
+
# do not disable pipelining if already set to 1 request at a time
|
239
|
+
return if @max_concurrent_requests == 1
|
227
240
|
|
228
241
|
@requests.each do |r|
|
229
242
|
r.transition(:idle)
|
@@ -240,7 +253,7 @@ module HTTPX
|
|
240
253
|
@pipelining = false
|
241
254
|
end
|
242
255
|
|
243
|
-
def
|
256
|
+
def set_protocol_headers(request)
|
244
257
|
request.headers["host"] ||= request.authority
|
245
258
|
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
246
259
|
if !request.headers.key?("content-length") &&
|
@@ -254,7 +267,6 @@ module HTTPX
|
|
254
267
|
end
|
255
268
|
|
256
269
|
def handle(request)
|
257
|
-
set_request_headers(request)
|
258
270
|
catch(:buffer_full) do
|
259
271
|
request.transition(:headers)
|
260
272
|
join_headers(request) if request.state == :headers
|
@@ -270,6 +282,7 @@ module HTTPX
|
|
270
282
|
log(color: :yellow) { "<- HEADLINE: #{buffer.chomp.inspect}" }
|
271
283
|
@buffer << buffer
|
272
284
|
buffer.clear
|
285
|
+
set_protocol_headers(request)
|
273
286
|
request.headers.each do |field, value|
|
274
287
|
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
275
288
|
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|