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
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
|