httpx 0.20.0 → 1.3.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/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
data/lib/httpx/plugins/grpc.rb
CHANGED
@@ -16,7 +16,7 @@ module HTTPX
|
|
16
16
|
#
|
17
17
|
# This plugin adds DSL to build GRPC interfaces.
|
18
18
|
#
|
19
|
-
# https://gitlab.com/
|
19
|
+
# https://gitlab.com/os85/httpx/wikis/GRPC
|
20
20
|
#
|
21
21
|
module GRPC
|
22
22
|
unless String.method_defined?(:underscore)
|
@@ -49,20 +49,19 @@ module HTTPX
|
|
49
49
|
class << self
|
50
50
|
def load_dependencies(*)
|
51
51
|
require "stringio"
|
52
|
+
require "httpx/plugins/grpc/grpc_encoding"
|
52
53
|
require "httpx/plugins/grpc/message"
|
53
54
|
require "httpx/plugins/grpc/call"
|
54
55
|
end
|
55
56
|
|
56
57
|
def configure(klass)
|
57
58
|
klass.plugin(:persistent)
|
58
|
-
klass.plugin(:compression)
|
59
59
|
klass.plugin(:stream)
|
60
60
|
end
|
61
61
|
|
62
62
|
def extra_options(options)
|
63
63
|
options.merge(
|
64
64
|
fallback_protocol: "h2",
|
65
|
-
http2_settings: { wait_for_handshake: false },
|
66
65
|
grpc_rpcs: {}.freeze,
|
67
66
|
grpc_compression: false,
|
68
67
|
grpc_deadline: DEADLINE
|
@@ -108,14 +107,24 @@ module HTTPX
|
|
108
107
|
@trailing_metadata = Hash[trailers]
|
109
108
|
super
|
110
109
|
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module RequestBodyMethods
|
113
|
+
def initialize(*, **)
|
114
|
+
super
|
111
115
|
|
112
|
-
|
113
|
-
|
116
|
+
if (compression = @headers["grpc-encoding"])
|
117
|
+
deflater_body = self.class.initialize_deflater_body(@body, compression)
|
118
|
+
@body = Transcoder::GRPCEncoding.encode(deflater_body || @body, compressed: !deflater_body.nil?)
|
119
|
+
else
|
120
|
+
@body = Transcoder::GRPCEncoding.encode(@body, compressed: false)
|
121
|
+
end
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
117
125
|
module InstanceMethods
|
118
126
|
def with_channel_credentials(ca_path, key = nil, cert = nil, **ssl_opts)
|
127
|
+
# @type var ssl_params: ::Hash[::Symbol, untyped]
|
119
128
|
ssl_params = {
|
120
129
|
**ssl_opts,
|
121
130
|
ca_file: ca_path,
|
@@ -141,17 +150,29 @@ module HTTPX
|
|
141
150
|
deadline: @options.grpc_deadline,
|
142
151
|
}.merge(opts)
|
143
152
|
|
153
|
+
local_rpc_name = rpc_name.underscore
|
154
|
+
|
144
155
|
session_class = Class.new(self.class) do
|
156
|
+
# define rpc method with ruby style name
|
145
157
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
146
|
-
def #{
|
147
|
-
rpc_execute("#{
|
148
|
-
end
|
158
|
+
def #{local_rpc_name}(input, **opts) # def grpc_action(input, **opts)
|
159
|
+
rpc_execute("#{local_rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
|
160
|
+
end # end
|
149
161
|
OUT
|
162
|
+
|
163
|
+
# define rpc method with original name
|
164
|
+
unless local_rpc_name == rpc_name
|
165
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
166
|
+
def #{rpc_name}(input, **opts) # def grpcAction(input, **opts)
|
167
|
+
rpc_execute("#{local_rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
|
168
|
+
end # end
|
169
|
+
OUT
|
170
|
+
end
|
150
171
|
end
|
151
172
|
|
152
173
|
session_class.new(@options.merge(
|
153
174
|
grpc_rpcs: @options.grpc_rpcs.merge(
|
154
|
-
|
175
|
+
local_rpc_name => [rpc_name, input, output, rpc_opts]
|
155
176
|
).freeze
|
156
177
|
))
|
157
178
|
end
|
@@ -195,7 +216,7 @@ module HTTPX
|
|
195
216
|
**opts)
|
196
217
|
grpc_request = build_grpc_request(rpc_method, input, deadline: deadline, metadata: metadata, **opts)
|
197
218
|
response = request(grpc_request, **opts)
|
198
|
-
response.raise_for_status
|
219
|
+
response.raise_for_status unless opts[:stream]
|
199
220
|
GRPC::Call.new(response)
|
200
221
|
end
|
201
222
|
|
@@ -233,7 +254,7 @@ module HTTPX
|
|
233
254
|
uri.path = rpc_method
|
234
255
|
|
235
256
|
headers = HEADERS.merge(
|
236
|
-
"grpc-accept-encoding" => ["identity", *@options.
|
257
|
+
"grpc-accept-encoding" => ["identity", *@options.supported_compression_formats]
|
237
258
|
)
|
238
259
|
unless deadline == Float::INFINITY
|
239
260
|
# convert to milliseconds
|
@@ -241,30 +262,16 @@ module HTTPX
|
|
241
262
|
headers["grpc-timeout"] = "#{deadline}m"
|
242
263
|
end
|
243
264
|
|
244
|
-
headers = headers.merge(metadata) if metadata
|
265
|
+
headers = headers.merge(metadata.transform_keys(&:to_s)) if metadata
|
245
266
|
|
246
267
|
# prepare compressor
|
247
|
-
deflater = nil
|
248
268
|
compression = @options.grpc_compression == true ? "gzip" : @options.grpc_compression
|
249
269
|
|
250
|
-
if compression
|
251
|
-
headers["grpc-encoding"] = compression
|
252
|
-
deflater = @options.encodings.registry(compression).deflater
|
253
|
-
end
|
254
|
-
|
255
|
-
headers.merge!(@options.call_credentials.call) if @options.call_credentials
|
270
|
+
headers["grpc-encoding"] = compression if compression
|
256
271
|
|
257
|
-
|
258
|
-
Enumerator.new do |y|
|
259
|
-
input.each do |message|
|
260
|
-
y << Message.encode(message, deflater: deflater)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
else
|
264
|
-
Message.encode(input, deflater: deflater)
|
265
|
-
end
|
272
|
+
headers.merge!(@options.call_credentials.call.transform_keys(&:to_s)) if @options.call_credentials
|
266
273
|
|
267
|
-
build_request(
|
274
|
+
build_request("POST", uri, headers: headers, body: input)
|
268
275
|
end
|
269
276
|
end
|
270
277
|
end
|
data/lib/httpx/plugins/h2c.rb
CHANGED
@@ -4,21 +4,16 @@ module HTTPX
|
|
4
4
|
module Plugins
|
5
5
|
#
|
6
6
|
# This plugin adds support for upgrading a plaintext HTTP/1.1 connection to HTTP/2
|
7
|
-
# (https://
|
7
|
+
# (https://datatracker.ietf.org/doc/html/rfc7540#section-3.2)
|
8
8
|
#
|
9
|
-
# https://gitlab.com/
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2c
|
10
10
|
#
|
11
11
|
module H2C
|
12
|
-
VALID_H2C_VERBS = %
|
12
|
+
VALID_H2C_VERBS = %w[GET OPTIONS HEAD].freeze
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def load_dependencies(
|
16
|
-
require "base64"
|
17
|
-
end
|
18
|
-
|
19
|
-
def configure(klass)
|
15
|
+
def load_dependencies(klass)
|
20
16
|
klass.plugin(:upgrade)
|
21
|
-
klass.default_options.upgrade_handlers.register "h2c", self
|
22
17
|
end
|
23
18
|
|
24
19
|
def call(connection, request, response)
|
@@ -26,7 +21,7 @@ module HTTPX
|
|
26
21
|
end
|
27
22
|
|
28
23
|
def extra_options(options)
|
29
|
-
options.merge(max_concurrent_requests: 1)
|
24
|
+
options.merge(max_concurrent_requests: 1, upgrade_handlers: options.upgrade_handlers.merge("h2c" => self))
|
30
25
|
end
|
31
26
|
end
|
32
27
|
|
@@ -38,13 +33,13 @@ module HTTPX
|
|
38
33
|
|
39
34
|
connection = pool.find_connection(upgrade_request.uri, upgrade_request.options)
|
40
35
|
|
41
|
-
return super if connection && connection.upgrade_protocol ==
|
36
|
+
return super if connection && connection.upgrade_protocol == "h2c"
|
42
37
|
|
43
38
|
# build upgrade request
|
44
39
|
upgrade_request.headers.add("connection", "upgrade")
|
45
40
|
upgrade_request.headers.add("connection", "http2-settings")
|
46
41
|
upgrade_request.headers["upgrade"] = "h2c"
|
47
|
-
upgrade_request.headers["http2-settings"] =
|
42
|
+
upgrade_request.headers["http2-settings"] = ::HTTP2::Client.settings_header(upgrade_request.options.http2_settings)
|
48
43
|
|
49
44
|
super(upgrade_request, *remainder)
|
50
45
|
end
|
@@ -78,22 +73,34 @@ module HTTPX
|
|
78
73
|
@inflight -= prev_parser.requests.size
|
79
74
|
end
|
80
75
|
|
81
|
-
|
82
|
-
@parser = H2CParser.new(@write_buffer, parser_options)
|
76
|
+
@parser = H2CParser.new(@write_buffer, @options)
|
83
77
|
set_parser_callbacks(@parser)
|
84
78
|
@inflight += 1
|
85
79
|
@parser.upgrade(request, response)
|
86
|
-
@upgrade_protocol =
|
87
|
-
|
88
|
-
if request.options.max_concurrent_requests != @options.max_concurrent_requests
|
89
|
-
@options = @options.merge(max_concurrent_requests: nil)
|
90
|
-
end
|
80
|
+
@upgrade_protocol = "h2c"
|
91
81
|
|
92
82
|
prev_parser.requests.each do |req|
|
93
83
|
req.transition(:idle)
|
94
84
|
send(req)
|
95
85
|
end
|
96
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def send_request_to_parser(request)
|
91
|
+
super
|
92
|
+
|
93
|
+
return unless request.headers["upgrade"] == "h2c" && parser.is_a?(Connection::HTTP1)
|
94
|
+
|
95
|
+
max_concurrent_requests = parser.max_concurrent_requests
|
96
|
+
|
97
|
+
return if max_concurrent_requests == 1
|
98
|
+
|
99
|
+
parser.max_concurrent_requests = 1
|
100
|
+
request.once(:response) do
|
101
|
+
parser.max_concurrent_requests = max_concurrent_requests
|
102
|
+
end
|
103
|
+
end
|
97
104
|
end
|
98
105
|
end
|
99
106
|
register_plugin(:h2c, H2C)
|
@@ -32,6 +32,15 @@ module HTTPX
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
module NativeResolverMethods
|
36
|
+
def transition(nextstate)
|
37
|
+
state = @state
|
38
|
+
val = super
|
39
|
+
meter_elapsed_time("Resolver::Native: #{state} -> #{nextstate}")
|
40
|
+
val
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
module InstanceMethods
|
36
45
|
def self.included(klass)
|
37
46
|
klass.prepend TrackTimeMethods
|
@@ -42,6 +51,13 @@ module HTTPX
|
|
42
51
|
meter_elapsed_time("Session: initializing...")
|
43
52
|
super
|
44
53
|
meter_elapsed_time("Session: initialized!!!")
|
54
|
+
resolver_type = @options.resolver_class
|
55
|
+
resolver_type = Resolver.resolver_for(resolver_type)
|
56
|
+
return unless resolver_type <= Resolver::Native
|
57
|
+
|
58
|
+
resolver_type.prepend TrackTimeMethods
|
59
|
+
resolver_type.prepend NativeResolverMethods
|
60
|
+
@options = @options.merge(resolver_class: resolver_type)
|
45
61
|
end
|
46
62
|
|
47
63
|
def close(*)
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# https://gitlab.com/
|
6
|
+
# https://gitlab.com/os85/httpx/wikis/Auth#ntlm-auth
|
7
7
|
#
|
8
8
|
module NTLMAuth
|
9
9
|
class << self
|
10
10
|
def load_dependencies(_klass)
|
11
|
-
require_relative "
|
11
|
+
require_relative "auth/ntlm"
|
12
12
|
end
|
13
13
|
|
14
14
|
def extra_options(options)
|
@@ -25,11 +25,11 @@ module HTTPX
|
|
25
25
|
end
|
26
26
|
|
27
27
|
module InstanceMethods
|
28
|
-
def
|
28
|
+
def ntlm_auth(user, password, domain = nil)
|
29
29
|
with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
private
|
33
33
|
|
34
34
|
def send_requests(*requests)
|
35
35
|
requests.flat_map do |request|
|
@@ -39,6 +39,8 @@ module HTTPX
|
|
39
39
|
request.headers["authorization"] = ntlm.negotiate
|
40
40
|
probe_response = wrap { super(request).first }
|
41
41
|
|
42
|
+
return probe_response unless probe_response.is_a?(Response)
|
43
|
+
|
42
44
|
if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
|
43
45
|
request.transition(:idle)
|
44
46
|
request.headers["authorization"] = ntlm.authenticate(request, probe_response.headers["www-authenticate"])
|
@@ -53,6 +55,6 @@ module HTTPX
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
56
|
-
register_plugin :
|
58
|
+
register_plugin :ntlm_auth, NTLMAuth
|
57
59
|
end
|
58
60
|
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# https://gitlab.com/os85/httpx/wikis/OAuth
|
7
|
+
#
|
8
|
+
module OAuth
|
9
|
+
class << self
|
10
|
+
def load_dependencies(_klass)
|
11
|
+
require_relative "auth/basic"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
SUPPORTED_GRANT_TYPES = %w[client_credentials refresh_token].freeze
|
16
|
+
SUPPORTED_AUTH_METHODS = %w[client_secret_basic client_secret_post].freeze
|
17
|
+
|
18
|
+
class OAuthSession
|
19
|
+
attr_reader :grant_type, :client_id, :client_secret, :access_token, :refresh_token, :scope
|
20
|
+
|
21
|
+
def initialize(
|
22
|
+
issuer:,
|
23
|
+
client_id:,
|
24
|
+
client_secret:,
|
25
|
+
access_token: nil,
|
26
|
+
refresh_token: nil,
|
27
|
+
scope: nil,
|
28
|
+
token_endpoint: nil,
|
29
|
+
response_type: nil,
|
30
|
+
grant_type: nil,
|
31
|
+
token_endpoint_auth_method: nil
|
32
|
+
)
|
33
|
+
@issuer = URI(issuer)
|
34
|
+
@client_id = client_id
|
35
|
+
@client_secret = client_secret
|
36
|
+
@token_endpoint = URI(token_endpoint) if token_endpoint
|
37
|
+
@response_type = response_type
|
38
|
+
@scope = case scope
|
39
|
+
when String
|
40
|
+
scope.split
|
41
|
+
when Array
|
42
|
+
scope
|
43
|
+
end
|
44
|
+
@access_token = access_token
|
45
|
+
@refresh_token = refresh_token
|
46
|
+
@token_endpoint_auth_method = String(token_endpoint_auth_method) if token_endpoint_auth_method
|
47
|
+
@grant_type = grant_type || (@refresh_token ? "refresh_token" : "client_credentials")
|
48
|
+
|
49
|
+
unless @token_endpoint_auth_method.nil? || SUPPORTED_AUTH_METHODS.include?(@token_endpoint_auth_method)
|
50
|
+
raise Error, "#{@token_endpoint_auth_method} is not a supported auth method"
|
51
|
+
end
|
52
|
+
|
53
|
+
return if SUPPORTED_GRANT_TYPES.include?(@grant_type)
|
54
|
+
|
55
|
+
raise Error, "#{@grant_type} is not a supported grant type"
|
56
|
+
end
|
57
|
+
|
58
|
+
def token_endpoint
|
59
|
+
@token_endpoint || "#{@issuer}/token"
|
60
|
+
end
|
61
|
+
|
62
|
+
def token_endpoint_auth_method
|
63
|
+
@token_endpoint_auth_method || "client_secret_basic"
|
64
|
+
end
|
65
|
+
|
66
|
+
def load(http)
|
67
|
+
return if @grant_type && @scope
|
68
|
+
|
69
|
+
metadata = http.get("#{@issuer}/.well-known/oauth-authorization-server").raise_for_status.json
|
70
|
+
|
71
|
+
@token_endpoint = metadata["token_endpoint"]
|
72
|
+
@scope = metadata["scopes_supported"]
|
73
|
+
@grant_type = Array(metadata["grant_types_supported"]).find { |gr| SUPPORTED_GRANT_TYPES.include?(gr) }
|
74
|
+
@token_endpoint_auth_method = Array(metadata["token_endpoint_auth_methods_supported"]).find do |am|
|
75
|
+
SUPPORTED_AUTH_METHODS.include?(am)
|
76
|
+
end
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def merge(other)
|
81
|
+
obj = dup
|
82
|
+
|
83
|
+
case other
|
84
|
+
when OAuthSession
|
85
|
+
other.instance_variables.each do |ivar|
|
86
|
+
val = other.instance_variable_get(ivar)
|
87
|
+
next unless val
|
88
|
+
|
89
|
+
obj.instance_variable_set(ivar, val)
|
90
|
+
end
|
91
|
+
when Hash
|
92
|
+
other.each do |k, v|
|
93
|
+
obj.instance_variable_set(:"@#{k}", v) if obj.instance_variable_defined?(:"@#{k}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
obj
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module OptionsMethods
|
101
|
+
def option_oauth_session(value)
|
102
|
+
case value
|
103
|
+
when Hash
|
104
|
+
OAuthSession.new(**value)
|
105
|
+
when OAuthSession
|
106
|
+
value
|
107
|
+
else
|
108
|
+
raise TypeError, ":oauth_session must be a #{OAuthSession}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
module InstanceMethods
|
114
|
+
def oauth_auth(**args)
|
115
|
+
with(oauth_session: OAuthSession.new(**args))
|
116
|
+
end
|
117
|
+
|
118
|
+
def with_access_token
|
119
|
+
oauth_session = @options.oauth_session
|
120
|
+
|
121
|
+
oauth_session.load(self)
|
122
|
+
|
123
|
+
grant_type = oauth_session.grant_type
|
124
|
+
|
125
|
+
headers = {}
|
126
|
+
form_post = { "grant_type" => grant_type, "scope" => Array(oauth_session.scope).join(" ") }.compact
|
127
|
+
|
128
|
+
# auth
|
129
|
+
case oauth_session.token_endpoint_auth_method
|
130
|
+
when "client_secret_post"
|
131
|
+
form_post["client_id"] = oauth_session.client_id
|
132
|
+
form_post["client_secret"] = oauth_session.client_secret
|
133
|
+
when "client_secret_basic"
|
134
|
+
headers["authorization"] = Authentication::Basic.new(oauth_session.client_id, oauth_session.client_secret).authenticate
|
135
|
+
end
|
136
|
+
|
137
|
+
case grant_type
|
138
|
+
when "client_credentials"
|
139
|
+
# do nothing
|
140
|
+
when "refresh_token"
|
141
|
+
form_post["refresh_token"] = oauth_session.refresh_token
|
142
|
+
end
|
143
|
+
|
144
|
+
token_request = build_request("POST", oauth_session.token_endpoint, headers: headers, form: form_post)
|
145
|
+
token_request.headers.delete("authorization") unless oauth_session.token_endpoint_auth_method == "client_secret_basic"
|
146
|
+
|
147
|
+
token_response = request(token_request)
|
148
|
+
token_response.raise_for_status
|
149
|
+
|
150
|
+
payload = token_response.json
|
151
|
+
|
152
|
+
access_token = payload["access_token"]
|
153
|
+
refresh_token = payload["refresh_token"]
|
154
|
+
|
155
|
+
with(oauth_session: oauth_session.merge(access_token: access_token, refresh_token: refresh_token))
|
156
|
+
end
|
157
|
+
|
158
|
+
def build_request(*)
|
159
|
+
request = super
|
160
|
+
|
161
|
+
return request if request.headers.key?("authorization")
|
162
|
+
|
163
|
+
oauth_session = @options.oauth_session
|
164
|
+
|
165
|
+
return request unless oauth_session && oauth_session.access_token
|
166
|
+
|
167
|
+
request.headers["authorization"] = "Bearer #{oauth_session.access_token}"
|
168
|
+
|
169
|
+
request
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
register_plugin :oauth, OAuth
|
174
|
+
end
|
175
|
+
end
|
@@ -15,7 +15,7 @@ module HTTPX
|
|
15
15
|
# This plugin is also not recommendable when connecting to >9000 (like, a lot) different origins.
|
16
16
|
# So when you use this, make sure that you don't fall into this trap.
|
17
17
|
#
|
18
|
-
# https://gitlab.com/
|
18
|
+
# https://gitlab.com/os85/httpx/wikis/Persistent
|
19
19
|
#
|
20
20
|
module Persistent
|
21
21
|
def self.load_dependencies(klass)
|
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "base64"
|
4
|
-
|
5
3
|
module HTTPX
|
6
4
|
module Plugins
|
7
5
|
module Proxy
|
8
6
|
module HTTP
|
7
|
+
class << self
|
8
|
+
def extra_options(options)
|
9
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[http])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
9
13
|
module InstanceMethods
|
10
14
|
def with_proxy_basic_auth(opts)
|
11
15
|
with(proxy: opts.merge(scheme: "basic"))
|
@@ -23,17 +27,23 @@ module HTTPX
|
|
23
27
|
response = super
|
24
28
|
|
25
29
|
if response &&
|
30
|
+
response.is_a?(Response) &&
|
26
31
|
response.status == 407 &&
|
27
32
|
!request.headers.key?("proxy-authorization") &&
|
28
33
|
response.headers.key?("proxy-authenticate")
|
29
34
|
|
30
|
-
|
35
|
+
uri = request.uri
|
36
|
+
|
37
|
+
proxy_options = proxy_options(uri, options)
|
38
|
+
connection = connections.find do |conn|
|
39
|
+
conn.match?(uri, proxy_options)
|
40
|
+
end
|
31
41
|
|
32
|
-
if connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
42
|
+
if connection && connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
33
43
|
request.transition(:idle)
|
34
44
|
request.headers["proxy-authorization"] =
|
35
45
|
connection.options.proxy.authenticate(request, response.headers["proxy-authenticate"])
|
36
|
-
|
46
|
+
send_request(request, connections)
|
37
47
|
return
|
38
48
|
end
|
39
49
|
end
|
@@ -60,7 +70,7 @@ module HTTPX
|
|
60
70
|
return unless @io.connected?
|
61
71
|
|
62
72
|
@parser || begin
|
63
|
-
@parser =
|
73
|
+
@parser = self.class.parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
64
74
|
parser = @parser
|
65
75
|
parser.extend(ProxyParser)
|
66
76
|
parser.on(:response, &method(:__http_on_connect))
|
@@ -71,7 +81,6 @@ module HTTPX
|
|
71
81
|
else
|
72
82
|
transition(:closing)
|
73
83
|
transition(:closed)
|
74
|
-
emit(:reset)
|
75
84
|
|
76
85
|
parser.reset if @parser
|
77
86
|
transition(:idle)
|
@@ -113,13 +122,14 @@ module HTTPX
|
|
113
122
|
|
114
123
|
def __http_on_connect(request, response)
|
115
124
|
@inflight -= 1
|
116
|
-
if response.status == 200
|
125
|
+
if response.is_a?(Response) && response.status == 200
|
117
126
|
req = @pending.first
|
118
127
|
request_uri = req.uri
|
119
128
|
@io = ProxySSL.new(@io, request_uri, @options)
|
120
129
|
transition(:connected)
|
121
130
|
throw(:called)
|
122
|
-
elsif response.
|
131
|
+
elsif response.is_a?(Response) &&
|
132
|
+
response.status == 407 &&
|
123
133
|
!request.headers.key?("proxy-authorization") &&
|
124
134
|
@options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
125
135
|
|
@@ -139,9 +149,9 @@ module HTTPX
|
|
139
149
|
|
140
150
|
module ProxyParser
|
141
151
|
def join_headline(request)
|
142
|
-
return super if request.verb ==
|
152
|
+
return super if request.verb == "CONNECT"
|
143
153
|
|
144
|
-
"#{request.verb
|
154
|
+
"#{request.verb} #{request.uri} HTTP/#{@version.join(".")}"
|
145
155
|
end
|
146
156
|
|
147
157
|
def set_protocol_headers(request)
|
@@ -158,8 +168,8 @@ module HTTPX
|
|
158
168
|
end
|
159
169
|
|
160
170
|
class ConnectRequest < Request
|
161
|
-
def initialize(uri,
|
162
|
-
super(
|
171
|
+
def initialize(uri, options)
|
172
|
+
super("CONNECT", uri, options)
|
163
173
|
@headers.delete("accept")
|
164
174
|
end
|
165
175
|
|
@@ -4,7 +4,7 @@ require "resolv"
|
|
4
4
|
require "ipaddr"
|
5
5
|
|
6
6
|
module HTTPX
|
7
|
-
class Socks4Error <
|
7
|
+
class Socks4Error < HTTPProxyError; end
|
8
8
|
|
9
9
|
module Plugins
|
10
10
|
module Proxy
|
@@ -16,6 +16,12 @@ module HTTPX
|
|
16
16
|
|
17
17
|
Error = Socks4Error
|
18
18
|
|
19
|
+
class << self
|
20
|
+
def extra_options(options)
|
21
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + PROTOCOLS)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
module ConnectionMethods
|
20
26
|
def interests
|
21
27
|
if @state == :connecting
|
@@ -79,15 +85,11 @@ module HTTPX
|
|
79
85
|
end
|
80
86
|
|
81
87
|
class SocksParser
|
82
|
-
include Callbacks
|
88
|
+
include HTTPX::Callbacks
|
83
89
|
|
84
90
|
def initialize(buffer, options)
|
85
91
|
@buffer = buffer
|
86
|
-
@options =
|
87
|
-
end
|
88
|
-
|
89
|
-
def timeout
|
90
|
-
@options.timeout[:operation_timeout]
|
92
|
+
@options = options
|
91
93
|
end
|
92
94
|
|
93
95
|
def close; end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
-
class Socks5Error <
|
4
|
+
class Socks5Error < HTTPProxyError; end
|
5
5
|
|
6
6
|
module Plugins
|
7
7
|
module Proxy
|
@@ -18,8 +18,14 @@ module HTTPX
|
|
18
18
|
|
19
19
|
Error = Socks5Error
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
class << self
|
22
|
+
def load_dependencies(*)
|
23
|
+
require_relative "../auth/socks5"
|
24
|
+
end
|
25
|
+
|
26
|
+
def extra_options(options)
|
27
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[socks5])
|
28
|
+
end
|
23
29
|
end
|
24
30
|
|
25
31
|
module ConnectionMethods
|
@@ -131,15 +137,11 @@ module HTTPX
|
|
131
137
|
end
|
132
138
|
|
133
139
|
class SocksParser
|
134
|
-
include Callbacks
|
140
|
+
include HTTPX::Callbacks
|
135
141
|
|
136
142
|
def initialize(buffer, options)
|
137
143
|
@buffer = buffer
|
138
|
-
@options =
|
139
|
-
end
|
140
|
-
|
141
|
-
def timeout
|
142
|
-
@options.timeout[:operation_timeout]
|
144
|
+
@options = options
|
143
145
|
end
|
144
146
|
|
145
147
|
def close; end
|