httpx 0.20.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +0 -48
- data/README.md +54 -45
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +5 -5
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_19_8.md +1 -1
- data/doc/release_notes/0_20_0.md +2 -2
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/lib/httpx/adapters/datadog.rb +215 -122
- data/lib/httpx/adapters/faraday.rb +145 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +34 -18
- data/lib/httpx/altsvc.rb +63 -26
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +12 -0
- data/lib/httpx/callbacks.rb +5 -3
- data/lib/httpx/chainable.rb +54 -39
- data/lib/httpx/connection/http1.rb +75 -44
- data/lib/httpx/connection/http2.rb +31 -38
- data/lib/httpx/connection.rb +287 -117
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +52 -2
- data/lib/httpx/extensions.rb +24 -131
- data/lib/httpx/io/ssl.rb +83 -77
- data/lib/httpx/io/tcp.rb +48 -71
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +10 -15
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +176 -118
- data/lib/httpx/parser/http1.rb +4 -0
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
- data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
- data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
- data/lib/httpx/plugins/aws_sigv4.rb +12 -9
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +148 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +30 -17
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
- data/lib/httpx/plugins/expect.rb +21 -14
- data/lib/httpx/plugins/follow_redirects.rb +140 -41
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -37
- data/lib/httpx/plugins/grpc.rb +36 -29
- data/lib/httpx/plugins/h2c.rb +26 -19
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
- data/lib/httpx/plugins/oauth.rb +175 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +23 -13
- data/lib/httpx/plugins/proxy/socks4.rb +9 -7
- data/lib/httpx/plugins/proxy/socks5.rb +11 -9
- data/lib/httpx/plugins/proxy.rb +80 -61
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +5 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +62 -25
- data/lib/httpx/plugins/response_cache.rb +105 -12
- data/lib/httpx/plugins/retries.rb +87 -17
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +27 -23
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +80 -0
- data/lib/httpx/pool/synch_pool.rb +93 -0
- data/lib/httpx/pool.rb +102 -27
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +154 -0
- data/lib/httpx/request.rb +130 -146
- data/lib/httpx/resolver/https.rb +62 -27
- data/lib/httpx/resolver/multi.rb +9 -13
- data/lib/httpx/resolver/native.rb +192 -76
- data/lib/httpx/resolver/resolver.rb +34 -9
- data/lib/httpx/resolver/system.rb +16 -11
- data/lib/httpx/resolver.rb +38 -16
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +96 -0
- data/lib/httpx/response.rb +159 -217
- data/lib/httpx/selector.rb +9 -4
- data/lib/httpx/session.rb +137 -89
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +34 -8
- data/lib/httpx/transcoder/body.rb +0 -2
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -33
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +21 -8
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
- data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
- data/lib/httpx/transcoder/multipart.rb +17 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +72 -0
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/transcoder/xml.rb +52 -0
- data/lib/httpx/transcoder.rb +5 -6
- data/lib/httpx/utils.rb +36 -16
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +12 -14
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +2 -1
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +11 -9
- data/sig/connection/http1.rbs +8 -7
- data/sig/connection/http2.rbs +19 -19
- data/sig/connection.rbs +64 -24
- data/sig/errors.rbs +22 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +27 -0
- data/sig/io.rbs +6 -0
- data/sig/options.rbs +32 -22
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +18 -4
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +7 -32
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +54 -0
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks4.rbs +9 -6
- data/sig/plugins/proxy/socks5.rbs +10 -6
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +13 -5
- data/sig/plugins/push_promise.rbs +3 -3
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache.rbs +36 -7
- data/sig/plugins/retries.rbs +30 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +10 -7
- data/sig/request/body.rbs +38 -0
- data/sig/request.rbs +15 -24
- data/sig/resolver/https.rbs +8 -3
- data/sig/resolver/native.rbs +17 -4
- data/sig/resolver/resolver.rbs +8 -6
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +9 -5
- data/sig/response/body.rbs +53 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +24 -39
- data/sig/selector.rbs +1 -1
- data/sig/session.rbs +29 -18
- data/sig/timers.rbs +18 -8
- data/sig/transcoder/body.rbs +4 -3
- data/sig/transcoder/deflate.rbs +11 -0
- data/sig/transcoder/form.rbs +5 -3
- data/sig/transcoder/gzip.rbs +24 -0
- data/sig/transcoder/json.rbs +8 -3
- data/sig/{plugins → transcoder}/multipart.rbs +15 -19
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder/utils/inflater.rbs +12 -0
- data/sig/transcoder/xml.rbs +22 -0
- data/sig/transcoder.rbs +24 -9
- data/sig/utils.rbs +8 -2
- metadata +163 -41
- data/lib/httpx/plugins/authentication.rb +0 -20
- data/lib/httpx/plugins/basic_authentication.rb +0 -30
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -49
- data/lib/httpx/plugins/compression/gzip.rb +0 -88
- data/lib/httpx/plugins/compression.rb +0 -164
- data/lib/httpx/plugins/multipart/decoder.rb +0 -187
- data/lib/httpx/plugins/multipart.rb +0 -84
- data/lib/httpx/registry.rb +0 -85
- data/sig/plugins/authentication.rbs +0 -11
- data/sig/plugins/compression/brotli.rbs +0 -21
- data/sig/plugins/compression/deflate.rbs +0 -17
- data/sig/plugins/compression/gzip.rbs +0 -29
- data/sig/registry.rbs +0 -12
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -7,69 +7,110 @@ require "faraday"
|
|
7
7
|
module Faraday
|
8
8
|
class Adapter
|
9
9
|
class HTTPX < Faraday::Adapter
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
else
|
14
|
-
Faraday::Error::SSLError
|
15
|
-
end
|
10
|
+
module RequestMixin
|
11
|
+
def build_connection(env)
|
12
|
+
return @connection if defined?(@connection)
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
Faraday::Error::ConnectionFailed
|
21
|
-
end
|
22
|
-
# :nocov:
|
14
|
+
@connection = ::HTTPX.plugin(:persistent).plugin(ReasonPlugin)
|
15
|
+
@connection = @connection.with(@connection_options) unless @connection_options.empty?
|
16
|
+
connection_opts = options_from_env(env)
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
if (bind = env.request.bind)
|
19
|
+
@bind = TCPSocket.new(bind[:host], bind[:port])
|
20
|
+
connection_opts[:io] = @bind
|
21
|
+
end
|
22
|
+
@connection = @connection.with(connection_opts)
|
23
|
+
|
24
|
+
if (proxy = env.request.proxy)
|
25
|
+
proxy_options = { uri: proxy.uri }
|
26
|
+
proxy_options[:username] = proxy.user if proxy.user
|
27
|
+
proxy_options[:password] = proxy.password if proxy.password
|
28
|
+
|
29
|
+
@connection = @connection.plugin(:proxy).with(proxy: proxy_options)
|
30
30
|
end
|
31
|
+
@connection = @connection.plugin(OnDataPlugin) if env.request.stream_response?
|
32
|
+
|
33
|
+
@connection = @config_block.call(@connection) || @connection if @config_block
|
34
|
+
@connection
|
31
35
|
end
|
32
|
-
using RequestOptionsExtensions
|
33
|
-
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
+
def close
|
38
|
+
@connection.close if @connection
|
39
|
+
@bind.close if @bind
|
40
|
+
end
|
37
41
|
|
38
42
|
private
|
39
43
|
|
44
|
+
def connect(env, &blk)
|
45
|
+
connection(env, &blk)
|
46
|
+
rescue ::HTTPX::TLSError => e
|
47
|
+
raise Faraday::SSLError, e
|
48
|
+
rescue Errno::ECONNABORTED,
|
49
|
+
Errno::ECONNREFUSED,
|
50
|
+
Errno::ECONNRESET,
|
51
|
+
Errno::EHOSTUNREACH,
|
52
|
+
Errno::EINVAL,
|
53
|
+
Errno::ENETUNREACH,
|
54
|
+
Errno::EPIPE,
|
55
|
+
::HTTPX::ConnectionError => e
|
56
|
+
raise Faraday::ConnectionFailed, e
|
57
|
+
end
|
58
|
+
|
40
59
|
def build_request(env)
|
41
60
|
meth = env[:method]
|
42
61
|
|
43
62
|
request_options = {
|
44
63
|
headers: env.request_headers,
|
45
64
|
body: env.body,
|
65
|
+
**options_from_env(env),
|
46
66
|
}
|
47
|
-
[meth, env.url, request_options]
|
67
|
+
[meth.to_s.upcase, env.url, request_options]
|
48
68
|
end
|
49
69
|
|
50
70
|
def options_from_env(env)
|
51
|
-
timeout_options = {
|
52
|
-
|
53
|
-
|
54
|
-
|
71
|
+
timeout_options = {}
|
72
|
+
req_opts = env.request
|
73
|
+
if (sec = request_timeout(:read, req_opts))
|
74
|
+
timeout_options[:read_timeout] = sec
|
75
|
+
end
|
55
76
|
|
56
|
-
|
57
|
-
|
77
|
+
if (sec = request_timeout(:write, req_opts))
|
78
|
+
timeout_options[:write_timeout] = sec
|
79
|
+
end
|
80
|
+
|
81
|
+
if (sec = request_timeout(:open, req_opts))
|
82
|
+
timeout_options[:connect_timeout] = sec
|
83
|
+
end
|
84
|
+
|
85
|
+
{
|
86
|
+
ssl: ssl_options_from_env(env),
|
58
87
|
timeout: timeout_options,
|
59
88
|
}
|
89
|
+
end
|
90
|
+
|
91
|
+
if defined?(::OpenSSL)
|
92
|
+
def ssl_options_from_env(env)
|
93
|
+
ssl_options = {}
|
94
|
+
|
95
|
+
unless env.ssl.verify.nil?
|
96
|
+
ssl_options[:verify_mode] = env.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
97
|
+
end
|
60
98
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
99
|
+
ssl_options[:ca_file] = env.ssl.ca_file if env.ssl.ca_file
|
100
|
+
ssl_options[:ca_path] = env.ssl.ca_path if env.ssl.ca_path
|
101
|
+
ssl_options[:cert_store] = env.ssl.cert_store if env.ssl.cert_store
|
102
|
+
ssl_options[:cert] = env.ssl.client_cert if env.ssl.client_cert
|
103
|
+
ssl_options[:key] = env.ssl.client_key if env.ssl.client_key
|
104
|
+
ssl_options[:ssl_version] = env.ssl.version if env.ssl.version
|
105
|
+
ssl_options[:verify_depth] = env.ssl.verify_depth if env.ssl.verify_depth
|
106
|
+
ssl_options[:min_version] = env.ssl.min_version if env.ssl.min_version
|
107
|
+
ssl_options[:max_version] = env.ssl.max_version if env.ssl.max_version
|
108
|
+
ssl_options
|
109
|
+
end
|
110
|
+
else
|
111
|
+
def ssl_options_from_env(*)
|
112
|
+
{}
|
113
|
+
end
|
73
114
|
end
|
74
115
|
end
|
75
116
|
|
@@ -100,32 +141,17 @@ module Faraday
|
|
100
141
|
end
|
101
142
|
|
102
143
|
module ReasonPlugin
|
103
|
-
|
104
|
-
|
105
|
-
require "webrick"
|
106
|
-
end
|
107
|
-
else
|
108
|
-
def self.load_dependencies(*)
|
109
|
-
require "net/http/status"
|
110
|
-
end
|
144
|
+
def self.load_dependencies(*)
|
145
|
+
require "net/http/status"
|
111
146
|
end
|
147
|
+
|
112
148
|
module ResponseMethods
|
113
|
-
|
114
|
-
|
115
|
-
WEBrick::HTTPStatus::StatusMessage.fetch(@status)
|
116
|
-
end
|
117
|
-
else
|
118
|
-
def reason
|
119
|
-
Net::HTTP::STATUS_CODES.fetch(@status)
|
120
|
-
end
|
149
|
+
def reason
|
150
|
+
Net::HTTP::STATUS_CODES.fetch(@status)
|
121
151
|
end
|
122
152
|
end
|
123
153
|
end
|
124
154
|
|
125
|
-
def self.session
|
126
|
-
@session ||= ::HTTPX.plugin(:compression).plugin(:persistent).plugin(ReasonPlugin)
|
127
|
-
end
|
128
|
-
|
129
155
|
class ParallelManager
|
130
156
|
class ResponseHandler < SimpleDelegator
|
131
157
|
attr_reader :env
|
@@ -158,8 +184,9 @@ module Faraday
|
|
158
184
|
|
159
185
|
include RequestMixin
|
160
186
|
|
161
|
-
def initialize
|
187
|
+
def initialize(options)
|
162
188
|
@handlers = []
|
189
|
+
@connection_options = options
|
163
190
|
end
|
164
191
|
|
165
192
|
def enqueue(request)
|
@@ -169,85 +196,96 @@ module Faraday
|
|
169
196
|
end
|
170
197
|
|
171
198
|
def run
|
199
|
+
return unless @handlers.last
|
200
|
+
|
172
201
|
env = @handlers.last.env
|
173
202
|
|
174
|
-
|
175
|
-
|
176
|
-
session = session.plugin(OnDataPlugin) if env.request.stream_response?
|
203
|
+
connect(env) do |session|
|
204
|
+
requests = @handlers.map { |handler| session.build_request(*build_request(handler.env)) }
|
177
205
|
|
178
|
-
|
206
|
+
if env.request.stream_response?
|
207
|
+
requests.each do |request|
|
208
|
+
request.response_on_data = env.request.on_data
|
209
|
+
end
|
210
|
+
end
|
179
211
|
|
180
|
-
|
181
|
-
|
182
|
-
|
212
|
+
responses = session.request(*requests)
|
213
|
+
Array(responses).each_with_index do |response, index|
|
214
|
+
handler = @handlers[index]
|
215
|
+
handler.on_response.call(response)
|
216
|
+
handler.on_complete.call(handler.env) if handler.on_complete
|
183
217
|
end
|
184
218
|
end
|
219
|
+
rescue ::HTTPX::TimeoutError => e
|
220
|
+
raise Faraday::TimeoutError, e
|
221
|
+
end
|
185
222
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
223
|
+
# from Faraday::Adapter#connection
|
224
|
+
def connection(env)
|
225
|
+
conn = build_connection(env)
|
226
|
+
return conn unless block_given?
|
227
|
+
|
228
|
+
yield conn
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
# from Faraday::Adapter#request_timeout
|
234
|
+
def request_timeout(type, options)
|
235
|
+
key = Faraday::Adapter::TIMEOUT_KEYS[type]
|
236
|
+
options[key] || options[:timeout]
|
192
237
|
end
|
193
238
|
end
|
194
239
|
|
195
240
|
self.supports_parallel = true
|
196
241
|
|
197
242
|
class << self
|
198
|
-
def setup_parallel_manager
|
199
|
-
ParallelManager.new
|
243
|
+
def setup_parallel_manager(options = {})
|
244
|
+
ParallelManager.new(options)
|
200
245
|
end
|
201
246
|
end
|
202
247
|
|
203
|
-
def initialize(app, options = {})
|
204
|
-
super(app)
|
205
|
-
@session_options = options
|
206
|
-
end
|
207
|
-
|
208
248
|
def call(env)
|
209
249
|
super
|
210
250
|
if parallel?(env)
|
211
251
|
handler = env[:parallel_manager].enqueue(env)
|
212
252
|
handler.on_response do |response|
|
213
|
-
response.
|
214
|
-
|
215
|
-
|
253
|
+
if response.is_a?(::HTTPX::Response)
|
254
|
+
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
255
|
+
response_headers.merge!(response.headers)
|
256
|
+
end
|
257
|
+
else
|
258
|
+
env[:error] = response.error
|
259
|
+
save_response(env, 0, "", {}, nil)
|
216
260
|
end
|
217
261
|
end
|
218
262
|
return handler
|
219
263
|
end
|
220
264
|
|
221
|
-
|
222
|
-
session = session.with(@session_options) unless @session_options.empty?
|
223
|
-
session = session.with(options_from_env(env))
|
224
|
-
session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
|
225
|
-
session = session.plugin(OnDataPlugin) if env.request.stream_response?
|
226
|
-
|
227
|
-
request = session.build_request(*build_request(env))
|
228
|
-
|
229
|
-
request.response_on_data = env.request.on_data if env.request.stream_response?
|
230
|
-
|
231
|
-
response = session.request(request)
|
232
|
-
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
265
|
+
response = connect_and_request(env)
|
233
266
|
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
234
267
|
response_headers.merge!(response.headers)
|
235
268
|
end
|
236
269
|
@app.call(env)
|
237
|
-
rescue ::HTTPX::TLSError => e
|
238
|
-
raise SSL_ERROR, e
|
239
|
-
rescue Errno::ECONNABORTED,
|
240
|
-
Errno::ECONNREFUSED,
|
241
|
-
Errno::ECONNRESET,
|
242
|
-
Errno::EHOSTUNREACH,
|
243
|
-
Errno::EINVAL,
|
244
|
-
Errno::ENETUNREACH,
|
245
|
-
Errno::EPIPE => e
|
246
|
-
raise CONNECTION_FAILED_ERROR, e
|
247
270
|
end
|
248
271
|
|
249
272
|
private
|
250
273
|
|
274
|
+
def connect_and_request(env)
|
275
|
+
connect(env) do |session|
|
276
|
+
request = session.build_request(*build_request(env))
|
277
|
+
|
278
|
+
request.response_on_data = env.request.on_data if env.request.stream_response?
|
279
|
+
|
280
|
+
response = session.request(request)
|
281
|
+
# do not call #raise_for_status for HTTP 4xx or 5xx, as faraday has a middleware for that.
|
282
|
+
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
283
|
+
response
|
284
|
+
end
|
285
|
+
rescue ::HTTPX::TimeoutError => e
|
286
|
+
raise Faraday::TimeoutError, e
|
287
|
+
end
|
288
|
+
|
251
289
|
def parallel?(env)
|
252
290
|
env[:parallel_manager]
|
253
291
|
end
|
@@ -27,6 +27,11 @@ module HTTPX::Plugins
|
|
27
27
|
def set_sentry_trace_header(request, sentry_span)
|
28
28
|
return unless sentry_span
|
29
29
|
|
30
|
+
config = ::Sentry.configuration
|
31
|
+
url = request.uri.to_s
|
32
|
+
|
33
|
+
return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }
|
34
|
+
|
30
35
|
trace = ::Sentry.get_current_client.generate_sentry_trace(sentry_span)
|
31
36
|
request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
|
32
37
|
end
|
@@ -43,8 +48,8 @@ module HTTPX::Plugins
|
|
43
48
|
|
44
49
|
request_info = extract_request_info(req)
|
45
50
|
|
46
|
-
data = if
|
47
|
-
{ error: res.message, **request_info }
|
51
|
+
data = if res.is_a?(HTTPX::ErrorResponse)
|
52
|
+
{ error: res.error.message, **request_info }
|
48
53
|
else
|
49
54
|
{ status: res.status, **request_info }
|
50
55
|
end
|
@@ -63,7 +68,11 @@ module HTTPX::Plugins
|
|
63
68
|
|
64
69
|
request_info = extract_request_info(req)
|
65
70
|
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
66
|
-
|
71
|
+
if res.is_a?(HTTPX::ErrorResponse)
|
72
|
+
sentry_span.set_data(:error, res.error.message)
|
73
|
+
else
|
74
|
+
sentry_span.set_data(:status, res.status)
|
75
|
+
end
|
67
76
|
sentry_span.set_timestamp(::Sentry.utc_now.to_f)
|
68
77
|
end
|
69
78
|
|
@@ -71,7 +80,7 @@ module HTTPX::Plugins
|
|
71
80
|
uri = req.uri
|
72
81
|
|
73
82
|
result = {
|
74
|
-
method: req.verb
|
83
|
+
method: req.verb,
|
75
84
|
}
|
76
85
|
|
77
86
|
if ::Sentry.configuration.send_default_pii
|
@@ -85,17 +94,27 @@ module HTTPX::Plugins
|
|
85
94
|
end
|
86
95
|
end
|
87
96
|
|
97
|
+
module RequestMethods
|
98
|
+
def __sentry_enable_trace!
|
99
|
+
return if @__sentry_enable_trace
|
100
|
+
|
101
|
+
Tracer.call(self)
|
102
|
+
@__sentry_enable_trace = true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
88
106
|
module ConnectionMethods
|
89
107
|
def send(request)
|
90
|
-
|
108
|
+
request.__sentry_enable_trace!
|
109
|
+
|
91
110
|
super
|
92
111
|
end
|
93
112
|
end
|
94
113
|
end
|
95
114
|
end
|
96
115
|
|
97
|
-
Sentry.register_patch do
|
98
|
-
sentry_session =
|
116
|
+
Sentry.register_patch(:httpx) do
|
117
|
+
sentry_session = HTTPX.plugin(HTTPX::Plugins::Sentry)
|
99
118
|
|
100
119
|
HTTPX.send(:remove_const, :Session)
|
101
120
|
HTTPX.send(:const_set, :Session, sentry_session.class)
|
@@ -2,13 +2,8 @@
|
|
2
2
|
|
3
3
|
module WebMock
|
4
4
|
module HttpLibAdapters
|
5
|
-
|
6
|
-
|
7
|
-
HTTP_REASONS = WEBrick::HTTPStatus::StatusMessage
|
8
|
-
else
|
9
|
-
require "net/http/status"
|
10
|
-
HTTP_REASONS = Net::HTTP::STATUS_CODES
|
11
|
-
end
|
5
|
+
require "net/http/status"
|
6
|
+
HTTP_REASONS = Net::HTTP::STATUS_CODES
|
12
7
|
|
13
8
|
#
|
14
9
|
# HTTPX plugin for webmock.
|
@@ -23,7 +18,7 @@ module WebMock
|
|
23
18
|
uri.path = uri.normalized_path.gsub("[^:]//", "/")
|
24
19
|
|
25
20
|
WebMock::RequestSignature.new(
|
26
|
-
request.verb,
|
21
|
+
request.verb.downcase.to_sym,
|
27
22
|
uri.to_s,
|
28
23
|
body: request.body.each.to_a.join,
|
29
24
|
headers: request.headers.to_h
|
@@ -43,30 +38,50 @@ module WebMock
|
|
43
38
|
|
44
39
|
return build_error_response(request, webmock_response.exception) if webmock_response.exception
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
request.options.response_class.new(request,
|
42
|
+
webmock_response.status[0],
|
43
|
+
"2.0",
|
44
|
+
webmock_response.headers).tap do |res|
|
45
|
+
res.mocked = true
|
46
|
+
end
|
52
47
|
end
|
53
48
|
|
54
49
|
def build_error_response(request, exception)
|
55
|
-
HTTPX::ErrorResponse.new(request, exception
|
50
|
+
HTTPX::ErrorResponse.new(request, exception)
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
59
54
|
module InstanceMethods
|
60
|
-
def
|
55
|
+
def init_connection(*)
|
61
56
|
connection = super
|
62
57
|
connection.once(:unmock_connection) do
|
58
|
+
unless connection.addresses
|
59
|
+
connection.__send__(:callbacks)[:connect_error].clear
|
60
|
+
pool.__send__(:unregister_connection, connection)
|
61
|
+
end
|
63
62
|
pool.__send__(:resolve_connection, connection)
|
64
|
-
pool.__send__(:unregister_connection, connection) unless connection.addresses
|
65
63
|
end
|
66
64
|
connection
|
67
65
|
end
|
68
66
|
end
|
69
67
|
|
68
|
+
module ResponseMethods
|
69
|
+
attr_accessor :mocked
|
70
|
+
|
71
|
+
def initialize(*)
|
72
|
+
super
|
73
|
+
@mocked = false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module ResponseBodyMethods
|
78
|
+
def decode_chunk(chunk)
|
79
|
+
return chunk if @response.mocked
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
70
85
|
module ConnectionMethods
|
71
86
|
def initialize(*)
|
72
87
|
super
|
@@ -95,6 +110,7 @@ module WebMock
|
|
95
110
|
log { "mocking #{request.uri} with #{mock_response.inspect}" }
|
96
111
|
request.response = response
|
97
112
|
request.emit(:response, response)
|
113
|
+
response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
|
98
114
|
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
99
115
|
if WebMock::CallbackRegistry.any_callbacks?
|
100
116
|
request.on(:response) do |resp|
|
@@ -122,7 +138,7 @@ module WebMock
|
|
122
138
|
|
123
139
|
class << self
|
124
140
|
def enable!
|
125
|
-
@original_session
|
141
|
+
@original_session ||= HTTPX::Session
|
126
142
|
|
127
143
|
webmock_session = HTTPX.plugin(Plugin)
|
128
144
|
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -4,7 +4,59 @@ require "strscan"
|
|
4
4
|
|
5
5
|
module HTTPX
|
6
6
|
module AltSvc
|
7
|
-
|
7
|
+
# makes connections able to accept requests destined to primary service.
|
8
|
+
module ConnectionMixin
|
9
|
+
using URIExtensions
|
10
|
+
|
11
|
+
def send(request)
|
12
|
+
request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def match?(uri, options)
|
18
|
+
return false if !used? && (@state == :closing || @state == :closed)
|
19
|
+
|
20
|
+
match_altsvcs?(uri) && match_altsvc_options?(uri, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# checks if this is connection is an alternative service of
|
26
|
+
# +uri+
|
27
|
+
def match_altsvcs?(uri)
|
28
|
+
@origins.any? { |origin| altsvc_match?(uri, origin) } ||
|
29
|
+
AltSvc.cached_altsvc(@origin).any? do |altsvc|
|
30
|
+
origin = altsvc["origin"]
|
31
|
+
altsvc_match?(origin, uri.origin)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def match_altsvc_options?(uri, options)
|
36
|
+
return @options == options unless @options.ssl.all? do |k, v|
|
37
|
+
v == (k == :hostname ? uri.host : options.ssl[k])
|
38
|
+
end
|
39
|
+
|
40
|
+
@options.options_equals?(options, Options::REQUEST_BODY_IVARS + %i[@ssl])
|
41
|
+
end
|
42
|
+
|
43
|
+
def altsvc_match?(uri, other_uri)
|
44
|
+
other_uri = URI(other_uri)
|
45
|
+
|
46
|
+
uri.origin == other_uri.origin || begin
|
47
|
+
case uri.scheme
|
48
|
+
when "h2"
|
49
|
+
(other_uri.scheme == "https" || other_uri.scheme == "h2") &&
|
50
|
+
uri.host == other_uri.host &&
|
51
|
+
uri.port == other_uri.port
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@altsvc_mutex = Thread::Mutex.new
|
8
60
|
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
9
61
|
|
10
62
|
module_function
|
@@ -46,7 +98,7 @@ module HTTPX
|
|
46
98
|
|
47
99
|
altsvc = response.headers["alt-svc"]
|
48
100
|
|
49
|
-
# https://
|
101
|
+
# https://datatracker.ietf.org/doc/html/rfc7838#section-3
|
50
102
|
# A field value containing the special value "clear" indicates that the
|
51
103
|
# origin requests all alternatives for that origin to be invalidated
|
52
104
|
# (including those specified in the same response, in case of an
|
@@ -79,9 +131,9 @@ module HTTPX
|
|
79
131
|
scanner.skip(/;/)
|
80
132
|
break if scanner.eos? || scanner.scan(/ *, */)
|
81
133
|
end
|
82
|
-
alt_params = Hash[alt_params.map { |field| field.split("=") }]
|
134
|
+
alt_params = Hash[alt_params.map { |field| field.split("=", 2) }]
|
83
135
|
|
84
|
-
alt_proto, alt_authority = alt_service.split("=")
|
136
|
+
alt_proto, alt_authority = alt_service.split("=", 2)
|
85
137
|
alt_origin = parse_altsvc_origin(alt_proto, alt_authority)
|
86
138
|
return unless alt_origin
|
87
139
|
|
@@ -98,29 +150,14 @@ module HTTPX
|
|
98
150
|
end
|
99
151
|
end
|
100
152
|
|
101
|
-
|
102
|
-
|
103
|
-
def parse_altsvc_origin(alt_proto, alt_origin)
|
104
|
-
alt_scheme = parse_altsvc_scheme(alt_proto) or return
|
105
|
-
|
106
|
-
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
107
|
-
if alt_origin.start_with?(":")
|
108
|
-
alt_origin = "#{alt_scheme}://dummy#{alt_origin}"
|
109
|
-
uri = URI.parse(alt_origin)
|
110
|
-
uri.host = nil
|
111
|
-
uri
|
112
|
-
else
|
113
|
-
URI.parse("#{alt_scheme}://#{alt_origin}")
|
114
|
-
end
|
115
|
-
end
|
116
|
-
else
|
117
|
-
def parse_altsvc_origin(alt_proto, alt_origin)
|
118
|
-
alt_scheme = parse_altsvc_scheme(alt_proto) or return
|
119
|
-
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
153
|
+
def parse_altsvc_origin(alt_proto, alt_origin)
|
154
|
+
alt_scheme = parse_altsvc_scheme(alt_proto)
|
120
155
|
|
121
|
-
|
122
|
-
|
156
|
+
return unless alt_scheme
|
157
|
+
|
158
|
+
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
159
|
+
|
160
|
+
URI.parse("#{alt_scheme}://#{alt_origin}")
|
123
161
|
end
|
124
|
-
# :nocov:
|
125
162
|
end
|
126
163
|
end
|
data/lib/httpx/base64.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if RUBY_VERSION < "3.3.0"
|
4
|
+
require "base64"
|
5
|
+
elsif !defined?(Base64)
|
6
|
+
module HTTPX
|
7
|
+
# require "base64" will not be a default gem after ruby 3.4.0
|
8
|
+
module Base64
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def decode64(str)
|
12
|
+
str.unpack1("m")
|
13
|
+
end
|
14
|
+
|
15
|
+
def strict_encode64(bin)
|
16
|
+
[bin].pack("m0")
|
17
|
+
end
|
18
|
+
|
19
|
+
def urlsafe_encode64(bin, padding: true)
|
20
|
+
str = strict_encode64(bin)
|
21
|
+
str.chomp!("==") or str.chomp!("=") unless padding
|
22
|
+
str.tr!("+/", "-_")
|
23
|
+
str
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/httpx/buffer.rb
CHANGED
@@ -3,6 +3,14 @@
|
|
3
3
|
require "forwardable"
|
4
4
|
|
5
5
|
module HTTPX
|
6
|
+
# Internal class to abstract a string buffer, by wrapping a string and providing the
|
7
|
+
# minimum possible API and functionality required.
|
8
|
+
#
|
9
|
+
# buffer = Buffer.new(640)
|
10
|
+
# buffer.full? #=> false
|
11
|
+
# buffer << "aa"
|
12
|
+
# buffer.capacity #=> 638
|
13
|
+
#
|
6
14
|
class Buffer
|
7
15
|
extend Forwardable
|
8
16
|
|
@@ -31,6 +39,10 @@ module HTTPX
|
|
31
39
|
@buffer.bytesize >= @limit
|
32
40
|
end
|
33
41
|
|
42
|
+
def capacity
|
43
|
+
@limit - @buffer.bytesize
|
44
|
+
end
|
45
|
+
|
34
46
|
def shift!(fin)
|
35
47
|
@buffer = @buffer.byteslice(fin..-1) || "".b
|
36
48
|
end
|