httpx 0.11.3 → 0.14.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_10_1.md +1 -1
- data/doc/release_notes/0_11_1.md +5 -1
- 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/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/lib/httpx.rb +3 -3
- data/lib/httpx/adapters/faraday.rb +4 -6
- data/lib/httpx/altsvc.rb +1 -0
- data/lib/httpx/callbacks.rb +12 -3
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection.rb +92 -37
- data/lib/httpx/connection/http1.rb +37 -19
- data/lib/httpx/connection/http2.rb +82 -31
- data/lib/httpx/headers.rb +1 -1
- 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/udp.rb +31 -7
- data/lib/httpx/io/unix.rb +27 -12
- data/lib/httpx/options.rb +97 -74
- data/lib/httpx/parser/http1.rb +4 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +84 -0
- data/lib/httpx/plugins/aws_sigv4.rb +219 -0
- data/lib/httpx/plugins/basic_authentication.rb +8 -3
- data/lib/httpx/plugins/compression.rb +24 -12
- data/lib/httpx/plugins/compression/brotli.rb +10 -7
- data/lib/httpx/plugins/compression/deflate.rb +8 -10
- data/lib/httpx/plugins/compression/gzip.rb +4 -3
- data/lib/httpx/plugins/cookies.rb +3 -7
- data/lib/httpx/plugins/digest_authentication.rb +5 -5
- data/lib/httpx/plugins/expect.rb +6 -6
- data/lib/httpx/plugins/follow_redirects.rb +4 -4
- data/lib/httpx/plugins/grpc.rb +247 -0
- data/lib/httpx/plugins/grpc/call.rb +62 -0
- data/lib/httpx/plugins/grpc/message.rb +85 -0
- 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/multipart/part.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +4 -8
- 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/proxy/ssh.rb +3 -3
- data/lib/httpx/plugins/push_promise.rb +3 -2
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +15 -16
- data/lib/httpx/plugins/stream.rb +99 -77
- data/lib/httpx/plugins/upgrade.rb +84 -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 +36 -3
- data/lib/httpx/resolver/https.rb +3 -11
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +18 -7
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +41 -8
- data/lib/httpx/transcoder/body.rb +3 -5
- data/lib/httpx/transcoder/chunker.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +2 -0
- data/sig/chainable.rbs +2 -1
- data/sig/connection/http1.rbs +7 -2
- data/sig/connection/http2.rbs +10 -4
- data/sig/options.rbs +16 -22
- data/sig/plugins/aws_sdk_authentication.rbs +19 -0
- data/sig/plugins/aws_sigv4.rbs +64 -0
- data/sig/plugins/basic_authentication.rbs +2 -0
- data/sig/plugins/compression.rbs +7 -5
- 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/stream.rbs +17 -16
- data/sig/plugins/upgrade.rbs +23 -0
- data/sig/request.rbs +7 -2
- data/sig/response.rbs +4 -1
- data/sig/session.rbs +4 -0
- metadata +56 -33
- data/lib/httpx/timeout.rb +0 -67
- data/sig/timeout.rbs +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abbeaccc55115244f08e7b39aaad174a34dd2e7aeb10b32cdc0563fbefe1b953
|
4
|
+
data.tar.gz: a39fc4e5644b21c1265840011e969c36904756573584cba0f805d568a549ec94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82fa475dcd9ef05ebe90f0b3c742c17c33185410a70eb5af730fce393681b7f9f346decf78349614f77b76257550c0e3056e61f3b5d6cd5efdba0149eff235f6
|
7
|
+
data.tar.gz: 64ceb596415b440c99c9df31569fa0ee2f7c94e51787d5e0d84141f1ade0be92e23e2d7011a361c1366b43c4f159525599367009bd4021c358537f11c4605cac
|
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_10_1.md
CHANGED
@@ -26,7 +26,7 @@ From now on, both headers and the responnse payload will also appear, so expecte
|
|
26
26
|
## Bugfixes
|
27
27
|
|
28
28
|
* HTTP/2 and HTTP/1.1 exhausted connections now get properly migrated into a new connection;
|
29
|
-
* HTTP/2 421 responses will now correctly migrate the connection and
|
29
|
+
* HTTP/2 421 responses will now correctly migrate the connection and pending requests to HTTP/1.1 (a hanging loop was being caused);
|
30
30
|
* HTTP/2 connection failed with a GOAWAY settings timeout will now return error responses (instead of hanging indefinitely);
|
31
31
|
* Non-IP proxy name-resolving errors will now move on to the next available proxy in the list (instead of hanging indefinitely);
|
32
32
|
* Non-IP DNS resolve errors for `native` and `https` variants will now return the appropriate error response (instead of hanging indefinitely);
|
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
|
+
*
|
@@ -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 certain 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.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# 0.13.1
|
2
|
+
|
3
|
+
## Improvements
|
4
|
+
|
5
|
+
`UDPSocket#sendmsg_nonblock` is now used in the native resolver.
|
6
|
+
|
7
|
+
## Bugfixes
|
8
|
+
|
9
|
+
Usage in Windows was buggy, resulting in `Errno::EINVAL` during DNS resolving, when using the native resolver. This was due to a discrepancy between `recvfrom` behaviour in WS Sockets and Linux Sockets. This was fixed by making we the UDP socket never tries to receive before a DNS query has been actually sent.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# 0.14.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### GRPC plugin
|
6
|
+
|
7
|
+
A new plugin, `:grpc`, is now available. This plugin provides a simple DSL to build GRPC services and performing calls using `httpx` under the hood.
|
8
|
+
|
9
|
+
Example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require "httpx"
|
13
|
+
|
14
|
+
grpc = HTTPX.plugin(:grpc)
|
15
|
+
helloworld_stub = grpc.build_stub("localhost:4545")
|
16
|
+
helloworld_svc = helloworld_stub.rpc(:SayHello, HelloRequest, HelloReply)
|
17
|
+
result = helloworld_svc.say_hello(HelloRequest.new(name: "Jack")) #=> HelloReply: "Hello Jack"
|
18
|
+
```
|
19
|
+
|
20
|
+
You can read more about the `:grpc` plugin in the [wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/GRPC).
|
21
|
+
|
22
|
+
### :origin
|
23
|
+
|
24
|
+
A new `:origin` option is available. You can use it for setting a base URL for subsequent relative paths on that session:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
HTTPX.get("/httpbin/get") #=> HTTPX::Error: invalid URI: /httpbin/get
|
28
|
+
|
29
|
+
httpbin = HTTPX.with(origin: "https://nghttp2.org")
|
30
|
+
httpbin.get("/httpbin/get") #=> #<Response:5420 HTTP/2.0 @status=200 ....
|
31
|
+
```
|
32
|
+
|
33
|
+
**Note!** The origin is **not** for setting base paths, i.e. if you pass it a relative path, it'll be filtered out in subsequent requests (`HTTPX.with(origin: "https://nghttp2.org/httpbin")` will still use only `"https://nghttp2.org"`).
|
34
|
+
|
35
|
+
## Improvements
|
36
|
+
|
37
|
+
* setting an unexpected option will now raise an `HTTPX::Error` with an helpful message, instead of a confusing `NoMethodError`:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
HTTPX.with(foo: "bar")
|
41
|
+
# before
|
42
|
+
#=> NoMethodError
|
43
|
+
# after
|
44
|
+
#=> HTTPX::Error: unknown option: foo
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
* `HTTPX::Options#def_option` (which can be used for setting custom plugin options) can now be passed a full body string (where the argument is `value`), although it still support the block form. This is the recommended approach, as the block form is based on `define_method`, which would make clients unusable inside ractors.
|
49
|
+
|
50
|
+
* Added support for `:wait_for_handshake` under the `http2_settings` option (`false` by default). HTTP/2 connections complete the protocol handshake before requests are sent. When this option is `true`, requests get send in the initial payload, before the HTTP/2 connection is fully acknowledged.
|
51
|
+
|
52
|
+
* 441716a5ac0f7707211ebe0048f568cf0b759c3f: The `:stream` plugin has been improved to start streaming the real response as methods are called (instead of a completely separate synchronous one, which is definitely not good):
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
session = HTTPX.plugin(:stream)
|
56
|
+
response = session.get(build_uri("/stream/3"), stream: true)
|
57
|
+
|
58
|
+
# before
|
59
|
+
response.status # this could block indefinitely, if the request truly streams infinitely.
|
60
|
+
|
61
|
+
# after
|
62
|
+
response.status # sends the request, and starts streaming the response until status is available.
|
63
|
+
response.each {|chunk|...} # and now you can start yielding the chunks...
|
64
|
+
```
|
65
|
+
|
66
|
+
|
67
|
+
## Bugfixes
|
68
|
+
|
69
|
+
* fixed usage of the `:multipart` if `pathname` isn't loaded.
|
70
|
+
* fixed HTTP/2 trailers.
|
71
|
+
* fixed connection merges with the same origin, which was causing them to be duplicated and breaking further usage. (#125)
|
72
|
+
* fixed repeated session callbacks on a connection, by ensure they're set only once.
|
73
|
+
* fixed calculation of `content-length` for streaming or chunked compressed requests.
|
74
|
+
|
75
|
+
|
76
|
+
## Chore
|
77
|
+
|
78
|
+
* using ruby base container images in CI instead.
|
79
|
+
* using truffleruby official container image.
|
data/lib/httpx.rb
CHANGED
@@ -12,14 +12,12 @@ require "httpx/callbacks"
|
|
12
12
|
require "httpx/loggable"
|
13
13
|
require "httpx/registry"
|
14
14
|
require "httpx/transcoder"
|
15
|
-
require "httpx/options"
|
16
|
-
require "httpx/timeout"
|
17
15
|
require "httpx/pool"
|
18
16
|
require "httpx/headers"
|
19
17
|
require "httpx/request"
|
20
18
|
require "httpx/response"
|
19
|
+
require "httpx/options"
|
21
20
|
require "httpx/chainable"
|
22
|
-
require "httpx/session"
|
23
21
|
|
24
22
|
# Top-Level Namespace
|
25
23
|
#
|
@@ -59,3 +57,5 @@ module HTTPX
|
|
59
57
|
|
60
58
|
extend Chainable
|
61
59
|
end
|
60
|
+
|
61
|
+
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/callbacks.rb
CHANGED
@@ -6,19 +6,28 @@ module HTTPX
|
|
6
6
|
callbacks(type) << action
|
7
7
|
end
|
8
8
|
|
9
|
-
def once(
|
10
|
-
on(
|
9
|
+
def once(type, &block)
|
10
|
+
on(type) do |*args, &callback|
|
11
11
|
block.call(*args, &callback)
|
12
12
|
:delete
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def only(type, &block)
|
17
|
+
callbacks(type).clear
|
18
|
+
on(type, &block)
|
19
|
+
end
|
20
|
+
|
16
21
|
def emit(type, *args)
|
17
|
-
callbacks(type).delete_if { |pr| pr[*args]
|
22
|
+
callbacks(type).delete_if { |pr| :delete == pr[*args] } # rubocop:disable Style/YodaCondition
|
18
23
|
end
|
19
24
|
|
20
25
|
protected
|
21
26
|
|
27
|
+
def callbacks_for?(type)
|
28
|
+
@callbacks.key?(type) && !@callbacks[type].empty?
|
29
|
+
end
|
30
|
+
|
22
31
|
def callbacks(type = nil)
|
23
32
|
return @callbacks unless type
|
24
33
|
|
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
@@ -69,8 +69,10 @@ module HTTPX
|
|
69
69
|
end
|
70
70
|
|
71
71
|
@inflight = 0
|
72
|
-
@keep_alive_timeout = options.timeout
|
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
|
|
@@ -125,7 +129,7 @@ module HTTPX
|
|
125
129
|
end
|
126
130
|
|
127
131
|
def merge(connection)
|
128
|
-
@origins
|
132
|
+
@origins |= connection.instance_variable_get(:@origins)
|
129
133
|
connection.purge_pending do |req|
|
130
134
|
send(req)
|
131
135
|
end
|
@@ -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
|
|
@@ -234,9 +238,9 @@ module HTTPX
|
|
234
238
|
def timeout
|
235
239
|
return @timeout if defined?(@timeout)
|
236
240
|
|
237
|
-
return @options.timeout
|
241
|
+
return @options.timeout[:connect_timeout] if @state == :idle
|
238
242
|
|
239
|
-
@options.timeout
|
243
|
+
@options.timeout[:operation_timeout]
|
240
244
|
end
|
241
245
|
|
242
246
|
private
|
@@ -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
|
@@ -361,7 +413,7 @@ module HTTPX
|
|
361
413
|
emit(:exhausted)
|
362
414
|
end
|
363
415
|
parser.on(:origin) do |origin|
|
364
|
-
@origins
|
416
|
+
@origins |= [origin]
|
365
417
|
end
|
366
418
|
parser.on(:close) do |force|
|
367
419
|
transition(:closing)
|
@@ -391,6 +443,7 @@ module HTTPX
|
|
391
443
|
emit(:misdirected, request)
|
392
444
|
else
|
393
445
|
response = ErrorResponse.new(request, ex, @options)
|
446
|
+
request.response = response
|
394
447
|
request.emit(:response, response)
|
395
448
|
end
|
396
449
|
end
|
@@ -399,7 +452,7 @@ module HTTPX
|
|
399
452
|
def transition(nextstate)
|
400
453
|
case nextstate
|
401
454
|
when :idle
|
402
|
-
@timeout = @current_timeout = @options.timeout
|
455
|
+
@timeout = @current_timeout = @options.timeout[:connect_timeout]
|
403
456
|
|
404
457
|
when :open
|
405
458
|
return if @state == :closed
|
@@ -411,7 +464,7 @@ module HTTPX
|
|
411
464
|
|
412
465
|
send_pending
|
413
466
|
|
414
|
-
@timeout = @current_timeout = @options.timeout
|
467
|
+
@timeout = @current_timeout = @options.timeout[:operation_timeout]
|
415
468
|
emit(:open)
|
416
469
|
when :closing
|
417
470
|
return unless @state == :open
|
@@ -424,33 +477,33 @@ module HTTPX
|
|
424
477
|
remove_instance_variable(:@total_timeout)
|
425
478
|
end
|
426
479
|
|
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)
|
480
|
+
purge_after_closed
|
435
481
|
when :already_open
|
436
482
|
nextstate = :open
|
437
483
|
send_pending
|
438
484
|
end
|
439
485
|
@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
486
|
rescue Errno::ECONNREFUSED,
|
446
487
|
Errno::EADDRNOTAVAIL,
|
447
|
-
|
488
|
+
Errno::EHOSTUNREACH,
|
489
|
+
TLSError => e
|
448
490
|
# connect errors, exit gracefully
|
449
491
|
handle_error(e)
|
450
492
|
@state = :closed
|
451
493
|
emit(:close)
|
452
494
|
end
|
453
495
|
|
496
|
+
def purge_after_closed
|
497
|
+
@io.close if @io
|
498
|
+
@read_buffer.clear
|
499
|
+
if @keep_alive_timer
|
500
|
+
@keep_alive_timer.cancel
|
501
|
+
remove_instance_variable(:@keep_alive_timer)
|
502
|
+
end
|
503
|
+
|
504
|
+
remove_instance_variable(:@timeout) if defined?(@timeout)
|
505
|
+
end
|
506
|
+
|
454
507
|
def handle_response
|
455
508
|
@inflight -= 1
|
456
509
|
return unless @inflight.zero?
|
@@ -490,12 +543,14 @@ module HTTPX
|
|
490
543
|
def handle_error(error)
|
491
544
|
parser.handle_error(error) if @parser && parser.respond_to?(:handle_error)
|
492
545
|
while (request = @pending.shift)
|
493
|
-
|
546
|
+
response = ErrorResponse.new(request, error, @options)
|
547
|
+
request.response = response
|
548
|
+
request.emit(:response, response)
|
494
549
|
end
|
495
550
|
end
|
496
551
|
|
497
552
|
def total_timeout
|
498
|
-
total = @options.timeout
|
553
|
+
total = @options.timeout[:total_timeout]
|
499
554
|
|
500
555
|
return unless total
|
501
556
|
|