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
@@ -3,9 +3,9 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds helper methods to implement HTTP Digest Auth (https://
|
6
|
+
# This plugin adds helper methods to implement HTTP Digest Auth (https://datatracker.ietf.org/doc/html/rfc7616)
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Auth#digest-auth
|
9
9
|
#
|
10
10
|
module DigestAuth
|
11
11
|
DigestError = Class.new(Error)
|
@@ -16,36 +16,38 @@ module HTTPX
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def load_dependencies(*)
|
19
|
-
require_relative "
|
19
|
+
require_relative "auth/digest"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
# adds support for the following options:
|
24
|
+
#
|
25
|
+
# :digest :: instance of HTTPX::Plugins::Authentication::Digest, used to authenticate requests in the session.
|
23
26
|
module OptionsMethods
|
24
27
|
def option_digest(value)
|
25
|
-
raise TypeError, ":digest must be a Digest" unless value.is_a?(Authentication::Digest)
|
28
|
+
raise TypeError, ":digest must be a #{Authentication::Digest}" unless value.is_a?(Authentication::Digest)
|
26
29
|
|
27
30
|
value
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
34
|
module InstanceMethods
|
32
|
-
def
|
33
|
-
with(digest: Authentication::Digest.new(user, password))
|
35
|
+
def digest_auth(user, password, hashed: false)
|
36
|
+
with(digest: Authentication::Digest.new(user, password, hashed: hashed))
|
34
37
|
end
|
35
38
|
|
36
|
-
|
39
|
+
private
|
37
40
|
|
38
41
|
def send_requests(*requests)
|
39
42
|
requests.flat_map do |request|
|
40
43
|
digest = request.options.digest
|
41
44
|
|
42
|
-
unless digest
|
43
|
-
super(request)
|
44
|
-
next
|
45
|
-
end
|
45
|
+
next super(request) unless digest
|
46
46
|
|
47
47
|
probe_response = wrap { super(request).first }
|
48
48
|
|
49
|
+
return probe_response unless probe_response.is_a?(Response)
|
50
|
+
|
49
51
|
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
50
52
|
request.transition(:idle)
|
51
53
|
request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"])
|
@@ -58,6 +60,6 @@ module HTTPX
|
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
61
|
-
register_plugin :
|
63
|
+
register_plugin :digest_auth, DigestAuth
|
62
64
|
end
|
63
65
|
end
|
data/lib/httpx/plugins/expect.rb
CHANGED
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin makes all HTTP/1.1 requests with a body send the "Expect: 100-continue".
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Expect#expect
|
9
9
|
#
|
10
10
|
module Expect
|
11
11
|
EXPECT_TIMEOUT = 2
|
@@ -20,9 +20,14 @@ module HTTPX
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
# adds support for the following options:
|
24
|
+
#
|
25
|
+
# :expect_timeout :: time (in seconds) to wait for a 100-expect response,
|
26
|
+
# before retrying without the Expect header (defaults to <tt>2</tt>).
|
27
|
+
# :expect_threshold_size :: min threshold (in bytes) of the request payload to enable the 100-continue negotiation on.
|
23
28
|
module OptionsMethods
|
24
29
|
def option_expect_timeout(value)
|
25
|
-
seconds =
|
30
|
+
seconds = Float(value)
|
26
31
|
raise TypeError, ":expect_timeout must be positive" unless seconds.positive?
|
27
32
|
|
28
33
|
seconds
|
@@ -50,7 +55,8 @@ module HTTPX
|
|
50
55
|
end
|
51
56
|
|
52
57
|
def response=(response)
|
53
|
-
if response
|
58
|
+
if response.is_a?(Response) &&
|
59
|
+
response.status == 100 &&
|
54
60
|
!@headers.key?("expect") &&
|
55
61
|
(@state == :body || @state == :done)
|
56
62
|
|
@@ -74,14 +80,16 @@ module HTTPX
|
|
74
80
|
|
75
81
|
return unless request.headers["expect"] == "100-continue"
|
76
82
|
|
77
|
-
request.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
expect_timeout = request.options.expect_timeout
|
84
|
+
|
85
|
+
return if expect_timeout.nil? || expect_timeout.infinite?
|
86
|
+
|
87
|
+
set_request_timeout(request, expect_timeout, :expect, %i[body response]) do
|
88
|
+
# expect timeout expired
|
89
|
+
if request.state == :expect && !request.expects?
|
90
|
+
Expect.no_expect_store << request.origin
|
91
|
+
request.headers.delete("expect")
|
92
|
+
consume
|
85
93
|
end
|
86
94
|
end
|
87
95
|
end
|
@@ -92,12 +100,11 @@ module HTTPX
|
|
92
100
|
response = @responses.delete(request)
|
93
101
|
return unless response
|
94
102
|
|
95
|
-
if response.status == 417 && request.headers.key?("expect")
|
103
|
+
if response.is_a?(Response) && response.status == 417 && request.headers.key?("expect")
|
96
104
|
response.close
|
97
105
|
request.headers.delete("expect")
|
98
106
|
request.transition(:idle)
|
99
|
-
|
100
|
-
connection.send(request)
|
107
|
+
send_request(request, connections, options)
|
101
108
|
return
|
102
109
|
end
|
103
110
|
|
@@ -4,19 +4,35 @@ module HTTPX
|
|
4
4
|
InsecureRedirectError = Class.new(Error)
|
5
5
|
module Plugins
|
6
6
|
#
|
7
|
-
# This plugin adds support for following redirect (status 30X) responses.
|
7
|
+
# This plugin adds support for automatically following redirect (status 30X) responses.
|
8
8
|
#
|
9
|
-
# It has
|
10
|
-
# will return the last redirect response. It will **not** raise an exception.
|
9
|
+
# It has a default upper bound of followed redirects (see *MAX_REDIRECTS* and the *max_redirects* option),
|
10
|
+
# after which it will return the last redirect response. It will **not** raise an exception.
|
11
11
|
#
|
12
|
-
# It
|
12
|
+
# It doesn't follow insecure redirects (https -> http) by default (see *follow_insecure_redirects*).
|
13
13
|
#
|
14
|
-
#
|
14
|
+
# It doesn't propagate authorization related headers to requests redirecting to different origins
|
15
|
+
# (see *allow_auth_to_other_origins*) to override.
|
16
|
+
#
|
17
|
+
# It allows customization of when to redirect via the *redirect_on* callback option).
|
18
|
+
#
|
19
|
+
# https://gitlab.com/os85/httpx/wikis/Follow-Redirects
|
15
20
|
#
|
16
21
|
module FollowRedirects
|
17
22
|
MAX_REDIRECTS = 3
|
18
23
|
REDIRECT_STATUS = (300..399).freeze
|
19
|
-
|
24
|
+
REQUEST_BODY_HEADERS = %w[transfer-encoding content-encoding content-type content-length content-language content-md5 trailer].freeze
|
25
|
+
|
26
|
+
using URIExtensions
|
27
|
+
|
28
|
+
# adds support for the following options:
|
29
|
+
#
|
30
|
+
# :max_redirects :: max number of times a request will be redirected (defaults to <tt>3</tt>).
|
31
|
+
# :follow_insecure_redirects :: whether redirects to an "http://" URI, when coming from an "https//", are allowed
|
32
|
+
# (defaults to <tt>false</tt>).
|
33
|
+
# :allow_auth_to_other_origins :: whether auth-related headers, such as "Authorization", are propagated on redirection
|
34
|
+
# (defaults to <tt>false</tt>).
|
35
|
+
# :redirect_on :: optional callback which receives the redirect location and can halt the redirect chain if it returns <tt>false</tt>.
|
20
36
|
module OptionsMethods
|
21
37
|
def option_max_redirects(value)
|
22
38
|
num = Integer(value)
|
@@ -28,9 +44,20 @@ module HTTPX
|
|
28
44
|
def option_follow_insecure_redirects(value)
|
29
45
|
value
|
30
46
|
end
|
47
|
+
|
48
|
+
def option_allow_auth_to_other_origins(value)
|
49
|
+
value
|
50
|
+
end
|
51
|
+
|
52
|
+
def option_redirect_on(value)
|
53
|
+
raise TypeError, ":redirect_on must be callable" unless value.respond_to?(:call)
|
54
|
+
|
55
|
+
value
|
56
|
+
end
|
31
57
|
end
|
32
58
|
|
33
59
|
module InstanceMethods
|
60
|
+
# returns a session with the *max_redirects* option set to +n+
|
34
61
|
def max_redirects(n)
|
35
62
|
with(max_redirects: n.to_i)
|
36
63
|
end
|
@@ -44,68 +71,117 @@ module HTTPX
|
|
44
71
|
|
45
72
|
max_redirects = redirect_request.max_redirects
|
46
73
|
|
74
|
+
return response unless response.is_a?(Response)
|
47
75
|
return response unless REDIRECT_STATUS.include?(response.status) && response.headers.key?("location")
|
48
76
|
return response unless max_redirects.positive?
|
49
77
|
|
50
|
-
|
78
|
+
redirect_uri = __get_location_from_response(response)
|
51
79
|
|
52
|
-
|
80
|
+
if options.redirect_on
|
81
|
+
redirect_allowed = options.redirect_on.call(redirect_uri)
|
82
|
+
return response unless redirect_allowed
|
83
|
+
end
|
84
|
+
|
85
|
+
# build redirect request
|
86
|
+
request_body = redirect_request.body
|
87
|
+
redirect_method = "GET"
|
88
|
+
redirect_params = {}
|
89
|
+
|
90
|
+
if response.status == 305 && options.respond_to?(:proxy)
|
91
|
+
request_body.rewind
|
92
|
+
# The requested resource MUST be accessed through the proxy given by
|
93
|
+
# the Location field. The Location field gives the URI of the proxy.
|
94
|
+
redirect_options = options.merge(headers: redirect_request.headers,
|
95
|
+
proxy: { uri: redirect_uri },
|
96
|
+
max_redirects: max_redirects - 1)
|
97
|
+
|
98
|
+
redirect_params[:body] = request_body
|
99
|
+
redirect_uri = redirect_request.uri
|
100
|
+
options = redirect_options
|
101
|
+
else
|
102
|
+
redirect_headers = redirect_request_headers(redirect_request.uri, redirect_uri, request.headers, options)
|
103
|
+
redirect_opts = Hash[options]
|
104
|
+
redirect_params[:max_redirects] = max_redirects - 1
|
105
|
+
|
106
|
+
unless request_body.empty?
|
107
|
+
if response.status == 307
|
108
|
+
# The method and the body of the original request are reused to perform the redirected request.
|
109
|
+
redirect_method = redirect_request.verb
|
110
|
+
request_body.rewind
|
111
|
+
redirect_params[:body] = request_body
|
112
|
+
else
|
113
|
+
# redirects are **ALWAYS** GET, so remove body-related headers
|
114
|
+
REQUEST_BODY_HEADERS.each do |h|
|
115
|
+
redirect_headers.delete(h)
|
116
|
+
end
|
117
|
+
redirect_params[:body] = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
options = options.class.new(redirect_opts.merge(headers: redirect_headers.to_h))
|
122
|
+
end
|
123
|
+
|
124
|
+
redirect_uri = Utils.to_uri(redirect_uri)
|
53
125
|
|
54
126
|
if !options.follow_insecure_redirects &&
|
55
127
|
response.uri.scheme == "https" &&
|
56
|
-
|
57
|
-
error = InsecureRedirectError.new(
|
128
|
+
redirect_uri.scheme == "http"
|
129
|
+
error = InsecureRedirectError.new(redirect_uri.to_s)
|
58
130
|
error.set_backtrace(caller)
|
59
|
-
return ErrorResponse.new(request, error
|
131
|
+
return ErrorResponse.new(request, error)
|
60
132
|
end
|
61
133
|
|
62
|
-
|
134
|
+
retry_request = build_request(redirect_method, redirect_uri, redirect_params, options)
|
63
135
|
|
64
|
-
|
136
|
+
request.redirect_request = retry_request
|
137
|
+
|
138
|
+
redirect_after = response.headers["retry-after"]
|
139
|
+
|
140
|
+
if redirect_after
|
65
141
|
# Servers send the "Retry-After" header field to indicate how long the
|
66
142
|
# user agent ought to wait before making a follow-up request.
|
67
143
|
# When sent with any 3xx (Redirection) response, Retry-After indicates
|
68
144
|
# the minimum time that the user agent is asked to wait before issuing
|
69
145
|
# the redirected request.
|
70
146
|
#
|
71
|
-
|
147
|
+
redirect_after = Utils.parse_retry_after(redirect_after)
|
148
|
+
|
149
|
+
log { "redirecting after #{redirect_after} secs..." }
|
72
150
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
151
|
+
deactivate_connection(request, connections, options)
|
152
|
+
|
153
|
+
pool.after(redirect_after) do
|
154
|
+
if request.response
|
155
|
+
# request has terminated abruptly meanwhile
|
156
|
+
retry_request.emit(:response, request.response)
|
157
|
+
else
|
158
|
+
send_request(retry_request, connections, options)
|
159
|
+
end
|
77
160
|
end
|
78
161
|
else
|
79
|
-
|
80
|
-
connection.send(retry_request)
|
162
|
+
send_request(retry_request, connections, options)
|
81
163
|
end
|
82
164
|
nil
|
83
165
|
end
|
84
166
|
|
85
|
-
|
86
|
-
|
87
|
-
|
167
|
+
# :nodoc:
|
168
|
+
def redirect_request_headers(original_uri, redirect_uri, headers, options)
|
169
|
+
headers = headers.dup
|
88
170
|
|
89
|
-
|
90
|
-
# The requested resource MUST be accessed through the proxy given by
|
91
|
-
# the Location field. The Location field gives the URI of the proxy.
|
92
|
-
retry_options = options.merge(headers: request.headers,
|
93
|
-
proxy: { uri: redirect_uri },
|
94
|
-
body: request.body,
|
95
|
-
max_redirects: max_redirects - 1)
|
96
|
-
redirect_uri = request.url
|
97
|
-
else
|
171
|
+
return headers if options.allow_auth_to_other_origins
|
98
172
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
173
|
+
return headers unless headers.key?("authorization")
|
174
|
+
|
175
|
+
return headers if original_uri.origin == redirect_uri.origin
|
176
|
+
|
177
|
+
headers.delete("authorization")
|
104
178
|
|
105
|
-
|
179
|
+
headers
|
106
180
|
end
|
107
181
|
|
182
|
+
# :nodoc:
|
108
183
|
def __get_location_from_response(response)
|
184
|
+
# @type var location_uri: http_uri
|
109
185
|
location_uri = URI(response.headers["location"])
|
110
186
|
location_uri = response.uri.merge(location_uri) if location_uri.relative?
|
111
187
|
location_uri
|
@@ -113,18 +189,41 @@ module HTTPX
|
|
113
189
|
end
|
114
190
|
|
115
191
|
module RequestMethods
|
116
|
-
|
117
|
-
|
118
|
-
end
|
192
|
+
# returns the top-most original HTTPX::Request from the redirect chain
|
193
|
+
attr_accessor :root_request
|
119
194
|
|
195
|
+
# returns the follow-up redirect request, or itself
|
120
196
|
def redirect_request
|
121
197
|
@redirect_request || self
|
122
198
|
end
|
123
199
|
|
200
|
+
# sets the follow-up redirect request
|
201
|
+
def redirect_request=(req)
|
202
|
+
@redirect_request = req
|
203
|
+
req.root_request = @root_request || self
|
204
|
+
@response = nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def response
|
208
|
+
return super unless @redirect_request && @response.nil?
|
209
|
+
|
210
|
+
@redirect_request.response
|
211
|
+
end
|
212
|
+
|
124
213
|
def max_redirects
|
125
214
|
@options.max_redirects || MAX_REDIRECTS
|
126
215
|
end
|
127
216
|
end
|
217
|
+
|
218
|
+
module ConnectionMethods
|
219
|
+
private
|
220
|
+
|
221
|
+
def set_request_request_timeout(request)
|
222
|
+
return unless request.root_request.nil?
|
223
|
+
|
224
|
+
super
|
225
|
+
end
|
226
|
+
end
|
128
227
|
end
|
129
228
|
register_plugin :follow_redirects, FollowRedirects
|
130
229
|
end
|
@@ -11,6 +11,7 @@ module HTTPX
|
|
11
11
|
@response = response
|
12
12
|
@decoder = ->(z) { z }
|
13
13
|
@consumed = false
|
14
|
+
@grpc_response = nil
|
14
15
|
end
|
15
16
|
|
16
17
|
def inspect
|
@@ -34,9 +35,7 @@ module HTTPX
|
|
34
35
|
private
|
35
36
|
|
36
37
|
def grpc_response
|
37
|
-
|
38
|
-
|
39
|
-
@grpc_response = if @response.respond_to?(:each)
|
38
|
+
@grpc_response ||= if @response.respond_to?(:each)
|
40
39
|
Enumerator.new do |y|
|
41
40
|
Message.stream(@response).each do |message|
|
42
41
|
y << @decoder.call(message)
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Transcoder
|
5
|
+
module GRPCEncoding
|
6
|
+
class Deflater
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :content_type
|
10
|
+
|
11
|
+
def initialize(body, compressed:)
|
12
|
+
@content_type = body.content_type
|
13
|
+
@body = BodyReader.new(body)
|
14
|
+
@compressed = compressed
|
15
|
+
end
|
16
|
+
|
17
|
+
def bytesize
|
18
|
+
return @body.bytesize if @body.respond_to?(:bytesize)
|
19
|
+
|
20
|
+
Float::INFINITY
|
21
|
+
end
|
22
|
+
|
23
|
+
def read(length = nil, outbuf = nil)
|
24
|
+
buf = @body.read(length, outbuf)
|
25
|
+
|
26
|
+
return unless buf
|
27
|
+
|
28
|
+
compressed_flag = @compressed ? 1 : 0
|
29
|
+
|
30
|
+
buf = outbuf if outbuf
|
31
|
+
|
32
|
+
buf.prepend([compressed_flag, buf.bytesize].pack("CL>"))
|
33
|
+
buf
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Inflater
|
38
|
+
def initialize(response)
|
39
|
+
@response = response
|
40
|
+
@grpc_encodings = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def call(message, &blk)
|
44
|
+
data = "".b
|
45
|
+
|
46
|
+
until message.empty?
|
47
|
+
compressed, size = message.unpack("CL>")
|
48
|
+
|
49
|
+
encoded_data = message.byteslice(5..size + 5 - 1)
|
50
|
+
|
51
|
+
if compressed == 1
|
52
|
+
grpc_encodings.reverse_each do |encoding|
|
53
|
+
decoder = @response.body.class.initialize_inflater_by_encoding(encoding, @response, bytesize: encoded_data.bytesize)
|
54
|
+
encoded_data = decoder.call(encoded_data)
|
55
|
+
|
56
|
+
blk.call(encoded_data) if blk
|
57
|
+
|
58
|
+
data << encoded_data
|
59
|
+
end
|
60
|
+
else
|
61
|
+
blk.call(encoded_data) if blk
|
62
|
+
|
63
|
+
data << encoded_data
|
64
|
+
end
|
65
|
+
|
66
|
+
message = message.byteslice((size + 5)..-1)
|
67
|
+
end
|
68
|
+
|
69
|
+
data
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def grpc_encodings
|
75
|
+
@grpc_encodings ||= @response.headers.get("grpc-encoding")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.encode(*args, **kwargs)
|
80
|
+
Deflater.new(*args, **kwargs)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.decode(response)
|
84
|
+
Inflater.new(response)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -12,55 +12,25 @@ module HTTPX
|
|
12
12
|
# decodes a unary grpc response
|
13
13
|
def unary(response)
|
14
14
|
verify_status(response)
|
15
|
-
|
15
|
+
|
16
|
+
decoder = Transcoder::GRPCEncoding.decode(response)
|
17
|
+
|
18
|
+
decoder.call(response.to_s)
|
16
19
|
end
|
17
20
|
|
18
21
|
# lazy decodes a grpc stream response
|
19
22
|
def stream(response, &block)
|
20
23
|
return enum_for(__method__, response) unless block
|
21
24
|
|
25
|
+
decoder = Transcoder::GRPCEncoding.decode(response)
|
26
|
+
|
22
27
|
response.each do |frame|
|
23
|
-
|
28
|
+
decoder.call(frame, &block)
|
24
29
|
end
|
25
30
|
|
26
31
|
verify_status(response)
|
27
32
|
end
|
28
33
|
|
29
|
-
# encodes a single grpc message
|
30
|
-
def encode(bytes, deflater:)
|
31
|
-
if deflater
|
32
|
-
compressed_flag = 1
|
33
|
-
bytes = deflater.deflate(StringIO.new(bytes))
|
34
|
-
else
|
35
|
-
compressed_flag = 0
|
36
|
-
end
|
37
|
-
|
38
|
-
"".b << [compressed_flag, bytes.bytesize].pack("CL>") << bytes.to_s
|
39
|
-
end
|
40
|
-
|
41
|
-
# decodes a single grpc message
|
42
|
-
def decode(message, encodings:, encoders:)
|
43
|
-
until message.empty?
|
44
|
-
|
45
|
-
compressed, size = message.unpack("CL>")
|
46
|
-
|
47
|
-
data = message.byteslice(5..size + 5 - 1)
|
48
|
-
if compressed == 1
|
49
|
-
encodings.reverse_each do |algo|
|
50
|
-
inflater = encoders.registry(algo).inflater(size)
|
51
|
-
data = inflater.inflate(data)
|
52
|
-
size = data.bytesize
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
return data unless block_given?
|
57
|
-
|
58
|
-
yield data
|
59
|
-
|
60
|
-
message = message.byteslice((5 + size)..-1)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
34
|
def cancel(request)
|
65
35
|
request.emit(:refuse, :client_cancellation)
|
66
36
|
end
|