httpx 1.6.2 → 1.7.0
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/doc/release_notes/0_11_0.md +3 -3
- data/doc/release_notes/1_6_3.md +47 -0
- data/doc/release_notes/1_7_0.md +149 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/sentry.rb +1 -1
- data/lib/httpx/altsvc.rb +3 -1
- data/lib/httpx/connection/http1.rb +14 -15
- data/lib/httpx/connection/http2.rb +16 -15
- data/lib/httpx/connection.rb +118 -110
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/extensions.rb +0 -14
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +1 -1
- data/lib/httpx/loggable.rb +14 -2
- data/lib/httpx/options.rb +60 -17
- data/lib/httpx/plugins/auth/digest.rb +44 -4
- data/lib/httpx/plugins/auth.rb +87 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +0 -1
- data/lib/httpx/plugins/callbacks.rb +15 -1
- data/lib/httpx/plugins/cookies/cookie.rb +1 -0
- data/lib/httpx/plugins/digest_auth.rb +4 -5
- data/lib/httpx/plugins/fiber_concurrency.rb +16 -1
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -1
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/internal_telemetry.rb +1 -1
- data/lib/httpx/plugins/ntlm_auth.rb +5 -3
- data/lib/httpx/plugins/oauth.rb +162 -56
- data/lib/httpx/plugins/proxy/http.rb +37 -9
- data/lib/httpx/plugins/rate_limiter.rb +2 -2
- data/lib/httpx/plugins/response_cache/file_store.rb +1 -0
- data/lib/httpx/plugins/response_cache.rb +16 -9
- data/lib/httpx/plugins/retries.rb +55 -16
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/stream.rb +59 -8
- data/lib/httpx/plugins/stream_bidi.rb +87 -22
- data/lib/httpx/pool.rb +65 -21
- data/lib/httpx/request.rb +13 -14
- data/lib/httpx/resolver/https.rb +100 -34
- data/lib/httpx/resolver/multi.rb +12 -27
- data/lib/httpx/resolver/native.rb +68 -38
- data/lib/httpx/resolver/resolver.rb +46 -29
- data/lib/httpx/resolver/system.rb +63 -39
- data/lib/httpx/resolver.rb +97 -29
- data/lib/httpx/response/body.rb +2 -0
- data/lib/httpx/response.rb +22 -6
- data/lib/httpx/selector.rb +44 -20
- data/lib/httpx/session.rb +23 -33
- data/lib/httpx/transcoder/body.rb +1 -1
- data/lib/httpx/transcoder/deflate.rb +13 -8
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/transcoder/multipart/decoder.rb +4 -4
- data/lib/httpx/transcoder/multipart/encoder.rb +1 -1
- data/lib/httpx/transcoder/multipart.rb +16 -8
- data/lib/httpx/transcoder/utils/body_reader.rb +1 -2
- data/lib/httpx/transcoder/utils/deflater.rb +1 -2
- data/lib/httpx/transcoder.rb +4 -6
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +3 -0
- data/sig/chainable.rbs +3 -3
- data/sig/connection.rbs +13 -6
- data/sig/loggable.rbs +5 -1
- data/sig/options.rbs +6 -2
- data/sig/plugins/auth/digest.rbs +6 -0
- data/sig/plugins/auth.rbs +28 -4
- data/sig/plugins/basic_auth.rbs +3 -3
- data/sig/plugins/callbacks.rbs +3 -0
- data/sig/plugins/digest_auth.rbs +2 -4
- data/sig/plugins/fiber_concurrency.rbs +6 -0
- data/sig/plugins/ntlm_auth.rbs +2 -2
- data/sig/plugins/oauth.rbs +46 -15
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache/file_store.rbs +2 -0
- data/sig/plugins/response_cache.rbs +4 -0
- data/sig/plugins/retries.rbs +8 -2
- data/sig/plugins/stream.rbs +13 -3
- data/sig/plugins/stream_bidi.rbs +5 -7
- data/sig/pool.rbs +1 -1
- data/sig/resolver/https.rbs +7 -0
- data/sig/resolver/multi.rbs +2 -9
- data/sig/resolver/native.rbs +1 -1
- data/sig/resolver/resolver.rbs +9 -8
- data/sig/resolver/system.rbs +4 -2
- data/sig/resolver.rbs +12 -3
- data/sig/response.rbs +3 -0
- data/sig/selector.rbs +2 -0
- data/sig/session.rbs +8 -8
- data/sig/transcoder/multipart.rbs +4 -2
- data/sig/transcoder.rbs +5 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2af63a63fe08211db58764570618b2e7c234d3473a28fc1ad6f5a31c3d0fb13d
|
|
4
|
+
data.tar.gz: 6b2671b85ac69e4817b8b764dfdabf638dacf94cfdcab6c44627ad64a57bcb50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50a2d2d3c0cb27f3bf84cc34553b06bd9fadf46ad789f9cfa08091cc458ac07751cb5593e01d242d88e444f10ac2b3014ea3e4e56f7c616ebc860517d08ef90d
|
|
7
|
+
data.tar.gz: 6b15f21262e85639c6d32b9851926d7e717b6f9bcbc226b9af3872d67461799fd24847a2d563dfbad1967d5349d371c18a1a6e1a28c721c5f33387f2fc1446a7
|
data/doc/release_notes/0_11_0.md
CHANGED
|
@@ -21,7 +21,7 @@ stub_http_request(:get, "https://www.google.com").and_return(status: 200, body:
|
|
|
21
21
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Read more about it in the [webmock integration documentation](https://
|
|
24
|
+
Read more about it in the [webmock integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter).
|
|
25
25
|
|
|
26
26
|
### Datadog Adapter
|
|
27
27
|
|
|
@@ -40,7 +40,7 @@ A trace will be emitted for every request, so this should be an interesting visu
|
|
|
40
40
|
|
|
41
41
|
Customization options and traces are similar to what [the net-http adapter provides](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#nethttp).
|
|
42
42
|
|
|
43
|
-
Read more about it in the [datadog integration documentation](https://
|
|
43
|
+
Read more about it in the [datadog integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Datadog-Adapter).
|
|
44
44
|
|
|
45
45
|
## Improvements
|
|
46
46
|
|
|
@@ -52,7 +52,7 @@ Read more about it in the [datadog integration documentation](https://os85.gitla
|
|
|
52
52
|
HTTPX.plugin(:multipart).post(uri, form: {file: File.new("path/to/file")})
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Read more about it in the [multipart plugin documentation](https://
|
|
55
|
+
Read more about it in the [multipart plugin documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
|
|
56
56
|
|
|
57
57
|
### Expect Plugin
|
|
58
58
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 1.6.3
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
* allow redacting only headers, or only the body, when using `debug_redact: :headers` or `debug_redact: :body` respectively.
|
|
6
|
+
|
|
7
|
+
## Improvements
|
|
8
|
+
|
|
9
|
+
* `system` resolver now works in a non-blocking manner, initiating the dns query in a separate thread and waiting on the pipe after that (it was blocking the main thread during resolution before).
|
|
10
|
+
* reduce allocation to a single shared option object when headers are passed as a session-level option, like `HTTPX.with(headers: headers).get(...)`
|
|
11
|
+
* favour using `String#replace` in buffer operations (instead of "clean-then-append").
|
|
12
|
+
* using `Array#unshift` instead of `Array#concat` in order to ensure that request ordering is respected in the face of an in-between error which requires reconnect-and-resend.
|
|
13
|
+
* replaced more internal callback indirection with plain method calls.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Bugfixes
|
|
17
|
+
|
|
18
|
+
* https: prevent modification of the ssl context object when performing a reconnection.
|
|
19
|
+
* compression: do not return early if the decompression buffer yields an empty string (more frequent under jruby 10).
|
|
20
|
+
* response cache: take query params into account when caching or retrieving cached responses.
|
|
21
|
+
* response cache: do not decompress cached responses on body consumption (the response bodies are cached in plaintext).
|
|
22
|
+
* native resolver: pick next timeout associated with the hostname being resolved (and not the hostnames in the queue).
|
|
23
|
+
* pool: assume that, even when signalled that a connection is available, context may be switched to a session which also checks the same connection out, before it's able to pick it up; in such a case, start from the beginning, until the pool timeout expires.
|
|
24
|
+
* session: forego bookkeeping when a connection is coalesced (instead, allow it to be dropped).
|
|
25
|
+
* digest_auth: make sure that an array is sent back if the probe response fails.
|
|
26
|
+
* alt-svc: when alt-svc handshake happens with more in-flight requests, defer termination to when these requests are made.
|
|
27
|
+
* http2: fix use of unexisting var `ex` when processing the connection closed callback.
|
|
28
|
+
* connection: fix potential session dereferencing, which allowed connections to be used across sessions, therefore bypassing needed synchronization and leading to the `undefined method 'after' for nil:NilClass` error.
|
|
29
|
+
* selector: close only selected connections (instead of all selectable connections) when an error occurs during IO readiness wait calls.
|
|
30
|
+
* resolvers: correctly propagate abrupt termination errors to the connection objects waiting for the answer.
|
|
31
|
+
* resolvers: when errors happenm force-close unresolved connections (and ensure they're both pinned to the corresponding session before the error happens, and are unpinned after error is propagated).
|
|
32
|
+
* resolvers: ensure resolvers transition to "closed" state, on all cases, when any error happens.
|
|
33
|
+
* resolvers: ensure that the next hostname is resolved when a timeout happens on the current one.
|
|
34
|
+
* native resolver: fixed duplication of the hostname to resolve in the list of candidates.
|
|
35
|
+
* https resolver: use a `system` resolver to resolve the DoH server hostname (instead of rerouting it to itself).
|
|
36
|
+
* https resolver: skip loop error reporting when error happens outside of it.
|
|
37
|
+
* https resolver: close connection on resolve errors, which prevents it from being around in the pool after termination; also deactivate it after successful use.
|
|
38
|
+
* multi resolver: do not check resolvers back into the pool if it's a multi resolver and the peer is still resolving (and do the check outside of the critical area).
|
|
39
|
+
* sentry adapter: removed usage of deprecated method which has been removed in sentry-ruby 6.0.0.
|
|
40
|
+
* selector: when coalescing connections, pin the current session before merging connections, to prevent it from registering in a selector being used in a different thread, and inadvertedly allowing it to be used across threads.
|
|
41
|
+
* session: fix: always pin connection before early-or-lazy resolution (fixes connection pool accounting under connection coalescing).
|
|
42
|
+
|
|
43
|
+
## Chores
|
|
44
|
+
|
|
45
|
+
* logging emits a timestamp as well (to monitor timeouts).
|
|
46
|
+
* `:stream_bidi` plugin: extends HTTP2 module by using plugin extensions.
|
|
47
|
+
* connection: remove session/selector references when closing a connection (prevents leaking them beyond the usage scope).
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# 1.7.0
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
### All AUTH plugin improvements!!
|
|
6
|
+
|
|
7
|
+
#### `:auth`
|
|
8
|
+
|
|
9
|
+
The `:auth` plugin can now be used with a dynamic callable object (methods, procs...) to generate the token.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# static token, pre 1.7.0
|
|
13
|
+
HTTPX.plugin(:auth).authorization("API-TOKEN")
|
|
14
|
+
# dynamically generate token!
|
|
15
|
+
HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The `.authorization` method is now syntactic sugar for a new option, `:auth_header_value`, which can be used directly, alongside a `:auth_header_type`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
HTTPX.plugin(:auth).authorization("API-TOKEN")
|
|
22
|
+
HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
|
|
23
|
+
HTTPX.plugin(:auth).authorization("Bearer API-TOKEN")
|
|
24
|
+
# same as
|
|
25
|
+
HTTPX.plugin(:auth, auth_header_value: "API-TOKEN")
|
|
26
|
+
HTTPX.plugin(:auth, auth_header_value: -> { generate_new_ephemeral_token })
|
|
27
|
+
HTTPX.plugin(:auth, auth_header_type: "Bearer", auth_header_value: "API-TOKEN")
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A new option `:generate_auth_value_on_retry` (which can be passed a callable receiving a response object) is now available; when used alongside the `:retries` plugin, it'll use the callable passed to the `.authorization` method to generate a new token before retrying the request:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
authed = HTTPX.plugin(:retries).plugin(:auth, generate_auth_value_on_retry: ->(res) {
|
|
34
|
+
res.status == 401
|
|
35
|
+
}).authorization { generate_new_ephemeral_token }
|
|
36
|
+
authed.get("https://example.com")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Auth).
|
|
40
|
+
|
|
41
|
+
#### `:oauth`
|
|
42
|
+
|
|
43
|
+
The `:oauth` plugin implementation was revamped to make use of the `:auth` plugin new functionality, in order to make managing an oauth session more seamless.
|
|
44
|
+
|
|
45
|
+
Take the following example:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
session = HTTPX.plugin(:oauth).with_oauth_options(
|
|
49
|
+
issuer: server.origin,
|
|
50
|
+
client_id: "CLIENT_ID",
|
|
51
|
+
client_secret: "SECRET",
|
|
52
|
+
)
|
|
53
|
+
session.get("https://example.com") #=> will load server metadata, request an access token, and perform the request with the access token.
|
|
54
|
+
# 2 hours later...
|
|
55
|
+
session.get("https://example.com")
|
|
56
|
+
# it'll reuse the same acces token, and if the request fails with 401, it'll request a new
|
|
57
|
+
# access token using the refresh token grant (when supported by the token issuer), and
|
|
58
|
+
# reperform the original request with the new access token.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A new option, `:oauth_options`, is now available. The same parameters previously supported by the `:oauth_session` options are supported.
|
|
62
|
+
|
|
63
|
+
The following components are therefore deprecated and scheduled for removal in a future major version:
|
|
64
|
+
|
|
65
|
+
* `:oauth_session` option
|
|
66
|
+
* `.oauth_auth` session method
|
|
67
|
+
* `.with_access_token` session method
|
|
68
|
+
|
|
69
|
+
#### `:bearer_auth`, `:digest_auth`; `:ntlm_auth`
|
|
70
|
+
|
|
71
|
+
The `:auth` plugin is now the foundation of each of these plugins, which haven't suffered major API changes.
|
|
72
|
+
|
|
73
|
+
Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/OAuth).
|
|
74
|
+
|
|
75
|
+
### `:retries` plugin: `:retry_after` backoff algorithms
|
|
76
|
+
|
|
77
|
+
The `:retries` plugins supports two new possible values for the `:retry_after` option: `:exponential_backoff` and `:polynomial_backoff`. They'll implement the respective calculation per each retry of a given request.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
# will wait 1, 2, 4, 8, 16 seconds... depending of how many retries it can wait for
|
|
81
|
+
session = HTTPX.plugin(:retries, retry_after: :exponential_backoff)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Read more about it in the [retries plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Retries).
|
|
85
|
+
|
|
86
|
+
### Ractor compatibility
|
|
87
|
+
|
|
88
|
+
`httpx` can be used within a ractor:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
# ruby 4.0 syntax
|
|
92
|
+
response = Ractor.new(uri) do |uri|
|
|
93
|
+
HTTPX.get(uri)
|
|
94
|
+
end.value
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Bear in mind that, if you're connection via HTTPS, you'll need make sure you're using version 4.0 or higher of the `openssl` gem.
|
|
98
|
+
|
|
99
|
+
The test suite isn't exhaustive for ractors yet, but most plugins should also be ractor-compatible. If they don't work, that's a bug, and you're recommended to report it.
|
|
100
|
+
|
|
101
|
+
## Improvements
|
|
102
|
+
|
|
103
|
+
* When encoding the `:json` param to send it as `application/json` payload, (example: `HTTPX.post("https://example.com", json: { foo: "bar })`), and the method uses the `json` standard library, it'll use `JSON.generate` (instead of `JSON.dump`) to encode the JSON payload. The reason is that, unlike `JSON.dump`, it doesn't rely on access to a global mutable hash, and is therefore ractor-safe.
|
|
104
|
+
* `:stream` plugin: the stream response class (the object that is returned in request calls is a stream response) can be extended now. You can add a `StreamResponseMethods` method to your plugin. Read more about it in the documentation.
|
|
105
|
+
* The resolver name cache (used by the native and https resolvers) was remade into a LRU cache, and will therefore not keep on growing when `httpx` is used to connect to a huge number of hostnames in a process.
|
|
106
|
+
* the native and https DNS resolvers will ignore answers with SERVFAIL code while there are retries left (some resolvers use such error code for rate limiting).
|
|
107
|
+
* `:timeout` option values are now validated, and an error is raised when passing an unrecognized timeout option (which is a good layer of protection for typos).
|
|
108
|
+
* pool: try passing the scheduler to a thread waiting on a connection, to avoid the current case where a connection may be checked-in-then-immediately-out-after when doing multiple requests in a loop, never giving a chance to others and potentially making the pool time out.
|
|
109
|
+
* headers deep-freeze and dup.
|
|
110
|
+
|
|
111
|
+
## Bugfixes
|
|
112
|
+
|
|
113
|
+
* recover and close connection when an `IOError` is raised while waiting for IO readiness (could cause busy loops during HTTP/2 termination handshake).
|
|
114
|
+
* `:stream_bidi` plugin: improve thread-safety of buffer operations when the session is used from multiple threads.
|
|
115
|
+
* `:stream_bidi` plugin: added missing methods to signal in order to comply with the Selectable API (it was reported as raising `NoMethodError` under certain conditions).
|
|
116
|
+
* `:stream_bidi` plugin: can support non-bidirectional stream requests using the same session.
|
|
117
|
+
* `:stream` plugin: is now compatible with fiber scheduler engines (via the `:fiber_concurrency` plugin).
|
|
118
|
+
* `:stream` plugin: make sure that stream long-running requests do not share the same connection as regular threads.
|
|
119
|
+
* `:digest_auth` plugin: can now support qop values wrapped inside parentheses in the `www-authenticate` header (i.e. `qop="('auth',)"`).
|
|
120
|
+
* https resolver: handle 3XX redirect responses in HTTP DNS queries.
|
|
121
|
+
* https resolver: do not close HTTP connections whhich are shared across AAAA and A resolution paths when its in use by one of them.
|
|
122
|
+
* fix access to private method from `http-2` which was made public in more recent versions, but not in older still-supported versions.
|
|
123
|
+
* fixed resolver log message using a "connection" label.
|
|
124
|
+
* `HTTPX::Response.copy_to` will explicitly close the response at the end; given that the body file can be moved as a result, there is no guarantee that the response is still usable, so might as well just close it altogether.
|
|
125
|
+
* selector: avoid skipping persistent connections in the selector to deactivate due to iterate-and-modify.
|
|
126
|
+
|
|
127
|
+
## Breaking Changes
|
|
128
|
+
|
|
129
|
+
### `:digest_auth` error
|
|
130
|
+
|
|
131
|
+
The main error class for the `:digest_auth` plugin has been moved to a different location. If you were rescuing the `HTTPX::Plugins::DigestAuth::DigestError` error, you should now point to the `HTTPX::Authentication::Digest::Error`.
|
|
132
|
+
|
|
133
|
+
### `:stream` plugin: `build_request` should receive `stream: true` for stream requests
|
|
134
|
+
|
|
135
|
+
In case you're building request objects before passing them to the session, you're now forced to create them with the `:stream` option on:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
session = HTTPX.plugin(:stream)
|
|
139
|
+
|
|
140
|
+
# before
|
|
141
|
+
req = session.build_request("GET", "https://example.com/stream")
|
|
142
|
+
session.request(req, stream: true)
|
|
143
|
+
|
|
144
|
+
# after
|
|
145
|
+
req = session.build_request("GET", "https://example.com/stream", stream: true)
|
|
146
|
+
session.request(req)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Previous code may still work in a few cases, but it is not guaranteed to work on all cases.
|
|
@@ -80,7 +80,7 @@ module Datadog::Tracing
|
|
|
80
80
|
else
|
|
81
81
|
span.set_tag(TAG_STATUS_CODE, response.status.to_s)
|
|
82
82
|
|
|
83
|
-
span.set_error(::HTTPX::HTTPError.new(response)) if response.status
|
|
83
|
+
span.set_error(::HTTPX::HTTPError.new(response)) if response.status.between?(400, 599)
|
|
84
84
|
|
|
85
85
|
span.set_tags(
|
|
86
86
|
Datadog.configuration.tracing.header_tags.response_tags(response.headers.to_h)
|
|
@@ -32,7 +32,7 @@ module HTTPX::Plugins
|
|
|
32
32
|
|
|
33
33
|
return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }
|
|
34
34
|
|
|
35
|
-
trace =
|
|
35
|
+
trace = sentry_span.to_sentry_trace
|
|
36
36
|
request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
|
|
37
37
|
end
|
|
38
38
|
|
data/lib/httpx/altsvc.rb
CHANGED
|
@@ -8,6 +8,8 @@ module HTTPX
|
|
|
8
8
|
module ConnectionMixin
|
|
9
9
|
using URIExtensions
|
|
10
10
|
|
|
11
|
+
H2_ALTSVC_SCHEMES = %w[https h2].freeze
|
|
12
|
+
|
|
11
13
|
def send(request)
|
|
12
14
|
request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
|
|
13
15
|
|
|
@@ -46,7 +48,7 @@ module HTTPX
|
|
|
46
48
|
uri.origin == other_uri.origin || begin
|
|
47
49
|
case uri.scheme
|
|
48
50
|
when "h2"
|
|
49
|
-
(other_uri.scheme
|
|
51
|
+
H2_ALTSVC_SCHEMES.include?(other_uri.scheme) &&
|
|
50
52
|
uri.host == other_uri.host &&
|
|
51
53
|
uri.port == other_uri.port
|
|
52
54
|
else
|
|
@@ -10,6 +10,11 @@ module HTTPX
|
|
|
10
10
|
MAX_REQUESTS = 200
|
|
11
11
|
CRLF = "\r\n"
|
|
12
12
|
|
|
13
|
+
UPCASED = {
|
|
14
|
+
"www-authenticate" => "WWW-Authenticate",
|
|
15
|
+
"http2-settings" => "HTTP2-Settings",
|
|
16
|
+
"content-md5" => "Content-MD5",
|
|
17
|
+
}.freeze
|
|
13
18
|
attr_reader :pending, :requests
|
|
14
19
|
|
|
15
20
|
attr_accessor :max_concurrent_requests
|
|
@@ -44,12 +49,12 @@ module HTTPX
|
|
|
44
49
|
@max_requests = @options.max_requests || MAX_REQUESTS
|
|
45
50
|
@parser.reset!
|
|
46
51
|
@handshake_completed = false
|
|
47
|
-
@pending.
|
|
52
|
+
@pending.unshift(*@requests)
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def close
|
|
51
56
|
reset
|
|
52
|
-
emit(:close
|
|
57
|
+
emit(:close)
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
def exhausted?
|
|
@@ -114,7 +119,7 @@ module HTTPX
|
|
|
114
119
|
@parser.http_version.join("."),
|
|
115
120
|
headers)
|
|
116
121
|
log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
|
|
117
|
-
log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{
|
|
122
|
+
log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v)}" }.join("\n") }
|
|
118
123
|
|
|
119
124
|
@request.response = response
|
|
120
125
|
on_complete if response.finished?
|
|
@@ -126,7 +131,7 @@ module HTTPX
|
|
|
126
131
|
response = @request.response
|
|
127
132
|
log(level: 2) { "trailer headers received" }
|
|
128
133
|
|
|
129
|
-
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{
|
|
134
|
+
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v.join(", "))}" }.join("\n") }
|
|
130
135
|
response.merge_headers(h)
|
|
131
136
|
end
|
|
132
137
|
|
|
@@ -136,7 +141,7 @@ module HTTPX
|
|
|
136
141
|
return unless request
|
|
137
142
|
|
|
138
143
|
log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
|
139
|
-
log(level: 2, color: :green) { "-> #{
|
|
144
|
+
log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
|
|
140
145
|
response = request.response
|
|
141
146
|
|
|
142
147
|
response << chunk
|
|
@@ -182,7 +187,7 @@ module HTTPX
|
|
|
182
187
|
end
|
|
183
188
|
|
|
184
189
|
if exhausted?
|
|
185
|
-
@pending.
|
|
190
|
+
@pending.unshift(*@requests)
|
|
186
191
|
@requests.clear
|
|
187
192
|
|
|
188
193
|
emit(:exhausted)
|
|
@@ -236,7 +241,7 @@ module HTTPX
|
|
|
236
241
|
when /keep-alive/i
|
|
237
242
|
if @handshake_completed
|
|
238
243
|
if @max_requests.zero?
|
|
239
|
-
@pending.
|
|
244
|
+
@pending.unshift(*@requests)
|
|
240
245
|
@requests.clear
|
|
241
246
|
emit(:exhausted)
|
|
242
247
|
end
|
|
@@ -360,7 +365,7 @@ module HTTPX
|
|
|
360
365
|
|
|
361
366
|
while (chunk = request.drain_body)
|
|
362
367
|
log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
|
|
363
|
-
log(level: 2, color: :green) { "<- #{
|
|
368
|
+
log(level: 2, color: :green) { "<- #{log_redact_body(chunk.inspect)}" }
|
|
364
369
|
@buffer << chunk
|
|
365
370
|
throw(:buffer_full, request) if @buffer.full?
|
|
366
371
|
end
|
|
@@ -381,17 +386,11 @@ module HTTPX
|
|
|
381
386
|
def join_headers2(headers)
|
|
382
387
|
headers.each do |field, value|
|
|
383
388
|
field = capitalized(field)
|
|
384
|
-
log(color: :yellow) { "<- HEADER: #{[field,
|
|
389
|
+
log(color: :yellow) { "<- HEADER: #{[field, log_redact_headers(value)].join(": ")}" }
|
|
385
390
|
@buffer << "#{field}: #{value}#{CRLF}"
|
|
386
391
|
end
|
|
387
392
|
end
|
|
388
393
|
|
|
389
|
-
UPCASED = {
|
|
390
|
-
"www-authenticate" => "WWW-Authenticate",
|
|
391
|
-
"http2-settings" => "HTTP2-Settings",
|
|
392
|
-
"content-md5" => "Content-MD5",
|
|
393
|
-
}.freeze
|
|
394
|
-
|
|
395
394
|
def capitalized(field)
|
|
396
395
|
UPCASED[field] || field.split("-").map(&:capitalize).join("-")
|
|
397
396
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "securerandom"
|
|
4
4
|
require "http/2"
|
|
5
5
|
|
|
6
|
+
HTTP2::Connection.__send__(:public, :send_buffer) if HTTP2::VERSION < "1.1.1"
|
|
7
|
+
|
|
6
8
|
module HTTPX
|
|
7
9
|
class Connection::HTTP2
|
|
8
10
|
include Callbacks
|
|
@@ -89,7 +91,7 @@ module HTTPX
|
|
|
89
91
|
@connection.goaway
|
|
90
92
|
emit(:timeout, @options.timeout[:close_handshake_timeout])
|
|
91
93
|
end
|
|
92
|
-
emit(:close
|
|
94
|
+
emit(:close)
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
def empty?
|
|
@@ -234,12 +236,12 @@ module HTTPX
|
|
|
234
236
|
extra_headers = set_protocol_headers(request)
|
|
235
237
|
|
|
236
238
|
if request.headers.key?("host")
|
|
237
|
-
log { "forbidden \"host\" header found (#{
|
|
239
|
+
log { "forbidden \"host\" header found (#{log_redact_headers(request.headers["host"])}), will use it as authority..." }
|
|
238
240
|
extra_headers[":authority"] = request.headers["host"]
|
|
239
241
|
end
|
|
240
242
|
|
|
241
243
|
log(level: 1, color: :yellow) do
|
|
242
|
-
"\n#{request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{
|
|
244
|
+
"\n#{request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")}"
|
|
243
245
|
end
|
|
244
246
|
stream.headers(request.headers.each(extra_headers), end_stream: request.body.empty?)
|
|
245
247
|
end
|
|
@@ -251,7 +253,7 @@ module HTTPX
|
|
|
251
253
|
end
|
|
252
254
|
|
|
253
255
|
log(level: 1, color: :yellow) do
|
|
254
|
-
request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{
|
|
256
|
+
request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
|
|
255
257
|
end
|
|
256
258
|
stream.headers(request.trailers.each, end_stream: true)
|
|
257
259
|
end
|
|
@@ -279,7 +281,7 @@ module HTTPX
|
|
|
279
281
|
|
|
280
282
|
def send_chunk(request, stream, chunk, next_chunk)
|
|
281
283
|
log(level: 1, color: :green) { "#{stream.id}: -> DATA: #{chunk.bytesize} bytes..." }
|
|
282
|
-
log(level: 2, color: :green) { "#{stream.id}: -> #{
|
|
284
|
+
log(level: 2, color: :green) { "#{stream.id}: -> #{log_redact_body(chunk.inspect)}" }
|
|
283
285
|
stream.data(chunk, end_stream: end_stream?(request, next_chunk))
|
|
284
286
|
end
|
|
285
287
|
|
|
@@ -300,7 +302,7 @@ module HTTPX
|
|
|
300
302
|
end
|
|
301
303
|
|
|
302
304
|
log(color: :yellow) do
|
|
303
|
-
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{
|
|
305
|
+
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
|
|
304
306
|
end
|
|
305
307
|
_, status = h.shift
|
|
306
308
|
headers = request.options.headers_class.new(h)
|
|
@@ -313,14 +315,14 @@ module HTTPX
|
|
|
313
315
|
|
|
314
316
|
def on_stream_trailers(stream, response, h)
|
|
315
317
|
log(color: :yellow) do
|
|
316
|
-
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{
|
|
318
|
+
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
|
|
317
319
|
end
|
|
318
320
|
response.merge_headers(h)
|
|
319
321
|
end
|
|
320
322
|
|
|
321
323
|
def on_stream_data(stream, request, data)
|
|
322
324
|
log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
|
|
323
|
-
log(level: 2, color: :green) { "#{stream.id}: <- #{
|
|
325
|
+
log(level: 2, color: :green) { "#{stream.id}: <- #{log_redact_body(data.inspect)}" }
|
|
324
326
|
request.response << data
|
|
325
327
|
end
|
|
326
328
|
|
|
@@ -387,18 +389,17 @@ module HTTPX
|
|
|
387
389
|
end
|
|
388
390
|
else
|
|
389
391
|
ex = GoawayError.new(error)
|
|
392
|
+
ex.set_backtrace(caller)
|
|
393
|
+
|
|
390
394
|
@pending.unshift(*@streams.keys)
|
|
391
395
|
teardown
|
|
392
|
-
end
|
|
393
396
|
|
|
394
|
-
if ex
|
|
395
|
-
ex.set_backtrace(caller)
|
|
396
397
|
handle_error(ex)
|
|
397
398
|
end
|
|
398
399
|
end
|
|
399
400
|
return unless is_connection_closed && @streams.empty?
|
|
400
401
|
|
|
401
|
-
emit(:close
|
|
402
|
+
emit(:close) if is_connection_closed
|
|
402
403
|
end
|
|
403
404
|
|
|
404
405
|
def on_frame_sent(frame)
|
|
@@ -409,7 +410,7 @@ module HTTPX
|
|
|
409
410
|
when :data
|
|
410
411
|
frame.merge(payload: frame[:payload].bytesize)
|
|
411
412
|
when :headers, :ping
|
|
412
|
-
frame.merge(payload:
|
|
413
|
+
frame.merge(payload: log_redact_headers(frame[:payload]))
|
|
413
414
|
else
|
|
414
415
|
frame
|
|
415
416
|
end
|
|
@@ -425,7 +426,7 @@ module HTTPX
|
|
|
425
426
|
when :data
|
|
426
427
|
frame.merge(payload: frame[:payload].bytesize)
|
|
427
428
|
when :headers, :ping
|
|
428
|
-
frame.merge(payload:
|
|
429
|
+
frame.merge(payload: log_redact_headers(frame[:payload]))
|
|
429
430
|
else
|
|
430
431
|
frame
|
|
431
432
|
end
|
|
@@ -435,7 +436,7 @@ module HTTPX
|
|
|
435
436
|
|
|
436
437
|
def on_altsvc(origin, frame)
|
|
437
438
|
log(level: 2) { "#{frame[:stream]}: altsvc frame was received" }
|
|
438
|
-
log(level: 2) { "#{frame[:stream]}: #{
|
|
439
|
+
log(level: 2) { "#{frame[:stream]}: #{log_redact_headers(frame.inspect)}" }
|
|
439
440
|
alt_origin = URI.parse("#{frame[:proto]}://#{frame[:host]}:#{frame[:port]}")
|
|
440
441
|
params = { "ma" => frame[:max_age] }
|
|
441
442
|
emit(:altsvc, origin, alt_origin, origin, params)
|