httpx 0.18.0 → 0.18.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/doc/release_notes/0_18_1.md +12 -0
- data/doc/release_notes/0_18_2.md +10 -0
- data/doc/release_notes/0_18_3.md +7 -0
- data/doc/release_notes/0_18_4.md +14 -0
- data/lib/httpx/adapters/faraday.rb +51 -9
- data/lib/httpx/adapters/webmock.rb +71 -59
- data/lib/httpx/altsvc.rb +25 -9
- data/lib/httpx/connection/http1.rb +5 -2
- data/lib/httpx/connection/http2.rb +16 -3
- data/lib/httpx/connection.rb +4 -2
- data/lib/httpx/io/ssl.rb +4 -0
- data/lib/httpx/io/udp.rb +0 -1
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +12 -4
- data/lib/httpx/plugins/retries.rb +13 -10
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/selector.rb +7 -0
- data/lib/httpx/session.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http1.rbs +5 -0
- data/sig/connection/http2.rbs +3 -0
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c6e05e5bda153614ac00aac76cdaff0d5d727c58a99fb2297c545aa54b27c0e
|
4
|
+
data.tar.gz: 9d682e4136c3e8a3d769e02f96209a72dadbb5722a01d4ec8267cec06b5f1a3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2549dd8e2b9786dfb6916f88bd9979e06fc32806a09ead664f97d67612eb3d4b182e4a48d1b23f037e899ecda50ad36b343e85e97258fd3ecb730f63238417e
|
7
|
+
data.tar.gz: f0388924dfba2717069b9cea8463e658d6262c6bfb874185c60baaab1650055fe4a98acd9fa39a6a060f00aa0aa05f1345d627bac138ecf5e17ad4d8aacd92b2
|
data/README.md
CHANGED
@@ -45,7 +45,7 @@ response = HTTPX.get("https://nghttp2.org")
|
|
45
45
|
puts response.status #=> 200
|
46
46
|
body = response.body
|
47
47
|
puts body #=> #<HTTPX::Response ...
|
48
|
-
```
|
48
|
+
```
|
49
49
|
|
50
50
|
You can also send as many requests as you want simultaneously:
|
51
51
|
|
@@ -79,7 +79,7 @@ In Ruby, HTTP client implementations are a known cheap commodity. Why this one?
|
|
79
79
|
|
80
80
|
### Concurrency
|
81
81
|
|
82
|
-
This library supports HTTP/2 seamlessly (which means, if the request is secure, and the server support ALPN negotiation AND HTTP/2, the request will be made through HTTP/2). If you pass multiple URIs, and they can utilize the same connection, they will run concurrently in it.
|
82
|
+
This library supports HTTP/2 seamlessly (which means, if the request is secure, and the server support ALPN negotiation AND HTTP/2, the request will be made through HTTP/2). If you pass multiple URIs, and they can utilize the same connection, they will run concurrently in it.
|
83
83
|
|
84
84
|
However if the server supports HTTP/1.1, it will try to use HTTP pipelining, falling back to 1 request at a time if the server doesn't support it (if the server support Keep-Alive connections, it will reuse the same connection).
|
85
85
|
|
@@ -137,7 +137,8 @@ In order to use HTTP/2 under JRuby, [check this link](https://gitlab.com/honeyry
|
|
137
137
|
|
138
138
|
### Known bugs
|
139
139
|
|
140
|
-
Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryderchuck/httpx/issues/36)).
|
140
|
+
* Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryderchuck/httpx/issues/36)).
|
141
|
+
* Using `total_timeout` along with the `:persistent` plugin [does not work as you might expect](https://gitlab.com/honeyryderchuck/httpx/-/wikis/Timeouts#total_timeout).
|
141
142
|
|
142
143
|
## Contributing
|
143
144
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# 0.18.1
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* HTTP/1.1 pipelining logs were logging the previously-buffered requests all together for each triggered request, which created some confusion for users when reporting errors. This has been fixed.
|
6
|
+
* HTTP/2 coalescing is now skipped when performing TLS connections with VERIFY_NONE.
|
7
|
+
* HTTP/2 peer GOAWAY frames will now result in a (retryable) connection error, instead of being ignored and leaving a "ghost" connection behind.
|
8
|
+
* fixed total timeout call which was not raising the exception.
|
9
|
+
|
10
|
+
## Chore
|
11
|
+
|
12
|
+
This gem now requires MFA-based gem releases.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# 0.18.2
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* A bug was reported and fixed, whereby a persistent connection with a `:total_timeout` set was triggering the timeout and leaving the process looping indefinitely.
|
6
|
+
|
7
|
+
|
8
|
+
## Chore
|
9
|
+
|
10
|
+
The quirk of using the `:persistent` plugin with `:total_timeout` has been documented: https://gitlab.com/honeyryderchuck/httpx/-/wikis/Timeouts#total_timeout.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# 0.18.3
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* request bodies eager-loaded from enumerables yield duped partial chunks.
|
6
|
+
|
7
|
+
An error was observed while looking at webmock integration, where requests formed via the multipart plugin where returning an empty string as body. The issue was caused by an optimization on multipart encoder, which reuses the same buffer when reading chunks. Unfortunately, these cannot be yielded the same way via IO.copy_stream, as the same (cleared) buffer will be used to generate the eager-loaded body chunks.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# 0.18.4
|
2
|
+
|
3
|
+
## Improvements
|
4
|
+
|
5
|
+
* faraday adapter: added support for `#on_data` callback in order to support [faraday streaming](https://lostisland.github.io/faraday/usage/streaming).
|
6
|
+
|
7
|
+
* multipart plugin: removed support for file mime type detection using `mime-types`. The reasoning behind it was that `mime-types` uses the filename, which is a very innacurate detection strategy (ex: an mp4 video will be identified as `application/mp4`, instead of the correct `video/mp4`).
|
8
|
+
* multipart plugin: supported for file mime type detection using `marcel` and `filemagic` was added. Both use the magic header bytes, which is a more accurate strategy for file type detection.
|
9
|
+
|
10
|
+
## Bugfixes
|
11
|
+
|
12
|
+
* webmock adapter has been reimplemented to work with `httpx` plugins (such as the `:retries` plugin). Some other fixes were applied to make it work better under `vcr` (a common `webmock` extension).
|
13
|
+
|
14
|
+
* fixed the URI-related bug which was making requests stall under ruby 3.1 (still not officially testinng against it).
|
@@ -21,6 +21,17 @@ module Faraday
|
|
21
21
|
end
|
22
22
|
# :nocov:
|
23
23
|
|
24
|
+
unless Faraday::RequestOptions.method_defined?(:stream_response?)
|
25
|
+
module RequestOptionsExtensions
|
26
|
+
refine Faraday::RequestOptions do
|
27
|
+
def stream_response?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
using RequestOptionsExtensions
|
33
|
+
end
|
34
|
+
|
24
35
|
module RequestMixin
|
25
36
|
using ::HTTPX::HashExtensions
|
26
37
|
|
@@ -64,6 +75,27 @@ module Faraday
|
|
64
75
|
|
65
76
|
include RequestMixin
|
66
77
|
|
78
|
+
module OnDataPlugin
|
79
|
+
module RequestMethods
|
80
|
+
attr_writer :response_on_data
|
81
|
+
|
82
|
+
def response=(response)
|
83
|
+
super
|
84
|
+
response.body.on_data = @response_on_data
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module ResponseBodyMethods
|
89
|
+
attr_writer :on_data
|
90
|
+
|
91
|
+
def write(chunk)
|
92
|
+
return super unless @on_data
|
93
|
+
|
94
|
+
@on_data.call(chunk, chunk.bytesize)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
67
99
|
class Session < ::HTTPX::Session
|
68
100
|
plugin(:compression)
|
69
101
|
plugin(:persistent)
|
@@ -137,15 +169,21 @@ module Faraday
|
|
137
169
|
end
|
138
170
|
|
139
171
|
def run
|
140
|
-
requests = @handlers.map { |handler| build_request(handler.env) }
|
141
172
|
env = @handlers.last.env
|
142
173
|
|
143
|
-
proxy_options = { uri: env.request.proxy }
|
144
|
-
|
145
174
|
session = @session.with(options_from_env(env))
|
146
|
-
session = session.plugin(:proxy).with(proxy:
|
175
|
+
session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
|
176
|
+
session = session.plugin(OnDataPlugin) if env.request.stream_response?
|
177
|
+
|
178
|
+
requests = @handlers.map { |handler| session.build_request(*build_request(handler.env)) }
|
179
|
+
|
180
|
+
if env.request.stream_response?
|
181
|
+
requests.each do |request|
|
182
|
+
request.response_on_data = env.request.on_data
|
183
|
+
end
|
184
|
+
end
|
147
185
|
|
148
|
-
responses = session.request(requests)
|
186
|
+
responses = session.request(*requests)
|
149
187
|
Array(responses).each_with_index do |response, index|
|
150
188
|
handler = @handlers[index]
|
151
189
|
handler.on_response.call(response)
|
@@ -179,11 +217,15 @@ module Faraday
|
|
179
217
|
return handler
|
180
218
|
end
|
181
219
|
|
182
|
-
meth, uri, request_options = build_request(env)
|
183
|
-
|
184
220
|
session = @session.with(options_from_env(env))
|
185
|
-
session = session.plugin(:proxy).with(proxy:
|
186
|
-
|
221
|
+
session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
|
222
|
+
session = session.plugin(OnDataPlugin) if env.request.stream_response?
|
223
|
+
|
224
|
+
request = session.build_request(*build_request(env))
|
225
|
+
|
226
|
+
request.response_on_data = env.request.on_data if env.request.stream_response?
|
227
|
+
|
228
|
+
response = session.request(request)
|
187
229
|
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
188
230
|
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
189
231
|
response_headers.merge!(response.headers)
|
@@ -16,56 +16,8 @@ module WebMock
|
|
16
16
|
# Requests are "hijacked" at the session, before they're distributed to a connection.
|
17
17
|
#
|
18
18
|
module Plugin
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def send_requests(*requests)
|
23
|
-
request_signatures = requests.map do |request|
|
24
|
-
request_signature = _build_webmock_request_signature(request)
|
25
|
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
26
|
-
request_signature
|
27
|
-
end
|
28
|
-
|
29
|
-
responses = request_signatures.map do |request_signature|
|
30
|
-
WebMock::StubRegistry.instance.response_for_request(request_signature)
|
31
|
-
end
|
32
|
-
|
33
|
-
real_requests = {}
|
34
|
-
|
35
|
-
requests.each_with_index.each_with_object([request_signatures, responses]) do |(request, idx), (sig_reqs, mock_responses)|
|
36
|
-
if (webmock_response = mock_responses[idx])
|
37
|
-
mock_responses[idx] = _build_from_webmock_response(request, webmock_response)
|
38
|
-
WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, sig_reqs[idx], webmock_response)
|
39
|
-
log { "mocking #{request.uri} with #{mock_responses[idx].inspect}" }
|
40
|
-
elsif WebMock.net_connect_allowed?(sig_reqs[idx].uri)
|
41
|
-
log { "performing #{request.uri}" }
|
42
|
-
real_requests[request] = idx
|
43
|
-
else
|
44
|
-
raise WebMock::NetConnectNotAllowedError, sig_reqs[idx]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
unless real_requests.empty?
|
49
|
-
reqs = real_requests.keys
|
50
|
-
reqs.zip(super(*reqs)).each do |req, res|
|
51
|
-
idx = real_requests[req]
|
52
|
-
|
53
|
-
if WebMock::CallbackRegistry.any_callbacks?
|
54
|
-
webmock_response = _build_webmock_response(req, res)
|
55
|
-
WebMock::CallbackRegistry.invoke_callbacks(
|
56
|
-
{ lib: :httpx, real_request: true }, request_signatures[idx],
|
57
|
-
webmock_response
|
58
|
-
)
|
59
|
-
end
|
60
|
-
|
61
|
-
responses[idx] = res
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
responses
|
66
|
-
end
|
67
|
-
|
68
|
-
def _build_webmock_request_signature(request)
|
19
|
+
class << self
|
20
|
+
def build_webmock_request_signature(request)
|
69
21
|
uri = WebMock::Util::URI.heuristic_parse(request.uri)
|
70
22
|
uri.path = uri.normalized_path.gsub("[^:]//", "/")
|
71
23
|
|
@@ -77,7 +29,7 @@ module WebMock
|
|
77
29
|
)
|
78
30
|
end
|
79
31
|
|
80
|
-
def
|
32
|
+
def build_webmock_response(_request, response)
|
81
33
|
webmock_response = WebMock::Response.new
|
82
34
|
webmock_response.status = [response.status, HTTP_REASONS[response.status]]
|
83
35
|
webmock_response.body = response.body.to_s
|
@@ -85,10 +37,10 @@ module WebMock
|
|
85
37
|
webmock_response
|
86
38
|
end
|
87
39
|
|
88
|
-
def
|
89
|
-
return
|
40
|
+
def build_from_webmock_response(request, webmock_response)
|
41
|
+
return build_error_response(request, HTTPX::TimeoutError.new(1, "Timed out")) if webmock_response.should_timeout
|
90
42
|
|
91
|
-
return
|
43
|
+
return build_error_response(request, webmock_response.exception) if webmock_response.exception
|
92
44
|
|
93
45
|
response = request.options.response_class.new(request,
|
94
46
|
webmock_response.status[0],
|
@@ -98,10 +50,70 @@ module WebMock
|
|
98
50
|
response
|
99
51
|
end
|
100
52
|
|
101
|
-
def
|
53
|
+
def build_error_response(request, exception)
|
102
54
|
HTTPX::ErrorResponse.new(request, exception, request.options)
|
103
55
|
end
|
104
56
|
end
|
57
|
+
|
58
|
+
module InstanceMethods
|
59
|
+
def build_connection(*)
|
60
|
+
connection = super
|
61
|
+
connection.once(:unmock_connection) do
|
62
|
+
pool.__send__(:resolve_connection, connection)
|
63
|
+
pool.__send__(:unregister_connection, connection) unless connection.addresses
|
64
|
+
end
|
65
|
+
connection
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ConnectionMethods
|
70
|
+
def initialize(*)
|
71
|
+
super
|
72
|
+
@mocked = true
|
73
|
+
end
|
74
|
+
|
75
|
+
def open?
|
76
|
+
return true if @mocked
|
77
|
+
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
def interests
|
82
|
+
return if @mocked
|
83
|
+
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def send(request)
|
88
|
+
request_signature = Plugin.build_webmock_request_signature(request)
|
89
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
90
|
+
|
91
|
+
if (mock_response = WebMock::StubRegistry.instance.response_for_request(request_signature))
|
92
|
+
response = Plugin.build_from_webmock_response(request, mock_response)
|
93
|
+
WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, request_signature, mock_response)
|
94
|
+
log { "mocking #{request.uri} with #{mock_response.inspect}" }
|
95
|
+
request.response = response
|
96
|
+
request.emit(:response, response)
|
97
|
+
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
98
|
+
if WebMock::CallbackRegistry.any_callbacks?
|
99
|
+
request.on(:response) do |resp|
|
100
|
+
unless resp.is_a?(HTTPX::ErrorResponse)
|
101
|
+
webmock_response = Plugin.build_webmock_response(request, resp)
|
102
|
+
WebMock::CallbackRegistry.invoke_callbacks(
|
103
|
+
{ lib: :httpx, real_request: true }, request_signature,
|
104
|
+
webmock_response
|
105
|
+
)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@mocked = false
|
110
|
+
emit(:unmock_connection, self)
|
111
|
+
super
|
112
|
+
else
|
113
|
+
raise WebMock::NetConnectNotAllowedError, request_signature
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
105
117
|
end
|
106
118
|
|
107
119
|
class HttpxAdapter < HttpLibAdapter
|
@@ -109,12 +121,12 @@ module WebMock
|
|
109
121
|
|
110
122
|
class << self
|
111
123
|
def enable!
|
112
|
-
@original_session =
|
124
|
+
@original_session = HTTPX::Session
|
113
125
|
|
114
|
-
webmock_session =
|
126
|
+
webmock_session = HTTPX.plugin(Plugin)
|
115
127
|
|
116
|
-
|
117
|
-
|
128
|
+
HTTPX.send(:remove_const, :Session)
|
129
|
+
HTTPX.send(:const_set, :Session, webmock_session.class)
|
118
130
|
end
|
119
131
|
|
120
132
|
def disable!
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -70,7 +70,7 @@ module HTTPX
|
|
70
70
|
|
71
71
|
scanner = StringScanner.new(altsvc)
|
72
72
|
until scanner.eos?
|
73
|
-
|
73
|
+
alt_service = scanner.scan(/[^=]+=("[^"]+"|[^;,]+)/)
|
74
74
|
|
75
75
|
alt_params = []
|
76
76
|
loop do
|
@@ -80,29 +80,45 @@ module HTTPX
|
|
80
80
|
break if scanner.eos? || scanner.scan(/ *, */)
|
81
81
|
end
|
82
82
|
alt_params = Hash[alt_params.map { |field| field.split("=") }]
|
83
|
-
|
83
|
+
|
84
|
+
alt_proto, alt_authority = alt_service.split("=")
|
85
|
+
alt_origin = parse_altsvc_origin(alt_proto, alt_authority)
|
86
|
+
return unless alt_origin
|
87
|
+
|
88
|
+
yield(alt_origin, alt_params.merge("proto" => alt_proto))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_altsvc_scheme(alt_proto)
|
93
|
+
case alt_proto
|
94
|
+
when "h2c"
|
95
|
+
"http"
|
96
|
+
when "h2"
|
97
|
+
"https"
|
84
98
|
end
|
85
99
|
end
|
86
100
|
|
87
101
|
# :nocov:
|
88
102
|
if RUBY_VERSION < "2.2"
|
89
|
-
def parse_altsvc_origin(alt_origin)
|
90
|
-
|
103
|
+
def parse_altsvc_origin(alt_proto, alt_origin)
|
104
|
+
alt_scheme = parse_altsvc_scheme(alt_proto) or return
|
105
|
+
|
91
106
|
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
92
107
|
if alt_origin.start_with?(":")
|
93
|
-
alt_origin = "#{
|
108
|
+
alt_origin = "#{alt_scheme}://dummy#{alt_origin}"
|
94
109
|
uri = URI.parse(alt_origin)
|
95
110
|
uri.host = nil
|
96
111
|
uri
|
97
112
|
else
|
98
|
-
URI.parse("#{
|
113
|
+
URI.parse("#{alt_scheme}://#{alt_origin}")
|
99
114
|
end
|
100
115
|
end
|
101
116
|
else
|
102
|
-
def parse_altsvc_origin(alt_origin)
|
103
|
-
|
117
|
+
def parse_altsvc_origin(alt_proto, alt_origin)
|
118
|
+
alt_scheme = parse_altsvc_scheme(alt_proto) or return
|
104
119
|
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
105
|
-
|
120
|
+
|
121
|
+
URI.parse("#{alt_scheme}://#{alt_origin}")
|
106
122
|
end
|
107
123
|
end
|
108
124
|
# :nocov:
|
@@ -36,6 +36,8 @@ module HTTPX
|
|
36
36
|
|
37
37
|
request = @requests.first
|
38
38
|
|
39
|
+
return unless request
|
40
|
+
|
39
41
|
return :w if request.interests == :w || !@buffer.empty?
|
40
42
|
|
41
43
|
:r
|
@@ -313,8 +315,9 @@ module HTTPX
|
|
313
315
|
end
|
314
316
|
|
315
317
|
def join_headers(request)
|
316
|
-
|
317
|
-
|
318
|
+
headline = "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}"
|
319
|
+
@buffer << headline << CRLF
|
320
|
+
log(color: :yellow) { "<- HEADLINE: #{headline.chomp.inspect}" }
|
318
321
|
extra_headers = set_protocol_headers(request)
|
319
322
|
join_headers2(request.headers.each(extra_headers))
|
320
323
|
log { "<- " }
|
@@ -16,6 +16,12 @@ module HTTPX
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class GoawayError < Error
|
20
|
+
def initialize
|
21
|
+
super(0, :no_error)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
attr_reader :streams, :pending
|
20
26
|
|
21
27
|
def initialize(buffer, options)
|
@@ -302,7 +308,7 @@ module HTTPX
|
|
302
308
|
@drains.delete(request)
|
303
309
|
@streams.delete(request)
|
304
310
|
|
305
|
-
if error
|
311
|
+
if error
|
306
312
|
ex = Error.new(stream.id, error)
|
307
313
|
ex.set_backtrace(caller)
|
308
314
|
response = ErrorResponse.new(request, ex, request.options)
|
@@ -344,9 +350,16 @@ module HTTPX
|
|
344
350
|
|
345
351
|
def on_close(_last_frame, error, _payload)
|
346
352
|
is_connection_closed = @connection.state == :closed
|
347
|
-
if error
|
353
|
+
if error
|
348
354
|
@buffer.clear if is_connection_closed
|
349
|
-
|
355
|
+
if error == :no_error
|
356
|
+
ex = GoawayError.new
|
357
|
+
@pending.unshift(*@streams.keys)
|
358
|
+
@drains.clear
|
359
|
+
@streams.clear
|
360
|
+
else
|
361
|
+
ex = Error.new(0, error)
|
362
|
+
end
|
350
363
|
ex.set_backtrace(caller)
|
351
364
|
handle_error(ex)
|
352
365
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -117,7 +117,8 @@ module HTTPX
|
|
117
117
|
def coalescable?(connection)
|
118
118
|
if @io.protocol == "h2" &&
|
119
119
|
@origin.scheme == "https" &&
|
120
|
-
connection.origin.scheme == "https"
|
120
|
+
connection.origin.scheme == "https" &&
|
121
|
+
@io.can_verify_peer?
|
121
122
|
@io.verify_hostname(connection.origin.host)
|
122
123
|
else
|
123
124
|
@origin == connection.origin
|
@@ -241,7 +242,7 @@ module HTTPX
|
|
241
242
|
if elapsed_time.negative?
|
242
243
|
ex = TotalTimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds")
|
243
244
|
ex.set_backtrace(caller)
|
244
|
-
on_error(
|
245
|
+
on_error(ex)
|
245
246
|
return
|
246
247
|
end
|
247
248
|
|
@@ -463,6 +464,7 @@ module HTTPX
|
|
463
464
|
transition(:closing)
|
464
465
|
transition(:closed)
|
465
466
|
emit(:reset)
|
467
|
+
|
466
468
|
@parser.reset if @parser
|
467
469
|
transition(:idle)
|
468
470
|
transition(:open)
|
data/lib/httpx/io/ssl.rb
CHANGED
@@ -27,6 +27,10 @@ module HTTPX
|
|
27
27
|
super
|
28
28
|
end
|
29
29
|
|
30
|
+
def can_verify_peer?
|
31
|
+
@ctx.verify_mode == OpenSSL::SSL::VERIFY_PEER
|
32
|
+
end
|
33
|
+
|
30
34
|
def verify_hostname(host)
|
31
35
|
return false if @ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE
|
32
36
|
return false if !@io.respond_to?(:peer_cert) || @io.peer_cert.nil?
|
data/lib/httpx/io/udp.rb
CHANGED
@@ -8,11 +8,19 @@ module HTTPX
|
|
8
8
|
DEFAULT_MIMETYPE = "application/octet-stream"
|
9
9
|
|
10
10
|
# inspired by https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb
|
11
|
-
if defined?(
|
11
|
+
if defined?(FileMagic)
|
12
|
+
def call(file, _)
|
13
|
+
return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
|
14
|
+
|
15
|
+
FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
|
16
|
+
filemagic.buffer(file.read(MAGIC_NUMBER))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
elsif defined?(Marcel)
|
20
|
+
def call(file, filename)
|
21
|
+
return nil if file.eof? # marcel returns "application/octet-stream" for empty files
|
12
22
|
|
13
|
-
|
14
|
-
mime = MIME::Types.of(filename).first
|
15
|
-
mime.content_type if mime
|
23
|
+
Marcel::MimeType.for(file, name: filename)
|
16
24
|
end
|
17
25
|
|
18
26
|
elsif defined?(MimeMagic)
|
@@ -12,16 +12,19 @@ module HTTPX
|
|
12
12
|
# TODO: pass max_retries in a configure/load block
|
13
13
|
|
14
14
|
IDEMPOTENT_METHODS = %i[get options head put delete].freeze
|
15
|
-
RETRYABLE_ERRORS = [
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
RETRYABLE_ERRORS = [
|
16
|
+
IOError,
|
17
|
+
EOFError,
|
18
|
+
Errno::ECONNRESET,
|
19
|
+
Errno::ECONNABORTED,
|
20
|
+
Errno::EPIPE,
|
21
|
+
Errno::EINVAL,
|
22
|
+
Errno::ETIMEDOUT,
|
23
|
+
Parser::Error,
|
24
|
+
TLSError,
|
25
|
+
TimeoutError,
|
26
|
+
Connection::HTTP2::GoawayError,
|
27
|
+
].freeze
|
25
28
|
DEFAULT_JITTER = ->(interval) { interval * (0.5 * (1 + rand)) }
|
26
29
|
|
27
30
|
if ENV.key?("HTTPX_NO_JITTER")
|
data/lib/httpx/request.rb
CHANGED
data/lib/httpx/selector.rb
CHANGED
@@ -117,6 +117,13 @@ class HTTPX::Selector
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def select(interval, &block)
|
120
|
+
# do not cause an infinite loop here.
|
121
|
+
#
|
122
|
+
# this may happen if timeout calculation actually triggered an error which causes
|
123
|
+
# the connections to be reaped (such as the total timeout error) before #select
|
124
|
+
# gets called.
|
125
|
+
return if interval.nil? && @selectables.empty?
|
126
|
+
|
120
127
|
return select_one(interval, &block) if @selectables.size == 1
|
121
128
|
|
122
129
|
select_many(interval, &block)
|
data/lib/httpx/session.rb
CHANGED
data/lib/httpx/version.rb
CHANGED
data/sig/connection/http1.rbs
CHANGED
data/sig/connection/http2.rbs
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.18.
|
4
|
+
version: 0.18.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2-next
|
@@ -63,6 +63,10 @@ extra_rdoc_files:
|
|
63
63
|
- doc/release_notes/0_16_1.md
|
64
64
|
- doc/release_notes/0_17_0.md
|
65
65
|
- doc/release_notes/0_18_0.md
|
66
|
+
- doc/release_notes/0_18_1.md
|
67
|
+
- doc/release_notes/0_18_2.md
|
68
|
+
- doc/release_notes/0_18_3.md
|
69
|
+
- doc/release_notes/0_18_4.md
|
66
70
|
- doc/release_notes/0_1_0.md
|
67
71
|
- doc/release_notes/0_2_0.md
|
68
72
|
- doc/release_notes/0_2_1.md
|
@@ -119,6 +123,10 @@ files:
|
|
119
123
|
- doc/release_notes/0_16_1.md
|
120
124
|
- doc/release_notes/0_17_0.md
|
121
125
|
- doc/release_notes/0_18_0.md
|
126
|
+
- doc/release_notes/0_18_1.md
|
127
|
+
- doc/release_notes/0_18_2.md
|
128
|
+
- doc/release_notes/0_18_3.md
|
129
|
+
- doc/release_notes/0_18_4.md
|
122
130
|
- doc/release_notes/0_1_0.md
|
123
131
|
- doc/release_notes/0_2_0.md
|
124
132
|
- doc/release_notes/0_2_1.md
|
@@ -300,6 +308,7 @@ metadata:
|
|
300
308
|
documentation_uri: https://honeyryderchuck.gitlab.io/httpx/rdoc/
|
301
309
|
source_code_uri: https://gitlab.com/honeyryderchuck/httpx
|
302
310
|
homepage_uri: https://honeyryderchuck.gitlab.io/httpx/
|
311
|
+
rubygems_mfa_required: 'true'
|
303
312
|
post_install_message:
|
304
313
|
rdoc_options: []
|
305
314
|
require_paths:
|
@@ -315,7 +324,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
315
324
|
- !ruby/object:Gem::Version
|
316
325
|
version: '0'
|
317
326
|
requirements: []
|
318
|
-
rubygems_version: 3.
|
327
|
+
rubygems_version: 3.3.3
|
319
328
|
signing_key:
|
320
329
|
specification_version: 4
|
321
330
|
summary: HTTPX, to the future, and beyond
|