httpx 0.8.0 → 0.10.1
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/LICENSE.txt +48 -0
- data/README.md +9 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +39 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx.rb +2 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/chainable.rb +11 -11
- data/lib/httpx/connection.rb +23 -31
- data/lib/httpx/connection/http1.rb +30 -4
- data/lib/httpx/connection/http2.rb +29 -10
- data/lib/httpx/domain_name.rb +440 -0
- data/lib/httpx/errors.rb +2 -1
- data/lib/httpx/extensions.rb +22 -2
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +0 -1
- data/lib/httpx/io/tcp.rb +6 -5
- data/lib/httpx/io/udp.rb +4 -1
- data/lib/httpx/options.rb +5 -1
- data/lib/httpx/parser/http1.rb +14 -17
- data/lib/httpx/plugins/compression.rb +46 -65
- data/lib/httpx/plugins/compression/brotli.rb +10 -14
- data/lib/httpx/plugins/compression/deflate.rb +7 -6
- data/lib/httpx/plugins/compression/gzip.rb +23 -5
- data/lib/httpx/plugins/cookies.rb +21 -60
- data/lib/httpx/plugins/cookies/cookie.rb +173 -0
- data/lib/httpx/plugins/cookies/jar.rb +74 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
- data/lib/httpx/plugins/expect.rb +12 -1
- data/lib/httpx/plugins/follow_redirects.rb +20 -2
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart.rb +12 -6
- data/lib/httpx/plugins/persistent.rb +6 -1
- data/lib/httpx/plugins/proxy.rb +16 -2
- data/lib/httpx/plugins/proxy/socks4.rb +14 -14
- data/lib/httpx/plugins/rate_limiter.rb +51 -0
- data/lib/httpx/plugins/retries.rb +3 -2
- data/lib/httpx/plugins/stream.rb +109 -13
- data/lib/httpx/pool.rb +14 -17
- data/lib/httpx/request.rb +8 -20
- data/lib/httpx/resolver.rb +7 -10
- data/lib/httpx/resolver/https.rb +22 -24
- data/lib/httpx/resolver/native.rb +19 -16
- data/lib/httpx/resolver/resolver_mixin.rb +4 -2
- data/lib/httpx/resolver/system.rb +2 -2
- data/lib/httpx/response.rb +16 -25
- data/lib/httpx/selector.rb +11 -18
- data/lib/httpx/session.rb +40 -26
- data/lib/httpx/transcoder.rb +18 -0
- data/lib/httpx/transcoder/chunker.rb +0 -2
- data/lib/httpx/transcoder/form.rb +9 -7
- data/lib/httpx/transcoder/json.rb +0 -4
- data/lib/httpx/utils.rb +45 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +24 -0
- data/sig/callbacks.rbs +14 -0
- data/sig/chainable.rbs +37 -0
- data/sig/connection.rbs +85 -0
- data/sig/connection/http1.rbs +66 -0
- data/sig/connection/http2.rbs +78 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +3 -0
- data/sig/headers.rbs +42 -0
- data/sig/httpx.rbs +15 -0
- data/sig/loggable.rbs +11 -0
- data/sig/missing.rbs +12 -0
- data/sig/options.rbs +118 -0
- data/sig/parser/http1.rbs +50 -0
- data/sig/plugins/authentication.rbs +11 -0
- data/sig/plugins/basic_authentication.rbs +13 -0
- data/sig/plugins/compression.rbs +55 -0
- data/sig/plugins/compression/brotli.rbs +21 -0
- data/sig/plugins/compression/deflate.rbs +17 -0
- data/sig/plugins/compression/gzip.rbs +29 -0
- data/sig/plugins/cookies.rbs +26 -0
- data/sig/plugins/cookies/cookie.rbs +50 -0
- data/sig/plugins/cookies/jar.rbs +27 -0
- data/sig/plugins/digest_authentication.rbs +33 -0
- data/sig/plugins/expect.rbs +19 -0
- data/sig/plugins/follow_redirects.rbs +37 -0
- data/sig/plugins/h2c.rbs +26 -0
- data/sig/plugins/multipart.rbs +21 -0
- data/sig/plugins/persistent.rbs +17 -0
- data/sig/plugins/proxy.rbs +47 -0
- data/sig/plugins/proxy/http.rbs +14 -0
- data/sig/plugins/proxy/socks4.rbs +33 -0
- data/sig/plugins/proxy/socks5.rbs +36 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/push_promise.rbs +22 -0
- data/sig/plugins/rate_limiter.rbs +11 -0
- data/sig/plugins/retries.rbs +48 -0
- data/sig/plugins/stream.rbs +39 -0
- data/sig/pool.rbs +36 -0
- data/sig/registry.rbs +9 -0
- data/sig/request.rbs +61 -0
- data/sig/resolver.rbs +26 -0
- data/sig/resolver/https.rbs +49 -0
- data/sig/resolver/native.rbs +60 -0
- data/sig/resolver/resolver_mixin.rbs +27 -0
- data/sig/resolver/system.rbs +17 -0
- data/sig/response.rbs +87 -0
- data/sig/selector.rbs +20 -0
- data/sig/session.rbs +49 -0
- data/sig/timeout.rbs +29 -0
- data/sig/transcoder.rbs +18 -0
- data/sig/transcoder/body.rbs +18 -0
- data/sig/transcoder/chunker.rbs +32 -0
- data/sig/transcoder/form.rbs +16 -0
- data/sig/transcoder/json.rbs +14 -0
- metadata +128 -22
- data/lib/httpx/resolver/options.rb +0 -25
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 0.6.5
|
|
2
|
+
|
|
3
|
+
This release fixes important bugs, and automates the PKI for the test suite.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* `resolver_options` can now receive a `:cache` flag (default: `true`). This bypasses caching and forces the lookup;
|
|
8
|
+
|
|
9
|
+
## Improvements
|
|
10
|
+
|
|
11
|
+
* Building the TLS certs necessary for the test suite has been scripted, after the initial certs expired and brought the CI to a halt;
|
|
12
|
+
* All DNS resolvers have a functional test, both for the happy as well as the error case;
|
|
13
|
+
* Added functional tests for HTTP and HTTPS proxy with authentication, making all proxy options now tested with authentication;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Bugfixes
|
|
17
|
+
|
|
18
|
+
* native and https DNS resolvers weren't usable after a resolving error;
|
|
19
|
+
* system DNS resolver could halt the system after a dns resolving error;
|
|
20
|
+
* fixed system halt on HTTP proxy authentication error;
|
|
21
|
+
|
|
22
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 0.6.6
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
* The `retries` plugin receives two new options:
|
|
6
|
+
* `retry_on`: a callable that receives the failed response as an argument; the return value will determine whether there'll be a retried request.
|
|
7
|
+
* `retry_after`: time (in seconds) after which there request will be retried. Can be an integer or a callable that receives the request and returns an integer (one can do exponential back-off like that, for example).
|
|
8
|
+
* Added support for DNS-over-HTTPS GET requests as per the latest spec.
|
|
9
|
+
|
|
10
|
+
## Improvements
|
|
11
|
+
|
|
12
|
+
* `HTTPX.plugins` got deprecated; basically, it's great until you have to pass options to a plugin, and then it just works (not). The recommended way to load multiple plugins is `HTTPX.plugin(...).plugin(...)`.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Bugfixes
|
|
16
|
+
|
|
17
|
+
* fixed a proxy bug where an `Alt-Svc` response header would make the client try to connect. Just like connection coalescing and the ORIGIN frame, it ignores it when going through a proxy.
|
|
18
|
+
|
|
19
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 0.6.7
|
|
2
|
+
|
|
3
|
+
## Bugfixes
|
|
4
|
+
|
|
5
|
+
* An error was reported when using the follow plugin allowing insecure redirects: if the insecure redirect would be for the same host, and the original request was performed with HTTP/2, the library would try to coalesce the request, and blocks the reactor. A check was made to ensure that connection only coalesces if both are https connections.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 0.7.0
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Features
|
|
5
|
+
|
|
6
|
+
New option: `:max_requests`. This is a connection-level option signalizing how many requests can be performed on a connection. Although the HTTP/1 parser defined this well, in HTTP/2 this wasn't very clear, so: by definition, the remote MAX_CONCURRENT_STREAMS setting will be used to define it, unless the user explicitly passed the option. You can also pass `:max_requests => Float::INFINITY` if you know that the server allows more requests than that on a connection.
|
|
7
|
+
|
|
8
|
+
New plugin: `:expect`.
|
|
9
|
+
|
|
10
|
+
Although there was support for `expect: 100-continue` header already when passed, this plugin can:
|
|
11
|
+
|
|
12
|
+
* automatically set the header on requests with body;
|
|
13
|
+
* execute the flow;
|
|
14
|
+
* recover from 417 status errors (i.e. try again without it);
|
|
15
|
+
* send body after X seconds if no 100 response came;
|
|
16
|
+
|
|
17
|
+
Suport for `with_` methods for the session. As long as the suffix is a valid attribute, it's just like that:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
HTTPX.with_timeout(...).with_ssl(...)
|
|
21
|
+
# same as:
|
|
22
|
+
# HTTPX.with(timeout: ..., ssl: ...)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Improvements
|
|
26
|
+
|
|
27
|
+
### Connections
|
|
28
|
+
|
|
29
|
+
The following improvements make the `persistent` plugin way more resilient:
|
|
30
|
+
|
|
31
|
+
* Better balancing of HTTP/2 connections by distributing requests among X connections depending of how many requests they can process.
|
|
32
|
+
* Exhausted connections can off-load to a new same-origin connection (such as, when the server negotiates less `MAX_CONCURRENT_STREAMS` than what's expected).
|
|
33
|
+
|
|
34
|
+
### Timeouts
|
|
35
|
+
|
|
36
|
+
(Timeouts will be one of the main improvements from the 0.7.x series)
|
|
37
|
+
|
|
38
|
+
`:total_timeout` is now a connection-level directive, which means that this feature will actually make more sense and account for all requests in a block at the same time, instead of one-by-one.
|
|
39
|
+
|
|
40
|
+
### Options
|
|
41
|
+
|
|
42
|
+
Option setters were being bypassed, therefore a lot of the type-checks defined there weren't effectively being picked upon, which could have led to weird user errors.
|
|
43
|
+
|
|
44
|
+
## Bugfixes
|
|
45
|
+
|
|
46
|
+
* fixed the `push_promise` plugin integration (wasn't working well since `http-2-next` was adopted);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 0.8.0
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Features
|
|
5
|
+
|
|
6
|
+
* `keep_alive_timeout`: for persistent connections, the keep alive timeout will set the connection to be closed if not reused for a request **after** the last received response;
|
|
7
|
+
|
|
8
|
+
## Improvements
|
|
9
|
+
|
|
10
|
+
* using `max_requests` for HTTP/1 pipelining as well;
|
|
11
|
+
* `retries` plugin now works with plain HTTP responses (not just error responses);
|
|
12
|
+
* reduced the number of string allocations from log labels;
|
|
13
|
+
* performance: a lot of improvements were made to optimize the "waiting for IO events" phase, which dramatically reduced the CPU usage and make the performance of the library more on-par with other ruby HTTP gems for the 1-shot request scenario.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Bugfixes
|
|
17
|
+
|
|
18
|
+
* fixed `HTTPX::Response#copy_to`;
|
|
19
|
+
* fixed `compression` plugin not properly compressing request bodies using `gzip`;
|
|
20
|
+
* fixed `compression` plugin not handling `content-encoding: identity` payloads;
|
|
21
|
+
* do not overwrite user-defined `max_requests`on HTTP2 connection handshake;
|
|
22
|
+
* `retries` plugin: connection was blocking when a request with body was retried;
|
|
23
|
+
* `alt-svc: clear` response header was causing the process to hang;
|
|
24
|
+
|
|
25
|
+
## Tests
|
|
26
|
+
|
|
27
|
+
* Code coverage improved to 91%;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.8.2
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
* `:expect` plugin now supports a new option, `:expect_threshold_size`, meaning: the byte size threshold below which no `expect` header will be sent with requests with payload.
|
|
6
|
+
* `:compression` plugin now supports a new option, `:compression_threshold_size`, meaning: the bytesize threshold below which request payload won't be compressed before being sent.
|
|
7
|
+
* for HTTP/2 connections, when `keep_alive_timeout` expires, a `PING` frame is used to check connection availability; if successful, the connection will be reused.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 0.9.0
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
### Multiple requests with specific options
|
|
6
|
+
|
|
7
|
+
You can now pass a third element to the "request element" of an array to `.request`.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
requests = [
|
|
11
|
+
[:post, "https://url/post", { form: { foo: "bar" } }],
|
|
12
|
+
[:post, "https://url/post", { form: { foo: "bar2" } }]
|
|
13
|
+
]
|
|
14
|
+
HTTPX.request(requests)
|
|
15
|
+
# or, if you want to pass options common to all requests
|
|
16
|
+
HTTPX.request(requests, max_concurrent_requests: 1)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### HTTPX::Session#build_request
|
|
21
|
+
|
|
22
|
+
`HTTPX::Session::build_request` is now public API from a session. You can now build requests before you send them. These request objects are still considered somewhat "internal", so consider them immutable and **do not rely on its API**. Just pass them forward.
|
|
23
|
+
|
|
24
|
+
Note: this API is only available for instantiated session, so there is no `HTTPX.build_request`.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
|
|
29
|
+
HTTPX.wrap do |http|
|
|
30
|
+
requests = [
|
|
31
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar" } }),
|
|
32
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar2" } })
|
|
33
|
+
]
|
|
34
|
+
http.request(requests)
|
|
35
|
+
# or, if you want to pass options common to all requests
|
|
36
|
+
http.request(requests, max_concurrent_requests: 1)
|
|
37
|
+
end
|
|
38
|
+
```
|
data/lib/httpx.rb
CHANGED
data/lib/httpx/chainable.rb
CHANGED
|
@@ -10,8 +10,8 @@ module HTTPX
|
|
|
10
10
|
MOD
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def request(
|
|
14
|
-
branch(default_options).request(
|
|
13
|
+
def request(*args, **options)
|
|
14
|
+
branch(default_options).request(*args, **options)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# :nocov:
|
|
@@ -34,20 +34,23 @@ module HTTPX
|
|
|
34
34
|
branch(default_options).wrap(&blk)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def plugin(*args, **opts)
|
|
37
|
+
def plugin(*args, **opts, &blk)
|
|
38
38
|
klass = is_a?(Session) ? self.class : Session
|
|
39
39
|
klass = Class.new(klass)
|
|
40
40
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
|
41
|
-
klass.plugin(*args, **opts).new
|
|
41
|
+
klass.plugin(*args, **opts, &blk).new
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# deprecated
|
|
45
|
+
# :nocov:
|
|
45
46
|
def plugins(*args, **opts)
|
|
47
|
+
warn ":#{__method__} is deprecated, use :plugin instead"
|
|
46
48
|
klass = is_a?(Session) ? self.class : Session
|
|
47
49
|
klass = Class.new(klass)
|
|
48
50
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
|
49
51
|
klass.plugins(*args, **opts).new
|
|
50
52
|
end
|
|
53
|
+
# :nocov:
|
|
51
54
|
|
|
52
55
|
def with(options, &blk)
|
|
53
56
|
branch(default_options.merge(options), &blk)
|
|
@@ -59,7 +62,6 @@ module HTTPX
|
|
|
59
62
|
@options || Options.new
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
# :nodoc:
|
|
63
65
|
def branch(options, &blk)
|
|
64
66
|
return self.class.new(options, &blk) if is_a?(Session)
|
|
65
67
|
|
|
@@ -67,12 +69,10 @@ module HTTPX
|
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
def method_missing(meth, *args, **options)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
super
|
|
75
|
-
end
|
|
72
|
+
return super unless meth =~ /\Awith_(.+)/
|
|
73
|
+
|
|
74
|
+
option = Regexp.last_match(1).to_sym
|
|
75
|
+
with(option => (args.first || options))
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def respond_to_missing?(meth, *args)
|
data/lib/httpx/connection.rb
CHANGED
|
@@ -51,7 +51,7 @@ module HTTPX
|
|
|
51
51
|
def initialize(type, uri, options)
|
|
52
52
|
@type = type
|
|
53
53
|
@origins = [uri.origin]
|
|
54
|
-
@origin =
|
|
54
|
+
@origin = Utils.uri(uri.origin)
|
|
55
55
|
@options = Options.new(options)
|
|
56
56
|
@window_size = @options.window_size
|
|
57
57
|
@read_buffer = Buffer.new(BUFFER_SIZE)
|
|
@@ -88,8 +88,6 @@ module HTTPX
|
|
|
88
88
|
|
|
89
89
|
return false if exhausted?
|
|
90
90
|
|
|
91
|
-
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
|
|
92
|
-
|
|
93
91
|
(
|
|
94
92
|
(
|
|
95
93
|
@origins.include?(uri.origin) &&
|
|
@@ -107,8 +105,6 @@ module HTTPX
|
|
|
107
105
|
|
|
108
106
|
return false if exhausted?
|
|
109
107
|
|
|
110
|
-
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
|
|
111
|
-
|
|
112
108
|
!(@io.addresses & connection.addresses).empty? && @options == connection.options
|
|
113
109
|
end
|
|
114
110
|
|
|
@@ -124,8 +120,8 @@ module HTTPX
|
|
|
124
120
|
end
|
|
125
121
|
end
|
|
126
122
|
|
|
127
|
-
def create_idle
|
|
128
|
-
self.class.new(@type, @origin, @options)
|
|
123
|
+
def create_idle(options = {})
|
|
124
|
+
self.class.new(@type, @origin, @options.merge(options))
|
|
129
125
|
end
|
|
130
126
|
|
|
131
127
|
def merge(connection)
|
|
@@ -135,18 +131,7 @@ module HTTPX
|
|
|
135
131
|
end
|
|
136
132
|
end
|
|
137
133
|
|
|
138
|
-
def
|
|
139
|
-
@origins -= connection.instance_variable_get(:@origins)
|
|
140
|
-
purge_pending do |request|
|
|
141
|
-
request.uri.origin == connection.origin && begin
|
|
142
|
-
request.transition(:idle)
|
|
143
|
-
connection.send(request)
|
|
144
|
-
true
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def purge_pending
|
|
134
|
+
def purge_pending(&block)
|
|
150
135
|
pendings = []
|
|
151
136
|
if @parser
|
|
152
137
|
@inflight -= @parser.pending.size
|
|
@@ -154,9 +139,7 @@ module HTTPX
|
|
|
154
139
|
end
|
|
155
140
|
pendings << @pending
|
|
156
141
|
pendings.each do |pending|
|
|
157
|
-
pending.reject!
|
|
158
|
-
yield request
|
|
159
|
-
end
|
|
142
|
+
pending.reject!(&block)
|
|
160
143
|
end
|
|
161
144
|
end
|
|
162
145
|
|
|
@@ -229,8 +212,19 @@ module HTTPX
|
|
|
229
212
|
def send(request)
|
|
230
213
|
if @parser && !@write_buffer.full?
|
|
231
214
|
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
|
215
|
+
if @keep_alive_timer
|
|
216
|
+
# when pushing a request into an existing connection, we have to check whether there
|
|
217
|
+
# is the possibility that the connection might have extended the keep alive timeout.
|
|
218
|
+
# for such cases, we want to ping for availability before deciding to shovel requests.
|
|
219
|
+
if @keep_alive_timer.fires_in.negative?
|
|
220
|
+
@pending << request
|
|
221
|
+
parser.ping
|
|
222
|
+
return
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
@keep_alive_timer.pause
|
|
226
|
+
end
|
|
232
227
|
@inflight += 1
|
|
233
|
-
@keep_alive_timer.pause if @keep_alive_timer
|
|
234
228
|
parser.send(request)
|
|
235
229
|
else
|
|
236
230
|
@pending << request
|
|
@@ -281,8 +275,6 @@ module HTTPX
|
|
|
281
275
|
return
|
|
282
276
|
end
|
|
283
277
|
|
|
284
|
-
log { "READ: #{siz} bytes..." }
|
|
285
|
-
|
|
286
278
|
if siz.zero?
|
|
287
279
|
read_drained = @read_buffer.empty?
|
|
288
280
|
break
|
|
@@ -310,7 +302,6 @@ module HTTPX
|
|
|
310
302
|
on_error(ex)
|
|
311
303
|
return
|
|
312
304
|
end
|
|
313
|
-
log { "WRITE: #{siz} bytes..." }
|
|
314
305
|
|
|
315
306
|
if siz.zero?
|
|
316
307
|
write_drained = !@write_buffer.empty?
|
|
@@ -361,6 +352,8 @@ module HTTPX
|
|
|
361
352
|
emit(:altsvc, alt_origin, origin, alt_params)
|
|
362
353
|
end
|
|
363
354
|
|
|
355
|
+
parser.on(:pong, &method(:send_pending))
|
|
356
|
+
|
|
364
357
|
parser.on(:promise) do |request, stream|
|
|
365
358
|
request.emit(:promise, parser, stream)
|
|
366
359
|
end
|
|
@@ -395,7 +388,7 @@ module HTTPX
|
|
|
395
388
|
parser.on(:error) do |request, ex|
|
|
396
389
|
case ex
|
|
397
390
|
when MisdirectedRequestError
|
|
398
|
-
emit(:
|
|
391
|
+
emit(:misdirected, request)
|
|
399
392
|
else
|
|
400
393
|
response = ErrorResponse.new(request, ex, @options)
|
|
401
394
|
request.emit(:response, response)
|
|
@@ -431,7 +424,7 @@ module HTTPX
|
|
|
431
424
|
remove_instance_variable(:@total_timeout)
|
|
432
425
|
end
|
|
433
426
|
|
|
434
|
-
@io.close
|
|
427
|
+
@io.close if @io
|
|
435
428
|
@read_buffer.clear
|
|
436
429
|
if @keep_alive_timer
|
|
437
430
|
@keep_alive_timer.cancel
|
|
@@ -451,7 +444,6 @@ module HTTPX
|
|
|
451
444
|
throw(:jump_tick)
|
|
452
445
|
rescue Errno::ECONNREFUSED,
|
|
453
446
|
Errno::EADDRNOTAVAIL,
|
|
454
|
-
Errno::EHOSTUNREACH,
|
|
455
447
|
OpenSSL::SSL::SSLError => e
|
|
456
448
|
# connect errors, exit gracefully
|
|
457
449
|
handle_error(e)
|
|
@@ -469,8 +461,8 @@ module HTTPX
|
|
|
469
461
|
else
|
|
470
462
|
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
|
|
471
463
|
unless @inflight.zero?
|
|
472
|
-
log { "(#{
|
|
473
|
-
|
|
464
|
+
log { "(#{@origin}): keep alive timeout expired" }
|
|
465
|
+
parser.ping
|
|
474
466
|
end
|
|
475
467
|
end
|
|
476
468
|
end
|
|
@@ -21,6 +21,7 @@ module HTTPX
|
|
|
21
21
|
@version = [1, 1]
|
|
22
22
|
@pending = []
|
|
23
23
|
@requests = []
|
|
24
|
+
@handshake_completed = false
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def interests
|
|
@@ -78,6 +79,7 @@ module HTTPX
|
|
|
78
79
|
break if idx >= requests_limit
|
|
79
80
|
next if request.state == :done
|
|
80
81
|
|
|
82
|
+
request.headers["connection"] ||= request.options.persistent || idx < requests_limit - 1 ? "keep-alive" : "close"
|
|
81
83
|
handle(request)
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -154,6 +156,7 @@ module HTTPX
|
|
|
154
156
|
@parser.reset!
|
|
155
157
|
@max_requests -= 1
|
|
156
158
|
manage_connection(response)
|
|
159
|
+
|
|
157
160
|
send(@pending.shift) unless @pending.empty?
|
|
158
161
|
end
|
|
159
162
|
|
|
@@ -170,23 +173,39 @@ module HTTPX
|
|
|
170
173
|
end
|
|
171
174
|
end
|
|
172
175
|
|
|
176
|
+
def ping
|
|
177
|
+
emit(:reset)
|
|
178
|
+
emit(:exhausted)
|
|
179
|
+
end
|
|
180
|
+
|
|
173
181
|
private
|
|
174
182
|
|
|
175
183
|
def manage_connection(response)
|
|
176
184
|
connection = response.headers["connection"]
|
|
177
185
|
case connection
|
|
178
|
-
when /keep
|
|
186
|
+
when /keep-alive/i
|
|
187
|
+
if @handshake_completed
|
|
188
|
+
if @max_requests.zero?
|
|
189
|
+
@pending.concat(@requests)
|
|
190
|
+
@requests.clear
|
|
191
|
+
emit(:exhausted)
|
|
192
|
+
end
|
|
193
|
+
return
|
|
194
|
+
end
|
|
195
|
+
|
|
179
196
|
keep_alive = response.headers["keep-alive"]
|
|
180
197
|
return unless keep_alive
|
|
181
198
|
|
|
182
199
|
parameters = Hash[keep_alive.split(/ *, */).map do |pair|
|
|
183
200
|
pair.split(/ *= */)
|
|
184
201
|
end]
|
|
185
|
-
@max_requests = parameters["max"].to_i if parameters.key?("max")
|
|
202
|
+
@max_requests = parameters["max"].to_i - 1 if parameters.key?("max")
|
|
203
|
+
|
|
186
204
|
if parameters.key?("timeout")
|
|
187
205
|
keep_alive_timeout = parameters["timeout"].to_i
|
|
188
206
|
emit(:timeout, keep_alive_timeout)
|
|
189
207
|
end
|
|
208
|
+
@handshake_completed = true
|
|
190
209
|
when /close/i
|
|
191
210
|
disable
|
|
192
211
|
when nil
|
|
@@ -206,7 +225,14 @@ module HTTPX
|
|
|
206
225
|
def disable_pipelining
|
|
207
226
|
return if @requests.empty?
|
|
208
227
|
|
|
209
|
-
@requests.each
|
|
228
|
+
@requests.each do |r|
|
|
229
|
+
r.transition(:idle)
|
|
230
|
+
|
|
231
|
+
# when we disable pipelining, we still want to try keep-alive.
|
|
232
|
+
# only when keep-alive with one request fails, do we fallback to
|
|
233
|
+
# connection: close.
|
|
234
|
+
r.headers["connection"] = "close" if @max_concurrent_requests == 1
|
|
235
|
+
end
|
|
210
236
|
# server doesn't handle pipelining, and probably
|
|
211
237
|
# doesn't support keep-alive. Fallback to send only
|
|
212
238
|
# 1 keep alive request.
|
|
@@ -216,7 +242,7 @@ module HTTPX
|
|
|
216
242
|
|
|
217
243
|
def set_request_headers(request)
|
|
218
244
|
request.headers["host"] ||= request.authority
|
|
219
|
-
request.headers["connection"] ||= "keep-alive"
|
|
245
|
+
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
|
220
246
|
if !request.headers.key?("content-length") &&
|
|
221
247
|
request.body.bytesize == Float::INFINITY
|
|
222
248
|
request.chunk!
|