httpx 0.21.0 → 1.2.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 +4 -4
- 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_20_0.md +1 -1
- data/doc/release_notes/0_21_0.md +7 -5
- 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/lib/httpx/adapters/datadog.rb +100 -106
- data/lib/httpx/adapters/faraday.rb +143 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +33 -17
- data/lib/httpx/altsvc.rb +61 -24
- 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 +62 -37
- data/lib/httpx/connection/http2.rb +16 -27
- data/lib/httpx/connection.rb +213 -120
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +34 -2
- data/lib/httpx/extensions.rb +4 -134
- data/lib/httpx/io/ssl.rb +77 -71
- data/lib/httpx/io/tcp.rb +46 -70
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +6 -13
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +168 -110
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +13 -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 +1 -3
- data/lib/httpx/plugins/aws_sigv4.rb +5 -6
- 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 +40 -16
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +14 -5
- data/lib/httpx/plugins/circuit_breaker.rb +30 -7
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +20 -10
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +11 -12
- data/lib/httpx/plugins/expect.rb +15 -13
- data/lib/httpx/plugins/follow_redirects.rb +71 -29
- 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 +35 -29
- data/lib/httpx/plugins/h2c.rb +25 -18
- 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 +170 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +15 -10
- data/lib/httpx/plugins/proxy/socks4.rb +8 -6
- data/lib/httpx/plugins/proxy/socks5.rb +10 -8
- data/lib/httpx/plugins/proxy.rb +69 -67
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +3 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +34 -17
- data/lib/httpx/plugins/response_cache.rb +6 -6
- data/lib/httpx/plugins/retries.rb +61 -12
- data/lib/httpx/plugins/ssrf_filter.rb +142 -0
- data/lib/httpx/plugins/stream.rb +27 -32
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +10 -8
- data/lib/httpx/pool.rb +85 -23
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +158 -0
- data/lib/httpx/request.rb +86 -121
- data/lib/httpx/resolver/https.rb +54 -17
- data/lib/httpx/resolver/multi.rb +8 -12
- data/lib/httpx/resolver/native.rb +163 -70
- data/lib/httpx/resolver/resolver.rb +28 -13
- data/lib/httpx/resolver/system.rb +15 -10
- 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 +113 -211
- data/lib/httpx/selector.rb +2 -4
- data/lib/httpx/session.rb +91 -64
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +28 -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 +2 -5
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +3 -3
- 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 +0 -5
- data/lib/httpx/transcoder.rb +4 -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 +1 -0
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +10 -9
- data/sig/connection/http1.rbs +5 -4
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +46 -24
- data/sig/errors.rbs +9 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +26 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +10 -0
- data/sig/options.rbs +28 -12
- 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 +13 -3
- data/sig/plugins/compression.rbs +6 -4
- 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 +11 -2
- 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 +2 -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/socks4.rbs +4 -4
- data/sig/plugins/proxy/socks5.rbs +2 -2
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +10 -4
- data/sig/plugins/response_cache.rbs +12 -3
- data/sig/plugins/retries.rbs +28 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +5 -4
- data/sig/request/body.rbs +40 -0
- data/sig/request.rbs +12 -28
- data/sig/resolver/https.rbs +7 -2
- data/sig/resolver/native.rbs +10 -4
- data/sig/resolver/resolver.rbs +6 -4
- 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 +17 -38
- data/sig/session.rbs +24 -18
- data/sig/timers.rbs +17 -7
- 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 +4 -2
- data/sig/{plugins → transcoder}/multipart.rbs +3 -12
- 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 +1 -1
- data/sig/transcoder.rbs +22 -7
- data/sig/utils.rbs +2 -0
- metadata +127 -40
- 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 -13
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
data/lib/httpx/session.rb
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTPX
|
|
4
|
+
# Class implementing the APIs being used publicly.
|
|
5
|
+
#
|
|
6
|
+
# HTTPX.get(..) #=> delegating to an internal HTTPX::Session object.
|
|
7
|
+
# HTTPX.plugin(..).get(..) #=> creating an intermediate HTTPX::Session with plugin, then sending the GET request
|
|
4
8
|
class Session
|
|
5
9
|
include Loggable
|
|
6
10
|
include Chainable
|
|
7
11
|
|
|
8
12
|
EMPTY_HASH = {}.freeze
|
|
9
13
|
|
|
14
|
+
# initializes the session with a set of +options+, which will be shared by all
|
|
15
|
+
# requests sent from it.
|
|
16
|
+
#
|
|
17
|
+
# When pass a block, it'll yield itself to it, then closes after the block is evaluated.
|
|
10
18
|
def initialize(options = EMPTY_HASH, &blk)
|
|
11
19
|
@options = self.class.default_options.merge(options)
|
|
12
20
|
@responses = {}
|
|
@@ -14,6 +22,11 @@ module HTTPX
|
|
|
14
22
|
wrap(&blk) if blk
|
|
15
23
|
end
|
|
16
24
|
|
|
25
|
+
# Yields itself the block, then closes it after the block is evaluated.
|
|
26
|
+
#
|
|
27
|
+
# session.wrap do |http|
|
|
28
|
+
# http.get("https://wikipedia.com")
|
|
29
|
+
# end # wikipedia connection closes here
|
|
17
30
|
def wrap
|
|
18
31
|
begin
|
|
19
32
|
prev_persistent = @persistent
|
|
@@ -25,10 +38,31 @@ module HTTPX
|
|
|
25
38
|
end
|
|
26
39
|
end
|
|
27
40
|
|
|
41
|
+
# closes all the active connections from the session
|
|
28
42
|
def close(*args)
|
|
29
43
|
pool.close(*args)
|
|
30
44
|
end
|
|
31
45
|
|
|
46
|
+
# performs one, or multple requests; it accepts:
|
|
47
|
+
#
|
|
48
|
+
# 1. one or multiple HTTPX::Request objects;
|
|
49
|
+
# 2. an HTTP verb, then a sequence of URIs or URI/options tuples;
|
|
50
|
+
# 3. one or multiple HTTP verb / uri / (optional) options tuples;
|
|
51
|
+
#
|
|
52
|
+
# when present, the set of +options+ kwargs is applied to all of the
|
|
53
|
+
# sent requests.
|
|
54
|
+
#
|
|
55
|
+
# respectively returns a single HTTPX::Response response, or all of them in an Array, in the same order.
|
|
56
|
+
#
|
|
57
|
+
# resp1 = session.request(req1)
|
|
58
|
+
# resp1, resp2 = session.request(req1, req2)
|
|
59
|
+
# resp1 = session.request("GET", "https://server.org/a")
|
|
60
|
+
# resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"])
|
|
61
|
+
# resp1, resp2 = session.request(["GET", "https://server.org/a"], ["GET", "https://server.org/b"])
|
|
62
|
+
# resp1 = session.request("POST", "https://server.org/a", form: { "foo" => "bar" })
|
|
63
|
+
# resp1, resp2 = session.request(["POST", "https://server.org/a", form: { "foo" => "bar" }], ["GET", "https://server.org/b"])
|
|
64
|
+
# resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"], headers: { "x-api-token" => "TOKEN" })
|
|
65
|
+
#
|
|
32
66
|
def request(*args, **options)
|
|
33
67
|
raise ArgumentError, "must perform at least one request" if args.empty?
|
|
34
68
|
|
|
@@ -39,38 +73,50 @@ module HTTPX
|
|
|
39
73
|
responses
|
|
40
74
|
end
|
|
41
75
|
|
|
76
|
+
# returns a HTTP::Request instance built from the HTTP +verb+, the request +uri+, and
|
|
77
|
+
# the optional set of request-specific +options+. This request **must** be sent through
|
|
78
|
+
# the same session it was built from.
|
|
79
|
+
#
|
|
80
|
+
# req = session.build_request("GET", "https://server.com")
|
|
81
|
+
# resp = session.request(req)
|
|
42
82
|
def build_request(verb, uri, options = EMPTY_HASH)
|
|
43
83
|
rklass = @options.request_class
|
|
44
84
|
options = @options.merge(options) unless options.is_a?(Options)
|
|
45
|
-
request = rklass.new(verb, uri, options
|
|
46
|
-
request.
|
|
47
|
-
request
|
|
85
|
+
request = rklass.new(verb, uri, options)
|
|
86
|
+
request.persistent = @persistent
|
|
87
|
+
set_request_callbacks(request)
|
|
48
88
|
request
|
|
49
89
|
end
|
|
50
90
|
|
|
51
91
|
private
|
|
52
92
|
|
|
93
|
+
# returns the HTTPX::Pool object which manages the networking required to
|
|
94
|
+
# perform requests.
|
|
53
95
|
def pool
|
|
54
96
|
Thread.current[:httpx_connection_pool] ||= Pool.new
|
|
55
97
|
end
|
|
56
98
|
|
|
99
|
+
# callback executed when a response for a given request has been received.
|
|
57
100
|
def on_response(request, response)
|
|
58
101
|
@responses[request] = response
|
|
59
102
|
end
|
|
60
103
|
|
|
104
|
+
# callback executed when an HTTP/2 promise frame has been received.
|
|
61
105
|
def on_promise(_, stream)
|
|
62
106
|
log(level: 2) { "#{stream.id}: refusing stream!" }
|
|
63
107
|
stream.refuse
|
|
64
108
|
end
|
|
65
109
|
|
|
110
|
+
# returns the corresponding HTTP::Response to the given +request+ if it has been received.
|
|
66
111
|
def fetch_response(request, _, _)
|
|
67
112
|
@responses.delete(request)
|
|
68
113
|
end
|
|
69
114
|
|
|
115
|
+
# returns the HTTPX::Connection through which the +request+ should be sent through.
|
|
70
116
|
def find_connection(request, connections, options)
|
|
71
117
|
uri = request.uri
|
|
72
118
|
|
|
73
|
-
connection = pool.find_connection(uri, options) ||
|
|
119
|
+
connection = pool.find_connection(uri, options) || init_connection(uri, options)
|
|
74
120
|
unless connections.nil? || connections.include?(connection)
|
|
75
121
|
connections << connection
|
|
76
122
|
set_connection_callbacks(connection, connections, options)
|
|
@@ -78,6 +124,18 @@ module HTTPX
|
|
|
78
124
|
connection
|
|
79
125
|
end
|
|
80
126
|
|
|
127
|
+
def send_request(request, connections, options = request.options)
|
|
128
|
+
error = catch(:resolve_error) do
|
|
129
|
+
connection = find_connection(request, connections, options)
|
|
130
|
+
connection.send(request)
|
|
131
|
+
end
|
|
132
|
+
return unless error.is_a?(Error)
|
|
133
|
+
|
|
134
|
+
request.emit(:response, ErrorResponse.new(request, error, options))
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# sets the callbacks on the +connection+ required to process certain specific
|
|
138
|
+
# connection lifecycle events which deal with request rerouting.
|
|
81
139
|
def set_connection_callbacks(connection, connections, options)
|
|
82
140
|
connection.only(:misdirected) do |misdirected_request|
|
|
83
141
|
other_connection = connection.create_idle(ssl: { alpn_protocols: %w[http/1.1] })
|
|
@@ -94,17 +152,9 @@ module HTTPX
|
|
|
94
152
|
other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
|
|
95
153
|
connections << other_connection if other_connection
|
|
96
154
|
end
|
|
97
|
-
connection.only(:exhausted) do
|
|
98
|
-
other_connection = connection.create_idle
|
|
99
|
-
other_connection.merge(connection)
|
|
100
|
-
catch(:coalesced) do
|
|
101
|
-
pool.init_connection(other_connection, options)
|
|
102
|
-
end
|
|
103
|
-
set_connection_callbacks(other_connection, connections, options)
|
|
104
|
-
connections << other_connection
|
|
105
|
-
end
|
|
106
155
|
end
|
|
107
156
|
|
|
157
|
+
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
|
108
158
|
def build_altsvc_connection(existing_connection, connections, alt_origin, origin, alt_params, options)
|
|
109
159
|
# do not allow security downgrades on altsvc negotiation
|
|
110
160
|
return if existing_connection.origin.scheme == "https" && alt_origin.scheme != "https"
|
|
@@ -116,30 +166,26 @@ module HTTPX
|
|
|
116
166
|
|
|
117
167
|
alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
|
|
118
168
|
|
|
119
|
-
connection = pool.find_connection(alt_origin, alt_options) ||
|
|
169
|
+
connection = pool.find_connection(alt_origin, alt_options) || init_connection(alt_origin, alt_options)
|
|
170
|
+
|
|
120
171
|
# advertised altsvc is the same origin being used, ignore
|
|
121
172
|
return if connection == existing_connection
|
|
122
173
|
|
|
174
|
+
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
|
175
|
+
|
|
123
176
|
set_connection_callbacks(connection, connections, alt_options)
|
|
124
177
|
|
|
125
178
|
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
|
126
179
|
|
|
127
|
-
# get uninitialized requests
|
|
128
|
-
# incidentally, all requests will be re-routed to the first
|
|
129
|
-
# advertised alt-svc, which incidentally follows the spec.
|
|
130
|
-
existing_connection.purge_pending do |request|
|
|
131
|
-
request.origin == origin &&
|
|
132
|
-
request.state == :idle &&
|
|
133
|
-
!request.headers.key?("alt-used")
|
|
134
|
-
end
|
|
135
|
-
|
|
136
180
|
connection.merge(existing_connection)
|
|
181
|
+
existing_connection.terminate
|
|
137
182
|
connection
|
|
138
183
|
rescue UnsupportedSchemeError
|
|
139
184
|
altsvc["noop"] = true
|
|
140
185
|
nil
|
|
141
186
|
end
|
|
142
187
|
|
|
188
|
+
# returns a set of HTTPX::Request objects built from the given +args+ and +options+.
|
|
143
189
|
def build_requests(*args, options)
|
|
144
190
|
request_options = @options.merge(options)
|
|
145
191
|
|
|
@@ -163,46 +209,39 @@ module HTTPX
|
|
|
163
209
|
requests
|
|
164
210
|
end
|
|
165
211
|
|
|
166
|
-
def
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
else
|
|
174
|
-
raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
connection = options.connection_class.new(type, uri, options)
|
|
212
|
+
def set_request_callbacks(request)
|
|
213
|
+
request.on(:response, &method(:on_response).curry(2)[request])
|
|
214
|
+
request.on(:promise, &method(:on_promise))
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def init_connection(uri, options)
|
|
218
|
+
connection = options.connection_class.new(uri, options)
|
|
178
219
|
catch(:coalesced) do
|
|
179
220
|
pool.init_connection(connection, options)
|
|
180
221
|
connection
|
|
181
222
|
end
|
|
182
223
|
end
|
|
183
224
|
|
|
225
|
+
# sends an array of HTTPX::Request +requests+, returns the respective array of HTTPX::Response objects.
|
|
184
226
|
def send_requests(*requests)
|
|
185
227
|
connections = _send_requests(requests)
|
|
186
228
|
receive_requests(requests, connections)
|
|
187
229
|
end
|
|
188
230
|
|
|
231
|
+
# sends an array of HTTPX::Request objects
|
|
189
232
|
def _send_requests(requests)
|
|
190
233
|
connections = []
|
|
191
234
|
|
|
192
235
|
requests.each do |request|
|
|
193
|
-
|
|
194
|
-
connection = find_connection(request, connections, request.options)
|
|
195
|
-
connection.send(request)
|
|
196
|
-
end
|
|
197
|
-
next unless error.is_a?(ResolveError)
|
|
198
|
-
|
|
199
|
-
request.emit(:response, ErrorResponse.new(request, error, request.options))
|
|
236
|
+
send_request(request, connections)
|
|
200
237
|
end
|
|
201
238
|
|
|
202
239
|
connections
|
|
203
240
|
end
|
|
204
241
|
|
|
242
|
+
# returns the array of HTTPX::Response objects corresponding to the array of HTTPX::Request +requests+.
|
|
205
243
|
def receive_requests(requests, connections)
|
|
244
|
+
# @type var responses: Array[response]
|
|
206
245
|
responses = []
|
|
207
246
|
|
|
208
247
|
begin
|
|
@@ -251,8 +290,14 @@ module HTTPX
|
|
|
251
290
|
super
|
|
252
291
|
klass.instance_variable_set(:@default_options, @default_options)
|
|
253
292
|
klass.instance_variable_set(:@plugins, @plugins.dup)
|
|
293
|
+
klass.instance_variable_set(:@callbacks, @callbacks.dup)
|
|
254
294
|
end
|
|
255
295
|
|
|
296
|
+
# returns a new HTTPX::Session instance, with the plugin pointed by +pl+ loaded.
|
|
297
|
+
#
|
|
298
|
+
# session_with_retries = session.plugin(:retries)
|
|
299
|
+
# session_with_custom = session.plugin(CustomPlugin)
|
|
300
|
+
#
|
|
256
301
|
def plugin(pl, options = nil, &block)
|
|
257
302
|
# raise Error, "Cannot add a plugin to a frozen config" if frozen?
|
|
258
303
|
pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
|
|
@@ -266,19 +311,8 @@ module HTTPX
|
|
|
266
311
|
extend(pl::ClassMethods) if defined?(pl::ClassMethods)
|
|
267
312
|
|
|
268
313
|
opts = @default_options
|
|
269
|
-
opts.
|
|
270
|
-
opts.request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
|
271
|
-
opts.response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
|
272
|
-
opts.response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
|
273
|
-
opts.headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
|
274
|
-
opts.headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
|
275
|
-
opts.request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
|
276
|
-
opts.request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
|
277
|
-
opts.response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
|
278
|
-
opts.response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
|
279
|
-
opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
|
|
314
|
+
opts.extend_with_plugin_classes(pl)
|
|
280
315
|
if defined?(pl::OptionsMethods)
|
|
281
|
-
opts.options_class.__send__(:include, pl::OptionsMethods)
|
|
282
316
|
|
|
283
317
|
(pl::OptionsMethods.instance_methods - Object.instance_methods).each do |meth|
|
|
284
318
|
opts.options_class.method_added(meth)
|
|
@@ -302,16 +336,9 @@ module HTTPX
|
|
|
302
336
|
end
|
|
303
337
|
self
|
|
304
338
|
end
|
|
305
|
-
|
|
306
|
-
# :nocov:
|
|
307
|
-
def plugins(pls)
|
|
308
|
-
warn ":#{__method__} is deprecated, use :plugin instead"
|
|
309
|
-
pls.each do |pl|
|
|
310
|
-
plugin(pl)
|
|
311
|
-
end
|
|
312
|
-
self
|
|
313
|
-
end
|
|
314
|
-
# :nocov:
|
|
315
339
|
end
|
|
316
340
|
end
|
|
341
|
+
|
|
342
|
+
# session may be overridden by certain adapters.
|
|
343
|
+
S = Session
|
|
317
344
|
end
|
|
@@ -9,10 +9,13 @@ module HTTPX
|
|
|
9
9
|
# redefine the default options static var, which needs to
|
|
10
10
|
# refresh options_class
|
|
11
11
|
options = proxy_session.class.default_options.to_hash
|
|
12
|
-
options.freeze
|
|
13
12
|
original_verbosity = $VERBOSE
|
|
14
13
|
$VERBOSE = nil
|
|
14
|
+
const_set(:Options, proxy_session.class.default_options.options_class)
|
|
15
|
+
options[:options_class] = Class.new(options[:options_class])
|
|
16
|
+
options.freeze
|
|
15
17
|
Options.send(:const_set, :DEFAULT_OPTIONS, options)
|
|
18
|
+
Session.instance_variable_set(:@default_options, Options.new(options))
|
|
16
19
|
$VERBOSE = original_verbosity
|
|
17
20
|
end
|
|
18
21
|
|
data/lib/httpx/timers.rb
CHANGED
|
@@ -6,20 +6,27 @@ module HTTPX
|
|
|
6
6
|
@intervals = []
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def after(interval_in_secs, &blk)
|
|
9
|
+
def after(interval_in_secs, cb = nil, &blk)
|
|
10
10
|
return unless interval_in_secs
|
|
11
11
|
|
|
12
|
+
callback = cb || blk
|
|
13
|
+
|
|
12
14
|
# I'm assuming here that most requests will have the same
|
|
13
15
|
# request timeout, as in most cases they share common set of
|
|
14
16
|
# options. A user setting different request timeouts for 100s of
|
|
15
17
|
# requests will already have a hard time dealing with that.
|
|
16
|
-
unless (interval = @intervals.find { |t| t == interval_in_secs })
|
|
18
|
+
unless (interval = @intervals.find { |t| t.interval == interval_in_secs })
|
|
17
19
|
interval = Interval.new(interval_in_secs)
|
|
20
|
+
interval.on_empty { @intervals.delete(interval) }
|
|
18
21
|
@intervals << interval
|
|
19
22
|
@intervals.sort!
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
interval <<
|
|
25
|
+
interval << callback
|
|
26
|
+
|
|
27
|
+
@next_interval_at = nil
|
|
28
|
+
|
|
29
|
+
interval
|
|
23
30
|
end
|
|
24
31
|
|
|
25
32
|
def wait_interval
|
|
@@ -41,11 +48,6 @@ module HTTPX
|
|
|
41
48
|
@next_interval_at = nil if @intervals.empty?
|
|
42
49
|
end
|
|
43
50
|
|
|
44
|
-
def cancel
|
|
45
|
-
@next_interval_at = nil
|
|
46
|
-
@intervals.clear
|
|
47
|
-
end
|
|
48
|
-
|
|
49
51
|
class Interval
|
|
50
52
|
include Comparable
|
|
51
53
|
|
|
@@ -54,6 +56,11 @@ module HTTPX
|
|
|
54
56
|
def initialize(interval)
|
|
55
57
|
@interval = interval
|
|
56
58
|
@callbacks = []
|
|
59
|
+
@on_empty = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def on_empty(&blk)
|
|
63
|
+
@on_empty = blk
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
def <=>(other)
|
|
@@ -74,6 +81,19 @@ module HTTPX
|
|
|
74
81
|
@callbacks << callback
|
|
75
82
|
end
|
|
76
83
|
|
|
84
|
+
def delete(callback)
|
|
85
|
+
@callbacks.delete(callback)
|
|
86
|
+
@on_empty.call if @callbacks.empty?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def no_callbacks?
|
|
90
|
+
@callbacks.empty?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def elapsed?
|
|
94
|
+
@interval <= 0
|
|
95
|
+
end
|
|
96
|
+
|
|
77
97
|
def elapse(elapsed)
|
|
78
98
|
@interval -= elapsed
|
|
79
99
|
|
|
@@ -9,7 +9,6 @@ module HTTPX::Transcoder
|
|
|
9
9
|
module_function
|
|
10
10
|
|
|
11
11
|
class Encoder
|
|
12
|
-
using HTTPX::ArrayExtensions::Sum
|
|
13
12
|
extend Forwardable
|
|
14
13
|
|
|
15
14
|
def_delegator :@raw, :to_s
|
|
@@ -55,5 +54,4 @@ module HTTPX::Transcoder
|
|
|
55
54
|
Encoder.new(body)
|
|
56
55
|
end
|
|
57
56
|
end
|
|
58
|
-
register "body", Body
|
|
59
57
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
require_relative "utils/deflater"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Transcoder
|
|
8
|
+
module Deflate
|
|
9
|
+
class Deflater < Transcoder::Deflater
|
|
10
|
+
def deflate(chunk)
|
|
11
|
+
@deflater ||= Zlib::Deflate.new
|
|
12
|
+
|
|
13
|
+
if chunk.nil?
|
|
14
|
+
unless @deflater.closed?
|
|
15
|
+
last = @deflater.finish
|
|
16
|
+
@deflater.close
|
|
17
|
+
last.empty? ? nil : last
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
@deflater.deflate(chunk)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def encode(body)
|
|
28
|
+
Deflater.new(body)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decode(response, bytesize: nil)
|
|
32
|
+
bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
|
|
33
|
+
GZIP::Inflater.new(bytesize)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -2,58 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
require "forwardable"
|
|
4
4
|
require "uri"
|
|
5
|
+
require_relative "multipart"
|
|
5
6
|
|
|
6
|
-
module HTTPX
|
|
7
|
-
module
|
|
8
|
-
|
|
7
|
+
module HTTPX
|
|
8
|
+
module Transcoder
|
|
9
|
+
module Form
|
|
10
|
+
module_function
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
PARAM_DEPTH_LIMIT = 32
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
class Encoder
|
|
15
|
+
extend Forwardable
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
def_delegator :@raw, :to_s
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
def_delegator :@raw, :to_str
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
def_delegator :@raw, :bytesize
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def initialize(form)
|
|
24
|
+
@raw = form.each_with_object("".b) do |(key, val), buf|
|
|
25
|
+
HTTPX::Transcoder.normalize_keys(key, val) do |k, v|
|
|
26
|
+
buf << "&" unless buf.empty?
|
|
27
|
+
buf << URI.encode_www_form_component(k)
|
|
28
|
+
buf << "=#{URI.encode_www_form_component(v.to_s)}" unless v.nil?
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
|
-
end
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
def content_type
|
|
34
|
+
"application/x-www-form-urlencoded"
|
|
35
|
+
end
|
|
33
36
|
end
|
|
34
|
-
end
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
module Decoder
|
|
39
|
+
module_function
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
def call(response, *)
|
|
42
|
+
URI.decode_www_form(response.to_s).each_with_object({}) do |(field, value), params|
|
|
43
|
+
HTTPX::Transcoder.normalize_query(params, field, value, PARAM_DEPTH_LIMIT)
|
|
44
|
+
end
|
|
42
45
|
end
|
|
43
46
|
end
|
|
44
|
-
end
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
def encode(form)
|
|
49
|
+
if multipart?(form)
|
|
50
|
+
Multipart::Encoder.new(form)
|
|
51
|
+
else
|
|
52
|
+
Encoder.new(form)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
def decode(response)
|
|
57
|
+
content_type = response.content_type.mime_type
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
case content_type
|
|
60
|
+
when "application/x-www-form-urlencoded"
|
|
61
|
+
Decoder
|
|
62
|
+
when "multipart/form-data"
|
|
63
|
+
Multipart::Decoder.new(response)
|
|
64
|
+
else
|
|
65
|
+
raise Error, "invalid form mime type (#{content_type})"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
def multipart?(data)
|
|
70
|
+
data.any? do |_, v|
|
|
71
|
+
Multipart::MULTIPART_VALUE_COND.call(v) ||
|
|
72
|
+
(v.respond_to?(:to_ary) && v.to_ary.any?(&Multipart::MULTIPART_VALUE_COND)) ||
|
|
73
|
+
(v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| Multipart::MULTIPART_VALUE_COND.call(e) })
|
|
74
|
+
end
|
|
75
|
+
end
|
|
56
76
|
end
|
|
57
77
|
end
|
|
58
|
-
register "form", Form
|
|
59
78
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "stringio"
|
|
6
|
+
require "zlib"
|
|
7
|
+
|
|
8
|
+
module HTTPX
|
|
9
|
+
module Transcoder
|
|
10
|
+
module GZIP
|
|
11
|
+
class Deflater < Transcoder::Deflater
|
|
12
|
+
def initialize(body)
|
|
13
|
+
@compressed_chunk = "".b
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def deflate(chunk)
|
|
18
|
+
@deflater ||= Zlib::GzipWriter.new(self)
|
|
19
|
+
|
|
20
|
+
if chunk.nil?
|
|
21
|
+
unless @deflater.closed?
|
|
22
|
+
@deflater.flush
|
|
23
|
+
@deflater.close
|
|
24
|
+
compressed_chunk
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
@deflater.write(chunk)
|
|
28
|
+
compressed_chunk
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def write(chunk)
|
|
35
|
+
@compressed_chunk << chunk
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def compressed_chunk
|
|
39
|
+
@compressed_chunk.dup
|
|
40
|
+
ensure
|
|
41
|
+
@compressed_chunk.clear
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Inflater
|
|
46
|
+
def initialize(bytesize)
|
|
47
|
+
@inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
|
48
|
+
@bytesize = bytesize
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def call(chunk)
|
|
52
|
+
buffer = @inflater.inflate(chunk)
|
|
53
|
+
@bytesize -= chunk.bytesize
|
|
54
|
+
if @bytesize <= 0
|
|
55
|
+
buffer << @inflater.finish
|
|
56
|
+
@inflater.close
|
|
57
|
+
end
|
|
58
|
+
buffer
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module_function
|
|
63
|
+
|
|
64
|
+
def encode(body)
|
|
65
|
+
Deflater.new(body)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def decode(response, bytesize: nil)
|
|
69
|
+
bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
|
|
70
|
+
Inflater.new(bytesize)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -4,12 +4,10 @@ require "forwardable"
|
|
|
4
4
|
|
|
5
5
|
module HTTPX::Transcoder
|
|
6
6
|
module JSON
|
|
7
|
-
JSON_REGEX = %r{\bapplication/(?:vnd\.api\+)?json\b}i.freeze
|
|
8
|
-
|
|
9
|
-
using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?)
|
|
10
|
-
|
|
11
7
|
module_function
|
|
12
8
|
|
|
9
|
+
JSON_REGEX = %r{\bapplication/(?:vnd\.api\+|hal\+)?json\b}i.freeze
|
|
10
|
+
|
|
13
11
|
class Encoder
|
|
14
12
|
extend Forwardable
|
|
15
13
|
|
|
@@ -56,5 +54,4 @@ module HTTPX::Transcoder
|
|
|
56
54
|
end
|
|
57
55
|
# rubocop:enable Style/SingleLineMethods
|
|
58
56
|
end
|
|
59
|
-
register "json", JSON
|
|
60
57
|
end
|