httpx 0.18.3 → 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/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/io/udp.rb +0 -1
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +12 -4
- data/lib/httpx/session.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- metadata +5 -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
|
@@ -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:
|
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)
|
data/lib/httpx/session.rb
CHANGED
data/lib/httpx/version.rb
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-12-
|
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
|
@@ -66,6 +66,7 @@ extra_rdoc_files:
|
|
66
66
|
- doc/release_notes/0_18_1.md
|
67
67
|
- doc/release_notes/0_18_2.md
|
68
68
|
- doc/release_notes/0_18_3.md
|
69
|
+
- doc/release_notes/0_18_4.md
|
69
70
|
- doc/release_notes/0_1_0.md
|
70
71
|
- doc/release_notes/0_2_0.md
|
71
72
|
- doc/release_notes/0_2_1.md
|
@@ -125,6 +126,7 @@ files:
|
|
125
126
|
- doc/release_notes/0_18_1.md
|
126
127
|
- doc/release_notes/0_18_2.md
|
127
128
|
- doc/release_notes/0_18_3.md
|
129
|
+
- doc/release_notes/0_18_4.md
|
128
130
|
- doc/release_notes/0_1_0.md
|
129
131
|
- doc/release_notes/0_2_0.md
|
130
132
|
- doc/release_notes/0_2_1.md
|
@@ -322,7 +324,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
322
324
|
- !ruby/object:Gem::Version
|
323
325
|
version: '0'
|
324
326
|
requirements: []
|
325
|
-
rubygems_version: 3.
|
327
|
+
rubygems_version: 3.3.3
|
326
328
|
signing_key:
|
327
329
|
specification_version: 4
|
328
330
|
summary: HTTPX, to the future, and beyond
|