httpx 1.7.8 → 1.8.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/doc/release_notes/1_7_1.md +1 -2
- data/doc/release_notes/1_8_0.md +100 -0
- data/doc/release_notes/1_8_1.md +27 -0
- data/lib/httpx/adapters/datadog.rb +12 -1
- data/lib/httpx/adapters/faraday.rb +2 -2
- data/lib/httpx/adapters/webmock.rb +6 -0
- data/lib/httpx/connection/http1.rb +15 -12
- data/lib/httpx/connection/http2.rb +69 -9
- data/lib/httpx/connection.rb +119 -21
- data/lib/httpx/errors.rb +8 -1
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +8 -9
- data/lib/httpx/io/tcp.rb +22 -5
- data/lib/httpx/io/unix.rb +2 -2
- data/lib/httpx/loggable.rb +11 -4
- data/lib/httpx/options.rb +16 -4
- data/lib/httpx/parser/http1.rb +8 -2
- data/lib/httpx/plugins/auth/digest.rb +1 -1
- data/lib/httpx/plugins/auth.rb +61 -4
- data/lib/httpx/plugins/{response_cache → cache}/file_store.rb +1 -1
- data/lib/httpx/plugins/{response_cache → cache}/store.rb +1 -1
- data/lib/httpx/plugins/cache.rb +221 -0
- data/lib/httpx/plugins/callbacks.rb +12 -0
- data/lib/httpx/plugins/digest_auth.rb +4 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +75 -4
- data/lib/httpx/plugins/ntlm_v2_auth.rb +92 -0
- data/lib/httpx/plugins/oauth.rb +66 -14
- data/lib/httpx/plugins/persistent.rb +18 -2
- data/lib/httpx/plugins/proxy.rb +5 -0
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/response_cache.rb +26 -105
- data/lib/httpx/plugins/retries.rb +7 -5
- data/lib/httpx/plugins/server_sent_events.rb +158 -0
- data/lib/httpx/plugins/ssrf_filter.rb +17 -2
- data/lib/httpx/plugins/stream.rb +9 -5
- data/lib/httpx/plugins/stream_bidi.rb +2 -0
- data/lib/httpx/plugins/tracing.rb +15 -4
- data/lib/httpx/pool.rb +2 -2
- data/lib/httpx/request/body.rb +2 -2
- data/lib/httpx/request.rb +42 -10
- data/lib/httpx/resolver/cache/base.rb +1 -8
- data/lib/httpx/resolver/cache/file.rb +56 -0
- data/lib/httpx/resolver/https.rb +20 -20
- data/lib/httpx/resolver/native.rb +21 -3
- data/lib/httpx/resolver/resolver.rb +4 -0
- data/lib/httpx/resolver/system.rb +5 -2
- data/lib/httpx/response/body.rb +6 -4
- data/lib/httpx/response.rb +12 -5
- data/lib/httpx/selector.rb +12 -1
- data/lib/httpx/session.rb +6 -1
- data/lib/httpx/session_extensions.rb +2 -2
- data/lib/httpx/timers.rb +3 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/chainable.rbs +3 -0
- data/sig/connection/http1.rbs +1 -1
- data/sig/connection/http2.rbs +4 -1
- data/sig/connection.rbs +16 -9
- data/sig/errors.rbs +9 -3
- data/sig/httpx.rbs +2 -0
- data/sig/io/tcp.rbs +2 -0
- data/sig/loggable.rbs +8 -0
- data/sig/options.rbs +25 -12
- data/sig/parser/http1.rbs +3 -1
- data/sig/plugins/auth/ntlm.rbs +1 -1
- data/sig/plugins/auth.rbs +4 -0
- data/sig/plugins/{response_cache → cache}/file_store.rbs +2 -2
- data/sig/plugins/{response_cache → cache}/store.rbs +2 -2
- data/sig/plugins/cache.rbs +69 -0
- data/sig/plugins/fiber_concurrency.rbs +4 -0
- data/sig/plugins/ntlm_v2_auth.rbs +36 -0
- data/sig/plugins/persistent.rbs +3 -0
- data/sig/plugins/response_cache.rbs +13 -38
- data/sig/plugins/retries.rbs +5 -5
- data/sig/plugins/server_sent_events.rbs +45 -0
- data/sig/plugins/ssrf_filter.rbs +5 -1
- data/sig/plugins/stream.rbs +1 -1
- data/sig/plugins/stream_bidi.rbs +0 -2
- data/sig/plugins/webdav.rbs +1 -1
- data/sig/pool.rbs +2 -2
- data/sig/request.rbs +9 -6
- data/sig/resolver/cache/file.rbs +13 -0
- data/sig/resolver/entry.rbs +1 -1
- data/sig/resolver/https.rbs +3 -3
- data/sig/resolver/multi.rbs +1 -1
- data/sig/resolver/native.rbs +5 -5
- data/sig/resolver/resolver.rbs +1 -3
- data/sig/resolver/system.rbs +5 -2
- data/sig/resolver.rbs +3 -0
- data/sig/response.rbs +3 -0
- data/sig/selector.rbs +13 -8
- data/sig/timers.rbs +7 -5
- data/sig/transcoder/body.rbs +1 -1
- data/sig/transcoder/gzip.rbs +3 -2
- data/sig/transcoder/multipart.rbs +4 -1
- data/sig/transcoder/utils/deflater.rbs +2 -0
- data/sig/transcoder.rbs +2 -0
- data/sig/utils.rbs +1 -1
- metadata +19 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eece37bd091a7fea17ccc62eca63acaee714c08658d081a938fc38495a4c8813
|
|
4
|
+
data.tar.gz: 72dcd038803106f08a0317c8d7bc97d22d6cdf5c469ba8f8f9d380d00e8f5ea7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5af43571f7da30f709883474fb61ef3661b31fa836d249c423726f4815fb7b2bf5c1281e22ca7ad15c00ed26b2ffc950d521b14a9f5c02e7d95d4198b4b986d0
|
|
7
|
+
data.tar.gz: bd73537be9a610b590d7897cd96f7b6213ece24ea7079ce995a3e05cbee363dcf0f575405ea8d2e612a64737e3bbc70dbdc25ab9a24f5b7e154b9a5187974ffe
|
data/doc/release_notes/1_7_1.md
CHANGED
|
@@ -17,5 +17,4 @@
|
|
|
17
17
|
* `:stream_bidi` plugin: fix internal state preventing bidi requests from being retried.
|
|
18
18
|
* `:stream_bidi` plugin: will only allow initial request body being passed using `:body` param (others, like `:json`, will raise an exception)
|
|
19
19
|
* https resolver: return `:idle` on `#state` calls when no connection is available (sometimes called in internal log messages).
|
|
20
|
-
* selector loop fix: when there are no selectables and an interval is passed, sleep instead of returning (thereby avoiding potential busy loop).
|
|
21
|
-
*
|
|
20
|
+
* selector loop fix: when there are no selectables and an interval is passed, sleep instead of returning (thereby avoiding potential busy loop).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 1.8.0
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
### New plugins
|
|
6
|
+
|
|
7
|
+
#### `:server_sent_events` plugin
|
|
8
|
+
|
|
9
|
+
The `:server_sent_events` plugin provides a convenience API to deal with `text/event-stream` requests, on top of the `:stream` plugin.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
session = HTTPX.plugin(:server_sent_events)
|
|
13
|
+
|
|
14
|
+
sse_response = session.get("https://example.com/event-stream", event_stream: true)
|
|
15
|
+
|
|
16
|
+
sse_response.each_message do |message|
|
|
17
|
+
puts message.id
|
|
18
|
+
puts message.event
|
|
19
|
+
puts message.data
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can read more about it in https://gitlab.com/os85/httpx/wikis/Server-Sent-Events .
|
|
24
|
+
|
|
25
|
+
#### `:cache` plugin
|
|
26
|
+
|
|
27
|
+
The `:cache` plugin allows caching responses. It exposes some options to determine some of its functionality, i.e. whether a request can use a cached response, whether a response can be cached, whether a cached response is still valid, etc.
|
|
28
|
+
|
|
29
|
+
This functionality was extracted from the `:response_cache` plugin, which now uses it under the hood.
|
|
30
|
+
|
|
31
|
+
You can read more about it in https://gitlab.com/os85/httpx/wikis/Cache .
|
|
32
|
+
|
|
33
|
+
#### `:ntlm_v2_auth` plugin
|
|
34
|
+
|
|
35
|
+
The `:ntlm_v2_auth` plugin is now available. It implements the most recent version of the NTLM authentication scheme supported by Microsoft products.
|
|
36
|
+
|
|
37
|
+
You can read more about it in https://gitlab.com/os85/httpx/wikis/Auth#ntlm-v2-auth .
|
|
38
|
+
|
|
39
|
+
### New timeouts
|
|
40
|
+
|
|
41
|
+
#### `:total_request_timeout`
|
|
42
|
+
|
|
43
|
+
You can use the `:total_request_timeout` to time the time it takes a request to get its final response. This includes when your requests follows redirects (via the `:follow_redirects` plugin) or is retried multiple times (via the `:retries´ plugin).
|
|
44
|
+
|
|
45
|
+
#### `:ping_timeout`
|
|
46
|
+
|
|
47
|
+
Defines the number of seconds a connection waits to receive a ping response when probing for a connection for liveness.
|
|
48
|
+
|
|
49
|
+
Defaults to 2 seconds.
|
|
50
|
+
|
|
51
|
+
### `:ssrf_filter` plugin new options
|
|
52
|
+
|
|
53
|
+
#### `:extra_unsafe_ranges`
|
|
54
|
+
|
|
55
|
+
A list of extra unsafe IPs or IP ranges to the default deny list.
|
|
56
|
+
|
|
57
|
+
#### `:safe_private_ranges`
|
|
58
|
+
|
|
59
|
+
A list of IPs or IP ranges which are allowed and would otherwise be denied.
|
|
60
|
+
|
|
61
|
+
### `:auth` plugin new option
|
|
62
|
+
|
|
63
|
+
#### `:reset_auth_header_expires_in/at`
|
|
64
|
+
|
|
65
|
+
The `:reset_auth_header_expires_in` and `:reset_auth_header_expires_at` options enable discarding an authorization token an X number of seconds after it has been generated, or at a particuar point in time, respectively. This is useful when the token is dynamically generated, so that you can preemptively renegotiate a new one.
|
|
66
|
+
|
|
67
|
+
The `:oauth` plugin makes use of these fields, alongside the `expires_in` claim from token responses, to refresh the token as soon as the it expires.
|
|
68
|
+
|
|
69
|
+
### `:max_response_body_size`
|
|
70
|
+
|
|
71
|
+
Can be set to the maximum number of bytes a response may have, after which it'll return an `HTTPX::ErrorResponse`. The limit is enforced based on the `content-length` header and as bytes are received.
|
|
72
|
+
|
|
73
|
+
### `:max_response_headers`
|
|
74
|
+
|
|
75
|
+
Can be set to the maximum number of headers a response may have.
|
|
76
|
+
|
|
77
|
+
### `:max_response_header_value_size`
|
|
78
|
+
|
|
79
|
+
Can be set to the maximum number of bytes a header value may have. In cases where a header field may be spread across multiple entries (ex. `"cookie"`), the limit is enforced on the aggregate byte size.
|
|
80
|
+
|
|
81
|
+
### resolver file cache
|
|
82
|
+
|
|
83
|
+
By specifying `:file` as the resolver `:cache` option, the DNS entries will be cached in a known location in your file system. This will allow sharing entries across processes within the same machine to reduce overall DNS traffic. You can use it via:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
session = HTTPX.with(resolver_options: { cache: :file })
|
|
87
|
+
session.get("https://example.com")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Improvements
|
|
91
|
+
|
|
92
|
+
* `http-2` minimum version is now 1.2.0, which brings performance benefits around frame parsing and other common operations.
|
|
93
|
+
|
|
94
|
+
## Bugfixes
|
|
95
|
+
|
|
96
|
+
* several fixes to make `httpx` usable inside a fiber scheduler in ruby 4.
|
|
97
|
+
* `:proxy` plugin: unescape user/password from proxy options before reusing it (to avoid using percent-encoded values when p.ex. generating base64-encoding for basic auth).
|
|
98
|
+
* `:retries` plugin: fixed the polynomial and exponential backoff `:retry_after` strategies calculation.
|
|
99
|
+
* `:tracing` plugin: fixing span start time set up when request is sent to a closed connection, or when a long-lived connection will be probed for liveness.
|
|
100
|
+
* datadog adapter: fixed integration with the `datadog` gem v2.34 or higher.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 1.8.1
|
|
2
|
+
|
|
3
|
+
## Improvements
|
|
4
|
+
|
|
5
|
+
* the session will now eagerly write initial data to connections instead of waiting for I/O the first time (saves a syscall).
|
|
6
|
+
* removed all rescue blocks rescuing `StandardError` internally; this could interfere with usages of `httpx` inside `Timeout.timeout` blocks, which may emit an exception at any point, including in blocks where that exception could be rescued, leaving the session in a potentially irrecoverable state and looping.
|
|
7
|
+
* debug logs change the display of ids from 'tid:123, fid:123' to 'tid=123 fid:123'.
|
|
8
|
+
* and when using the `datadog` adapter, all related correlation ids (trace id, span id, etc) will also be added to the identifiers in the same format
|
|
9
|
+
* plugins built on top of the `:retries` plugin will skip the `retry_after` interval when certain conditions are met.
|
|
10
|
+
* `:persistent` plugin: request will be retried immediately, when the error happens after the keep alive interval expired.
|
|
11
|
+
* `:auth` plugin: request will be retried immediately if it failed due to a "401 Unauthorized Error".
|
|
12
|
+
* `:ssrf_filter` plugin: removed public/private references of exception message.
|
|
13
|
+
|
|
14
|
+
## Bugfixes
|
|
15
|
+
|
|
16
|
+
* `:auth` plugin: handle edge case where `:auth_header_expires_at` returns no value.
|
|
17
|
+
* `:digest_auth` plugin: do not retry if there was an issue building building the authorization header after the probe.
|
|
18
|
+
* `webmock` adapter: prevent mocked requests from opening a connection to the peer server that will not be used.
|
|
19
|
+
* clear and cancel request timers on completion or reset, ensuring that they won't be retained way past the point where the request is gone.
|
|
20
|
+
* connection: cancel and discard ping timer before closing (removes timer reference)
|
|
21
|
+
* connection: fix logic where purging-while-resending requests to a coalesced connection was leaving references in the old connection.
|
|
22
|
+
* moved an `on_body_chunk` callback to the `:callbacks` plugin, where it's used and expected to be defined.
|
|
23
|
+
* `http2`: avoid closing the connection on internal errors which are supposed to be specific to a request, such as the max headers or max response body size checks.
|
|
24
|
+
* system resolver will use an internal exception when using `Timeout.timeout` on resolutions to avoid conflicts with the default timeout exception class.
|
|
25
|
+
* prevent edge case where `#alpn_protocol` was being called on a `TCPSocket`.
|
|
26
|
+
* fixed v1.8.0 regression causing connection to loop when an error happens while connecting.
|
|
27
|
+
* resolver cache: fixed cache eviction logic, where alias answers weren't being evicted, causing resolutions to stall after a certain number of resolutions.
|
|
@@ -15,8 +15,10 @@ module Datadog::Tracing
|
|
|
15
15
|
|
|
16
16
|
TAG_BASE_SERVICE = if Gem::Version.new(DATADOG_VERSION::STRING) < Gem::Version.new("1.15.0")
|
|
17
17
|
"_dd.base_service"
|
|
18
|
-
|
|
18
|
+
elsif Gem::Version.new(DATADOG_VERSION::STRING) < Gem::Version.new("2.34.0")
|
|
19
19
|
Datadog::Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE
|
|
20
|
+
else
|
|
21
|
+
Datadog::Tracing::Metadata::Ext::TAG_BASE_SERVICE
|
|
20
22
|
end
|
|
21
23
|
TAG_PEER_HOSTNAME = Datadog::Tracing::Metadata::Ext::TAG_PEER_HOSTNAME
|
|
22
24
|
TAG_PEER_SERVICE = Datadog::Tracing::Metadata::Ext::TAG_PEER_SERVICE
|
|
@@ -183,6 +185,13 @@ module Datadog::Tracing
|
|
|
183
185
|
end
|
|
184
186
|
end
|
|
185
187
|
|
|
188
|
+
# patches httpx debug logs to include datadog correlation ids
|
|
189
|
+
module LoggablePatch
|
|
190
|
+
def log_identifiers
|
|
191
|
+
"#{super} #{Datadog::Tracing.log_correlation}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
186
195
|
module Configuration
|
|
187
196
|
# Default settings for httpx
|
|
188
197
|
#
|
|
@@ -295,6 +304,8 @@ module Datadog::Tracing
|
|
|
295
304
|
|
|
296
305
|
::HTTPX.send(:remove_const, :Session)
|
|
297
306
|
::HTTPX.send(:const_set, :Session, datadog_session.class)
|
|
307
|
+
|
|
308
|
+
::HTTPX::Loggable.singleton_class.prepend(LoggablePatch)
|
|
298
309
|
end
|
|
299
310
|
end
|
|
300
311
|
|
|
@@ -14,6 +14,7 @@ module HTTPX
|
|
|
14
14
|
"www-authenticate" => "WWW-Authenticate",
|
|
15
15
|
"http2-settings" => "HTTP2-Settings",
|
|
16
16
|
"content-md5" => "Content-MD5",
|
|
17
|
+
"last-event-id" => "Last-Event-ID",
|
|
17
18
|
}.freeze
|
|
18
19
|
attr_reader :pending, :requests
|
|
19
20
|
|
|
@@ -23,7 +24,7 @@ module HTTPX
|
|
|
23
24
|
@options = options
|
|
24
25
|
@max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
|
|
25
26
|
@max_requests = @options.max_requests
|
|
26
|
-
@parser = Parser::HTTP1.new(self)
|
|
27
|
+
@parser = Parser::HTTP1.new(self, options.max_response_headers, options.max_response_header_value_size)
|
|
27
28
|
@buffer = buffer
|
|
28
29
|
@version = [1, 1]
|
|
29
30
|
@pending = []
|
|
@@ -47,6 +48,10 @@ module HTTPX
|
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def reset
|
|
51
|
+
if @ping_timer
|
|
52
|
+
@ping_timer.cancel
|
|
53
|
+
@ping_timer = nil
|
|
54
|
+
end
|
|
50
55
|
@max_requests = @options.max_requests || MAX_REQUESTS
|
|
51
56
|
@parser.reset!
|
|
52
57
|
@handshake_completed = false
|
|
@@ -132,6 +137,10 @@ module HTTPX
|
|
|
132
137
|
request.log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
|
|
133
138
|
request.log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v)}" }.join("\n") }
|
|
134
139
|
|
|
140
|
+
if response.content_length && response.content_length > request.options.max_response_body_size
|
|
141
|
+
raise HTTPX::Error, "maximum response body size exceeded"
|
|
142
|
+
end
|
|
143
|
+
|
|
135
144
|
request.response = response
|
|
136
145
|
on_complete if response.finished?
|
|
137
146
|
end
|
|
@@ -153,18 +162,12 @@ module HTTPX
|
|
|
153
162
|
|
|
154
163
|
return unless request
|
|
155
164
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
request.log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
|
|
165
|
+
request.log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
|
166
|
+
request.log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
response = request.response
|
|
161
169
|
|
|
162
|
-
|
|
163
|
-
rescue StandardError => e
|
|
164
|
-
error_response = ErrorResponse.new(request, e)
|
|
165
|
-
request.response = error_response
|
|
166
|
-
dispatch(request)
|
|
167
|
-
end
|
|
170
|
+
response << chunk
|
|
168
171
|
end
|
|
169
172
|
|
|
170
173
|
def on_complete
|
|
@@ -407,7 +410,7 @@ module HTTPX
|
|
|
407
410
|
end
|
|
408
411
|
|
|
409
412
|
def capitalized(field)
|
|
410
|
-
UPCASED
|
|
413
|
+
UPCASED.fetch(field) { field.split("-").map(&:capitalize).join("-") }
|
|
411
414
|
end
|
|
412
415
|
end
|
|
413
416
|
end
|
|
@@ -37,6 +37,7 @@ module HTTPX
|
|
|
37
37
|
@streams = {}
|
|
38
38
|
@drains = {}
|
|
39
39
|
@pings = []
|
|
40
|
+
@streams_to_close_after_receive = []
|
|
40
41
|
@buffer = buffer
|
|
41
42
|
@handshake_completed = false
|
|
42
43
|
@wait_for_handshake = @settings.key?(:wait_for_handshake) ? @settings.delete(:wait_for_handshake) : true
|
|
@@ -110,6 +111,12 @@ module HTTPX
|
|
|
110
111
|
|
|
111
112
|
def <<(data)
|
|
112
113
|
@connection << data
|
|
114
|
+
|
|
115
|
+
while (stream, request, error = @streams_to_close_after_receive.shift)
|
|
116
|
+
# these streams were marked for cancellation due to errors found while processing the
|
|
117
|
+
# data received by the peer.
|
|
118
|
+
emit_stream_error(stream, request, error)
|
|
119
|
+
end
|
|
113
120
|
end
|
|
114
121
|
|
|
115
122
|
def send(request, head = false)
|
|
@@ -118,7 +125,7 @@ module HTTPX
|
|
|
118
125
|
return false
|
|
119
126
|
end
|
|
120
127
|
unless (stream = @streams[request])
|
|
121
|
-
stream = @connection.new_stream
|
|
128
|
+
stream = @connection.new_stream(**request.http2_stream_options)
|
|
122
129
|
handle_stream(stream, request)
|
|
123
130
|
@streams[request] = stream
|
|
124
131
|
@max_requests -= 1
|
|
@@ -128,6 +135,8 @@ module HTTPX
|
|
|
128
135
|
rescue ::HTTP2::Error::StreamLimitExceeded
|
|
129
136
|
@pending.unshift(request)
|
|
130
137
|
false
|
|
138
|
+
rescue ::HTTP2::Error::Error, ArgumentError => e
|
|
139
|
+
emit(:error, request, e)
|
|
131
140
|
end
|
|
132
141
|
|
|
133
142
|
def consume
|
|
@@ -312,11 +321,27 @@ module HTTPX
|
|
|
312
321
|
end
|
|
313
322
|
_, status = h.shift
|
|
314
323
|
headers = request.options.headers_class.new(h)
|
|
324
|
+
|
|
325
|
+
raise HTTPX::Error, "maximum number of response headers exceeded" if h.size > @options.max_response_headers
|
|
326
|
+
|
|
327
|
+
if (max_header_value_size = @options.max_response_header_value_size)
|
|
328
|
+
headers.each do |_, v| # rubocop:disable Style/HashEachMethods
|
|
329
|
+
raise HTTPX::Error, "maximum header value size exceeded" if v.size > max_header_value_size
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
315
333
|
response = request.options.response_class.new(request, status, "2.0", headers)
|
|
334
|
+
|
|
335
|
+
if response.content_length && response.content_length > request.options.max_response_body_size
|
|
336
|
+
raise HTTPX::Error.new, "maximum response body size exceeded"
|
|
337
|
+
end
|
|
338
|
+
|
|
316
339
|
request.response = response
|
|
317
340
|
@streams[request] = stream
|
|
318
341
|
|
|
319
342
|
handle(request, stream) if request.expects?
|
|
343
|
+
rescue HTTPX::Error => e
|
|
344
|
+
@streams_to_close_after_receive << [stream, request, e]
|
|
320
345
|
end
|
|
321
346
|
|
|
322
347
|
def on_stream_trailers(stream, request, response, h)
|
|
@@ -329,7 +354,18 @@ module HTTPX
|
|
|
329
354
|
def on_stream_data(stream, request, data)
|
|
330
355
|
request.log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
|
|
331
356
|
request.log(level: 2, color: :green) { "#{stream.id}: <- #{log_redact_body(data.inspect)}" }
|
|
357
|
+
|
|
358
|
+
return unless request.response
|
|
359
|
+
|
|
332
360
|
request.response << data
|
|
361
|
+
rescue HTTPX::Error => e
|
|
362
|
+
if stream.state == :closing
|
|
363
|
+
# there won't be any more chunks to process after this one.
|
|
364
|
+
emit_stream_error(stream, request, e)
|
|
365
|
+
else
|
|
366
|
+
# defer until the last chunk from the payload is processed.
|
|
367
|
+
@streams_to_close_after_receive << [stream, request, e]
|
|
368
|
+
end
|
|
333
369
|
end
|
|
334
370
|
|
|
335
371
|
def on_stream_refuse(stream, request, error)
|
|
@@ -365,11 +401,12 @@ module HTTPX
|
|
|
365
401
|
emit(:response, request, response)
|
|
366
402
|
end
|
|
367
403
|
else
|
|
368
|
-
response = request.response
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
404
|
+
if (response = request.response)
|
|
405
|
+
if response.is_a?(Response) && response.status == 421
|
|
406
|
+
emit(:error, request, :http_1_1_required)
|
|
407
|
+
else
|
|
408
|
+
emit(:response, request, response)
|
|
409
|
+
end
|
|
373
410
|
end
|
|
374
411
|
end
|
|
375
412
|
send(@pending.shift) unless @pending.empty?
|
|
@@ -428,11 +465,28 @@ module HTTPX
|
|
|
428
465
|
end
|
|
429
466
|
|
|
430
467
|
def frame_with_extra_info(frame)
|
|
468
|
+
flags_bits = frame.fetch(:flags, 0)
|
|
431
469
|
case frame[:type]
|
|
432
470
|
when :data
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
471
|
+
flags = [] #: Array[Symbol]
|
|
472
|
+
flags << :end_stream if flags_bits.anybits?(0b0001)
|
|
473
|
+
flags << :padded if flags_bits.anybits?(0b1000)
|
|
474
|
+
frame.merge(payload: frame[:payload].bytesize, flags: flags)
|
|
475
|
+
when :push_promise, :headers
|
|
476
|
+
flags = [] #: Array[Symbol]
|
|
477
|
+
flags << :end_stream if flags_bits.anybits?(0b0001)
|
|
478
|
+
flags << :priority if flags_bits.anybits?(0b0010)
|
|
479
|
+
flags << :end_headers if flags_bits.anybits?(0b0100)
|
|
480
|
+
flags << :padded if flags_bits.anybits?(0b1000)
|
|
481
|
+
frame.merge(payload: log_redact_headers(frame[:payload]), flags: flags)
|
|
482
|
+
when :ping
|
|
483
|
+
flags = [] #: Array[Symbol]
|
|
484
|
+
flags << :ack if flags_bits.anybits?(0b0001)
|
|
485
|
+
frame.merge(payload: log_redact_headers(frame[:payload]), flags: flags)
|
|
486
|
+
when :settings
|
|
487
|
+
flags = [] #: Array[Symbol]
|
|
488
|
+
flags << :ack if flags_bits.anybits?(0b0001)
|
|
489
|
+
frame.merge(flags: flags)
|
|
436
490
|
when :window_update
|
|
437
491
|
connection_or_stream = if (id = frame[:stream]).zero?
|
|
438
492
|
@connection
|
|
@@ -476,6 +530,12 @@ module HTTPX
|
|
|
476
530
|
emit(:pong)
|
|
477
531
|
end
|
|
478
532
|
|
|
533
|
+
def emit_stream_error(stream, request, error)
|
|
534
|
+
teardown(request)
|
|
535
|
+
stream.close
|
|
536
|
+
emit(:error, request, error)
|
|
537
|
+
end
|
|
538
|
+
|
|
479
539
|
def teardown(request = nil)
|
|
480
540
|
if request
|
|
481
541
|
@drains.delete(request)
|