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/response.rb
CHANGED
|
@@ -7,23 +7,48 @@ require "fileutils"
|
|
|
7
7
|
require "forwardable"
|
|
8
8
|
|
|
9
9
|
module HTTPX
|
|
10
|
+
# Defines a HTTP response is handled internally, with a few properties exposed as attributes,
|
|
11
|
+
# implements (indirectly, via the +body+) the IO write protocol to internally buffer payloads,
|
|
12
|
+
# implements the IO reader protocol in order for users to buffer/stream it, acts as an enumerable
|
|
13
|
+
# (of payload chunks).
|
|
10
14
|
class Response
|
|
11
15
|
extend Forwardable
|
|
16
|
+
include Callbacks
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
# the HTTP response status code
|
|
19
|
+
attr_reader :status
|
|
14
20
|
|
|
21
|
+
# an HTTPX::Headers object containing the response HTTP headers.
|
|
22
|
+
attr_reader :headers
|
|
23
|
+
|
|
24
|
+
# a HTTPX::Response::Body object wrapping the response body.
|
|
25
|
+
attr_reader :body
|
|
26
|
+
|
|
27
|
+
# The HTTP protocol version used to fetch the response.
|
|
28
|
+
attr_reader :version
|
|
29
|
+
|
|
30
|
+
# returns the response body buffered in a string.
|
|
15
31
|
def_delegator :@body, :to_s
|
|
16
32
|
|
|
17
33
|
def_delegator :@body, :to_str
|
|
18
34
|
|
|
35
|
+
# implements the IO reader +#read+ interface.
|
|
19
36
|
def_delegator :@body, :read
|
|
20
37
|
|
|
38
|
+
# copies the response body to a different location.
|
|
21
39
|
def_delegator :@body, :copy_to
|
|
22
40
|
|
|
41
|
+
# closes the body.
|
|
23
42
|
def_delegator :@body, :close
|
|
24
43
|
|
|
44
|
+
# the corresponding request uri.
|
|
25
45
|
def_delegator :@request, :uri
|
|
26
46
|
|
|
47
|
+
# the IP address of the peer server.
|
|
48
|
+
def_delegator :@request, :peer_address
|
|
49
|
+
|
|
50
|
+
# inits the instance with the corresponding +request+ to this response, an the
|
|
51
|
+
# response HTTP +status+, +version+ and HTTPX::Headers instance of +headers+.
|
|
27
52
|
def initialize(request, status, version, headers)
|
|
28
53
|
@request = request
|
|
29
54
|
@options = request.options
|
|
@@ -32,36 +57,54 @@ module HTTPX
|
|
|
32
57
|
@headers = @options.headers_class.new(headers)
|
|
33
58
|
@body = @options.response_body_class.new(self, @options)
|
|
34
59
|
@finished = complete?
|
|
60
|
+
@content_type = nil
|
|
35
61
|
end
|
|
36
62
|
|
|
63
|
+
# merges headers defined in +h+ into the response headers.
|
|
37
64
|
def merge_headers(h)
|
|
38
65
|
@headers = @headers.merge(h)
|
|
39
66
|
end
|
|
40
67
|
|
|
68
|
+
# writes +data+ chunk into the response body.
|
|
41
69
|
def <<(data)
|
|
42
70
|
@body.write(data)
|
|
43
71
|
end
|
|
44
72
|
|
|
73
|
+
# returns the HTTPX::ContentType for the response, as per what's declared in the content-type header.
|
|
74
|
+
#
|
|
75
|
+
# response.content_type #=> #<HTTPX::ContentType:xxx @header_value="text/plain">
|
|
76
|
+
# response.content_type.mime_type #=> "text/plain"
|
|
45
77
|
def content_type
|
|
46
78
|
@content_type ||= ContentType.new(@headers["content-type"])
|
|
47
79
|
end
|
|
48
80
|
|
|
81
|
+
# returns whether the response has been fully fetched.
|
|
49
82
|
def finished?
|
|
50
83
|
@finished
|
|
51
84
|
end
|
|
52
85
|
|
|
86
|
+
# marks the response as finished, freezes the headers.
|
|
53
87
|
def finish!
|
|
54
88
|
@finished = true
|
|
55
89
|
@headers.freeze
|
|
56
90
|
end
|
|
57
91
|
|
|
92
|
+
# returns whether the response contains body payload.
|
|
58
93
|
def bodyless?
|
|
59
|
-
@request.verb ==
|
|
60
|
-
|
|
94
|
+
@request.verb == "HEAD" ||
|
|
95
|
+
@status < 200 || # informational response
|
|
96
|
+
@status == 204 ||
|
|
97
|
+
@status == 205 ||
|
|
98
|
+
@status == 304 || begin
|
|
99
|
+
content_length = @headers["content-length"]
|
|
100
|
+
return false if content_length.nil?
|
|
101
|
+
|
|
102
|
+
content_length == "0"
|
|
103
|
+
end
|
|
61
104
|
end
|
|
62
105
|
|
|
63
106
|
def complete?
|
|
64
|
-
bodyless? || (@request.verb ==
|
|
107
|
+
bodyless? || (@request.verb == "CONNECT" && @status == 200)
|
|
65
108
|
end
|
|
66
109
|
|
|
67
110
|
# :nocov:
|
|
@@ -74,231 +117,67 @@ module HTTPX
|
|
|
74
117
|
end
|
|
75
118
|
# :nocov:
|
|
76
119
|
|
|
120
|
+
# returns an instance of HTTPX::HTTPError if the response has a 4xx or 5xx
|
|
121
|
+
# status code, or nothing.
|
|
122
|
+
#
|
|
123
|
+
# ok_response.error #=> nil
|
|
124
|
+
# not_found_response.error #=> HTTPX::HTTPError instance, status 404
|
|
77
125
|
def error
|
|
78
126
|
return if @status < 400
|
|
79
127
|
|
|
80
128
|
HTTPError.new(self)
|
|
81
129
|
end
|
|
82
130
|
|
|
131
|
+
# it raises the exception returned by +error+, or itself otherwise.
|
|
132
|
+
#
|
|
133
|
+
# ok_response.raise_for_status #=> ok_response
|
|
134
|
+
# not_found_response.raise_for_status #=> raises HTTPX::HTTPError exception
|
|
83
135
|
def raise_for_status
|
|
84
136
|
return self unless (err = error)
|
|
85
137
|
|
|
86
138
|
raise err
|
|
87
139
|
end
|
|
88
140
|
|
|
141
|
+
# decodes the response payload into a ruby object **if** the payload is valid json.
|
|
142
|
+
#
|
|
143
|
+
# response.json #≈> { "foo" => "bar" } for "{\"foo\":\"bar\"}" payload
|
|
144
|
+
# response.json(symbolize_names: true) #≈> { foo: "bar" } for "{\"foo\":\"bar\"}" payload
|
|
89
145
|
def json(*args)
|
|
90
|
-
decode(
|
|
146
|
+
decode(Transcoder::JSON, *args)
|
|
91
147
|
end
|
|
92
148
|
|
|
149
|
+
# decodes the response payload into a ruby object **if** the payload is valid
|
|
150
|
+
# "application/x-www-urlencoded" or "multipart/form-data".
|
|
93
151
|
def form
|
|
94
|
-
decode(
|
|
152
|
+
decode(Transcoder::Form)
|
|
95
153
|
end
|
|
96
154
|
|
|
155
|
+
# decodes the response payload into a Nokogiri::XML::Node object **if** the payload is valid
|
|
156
|
+
# "application/xml" (requires the "nokogiri" gem).
|
|
97
157
|
def xml
|
|
98
|
-
decode(
|
|
158
|
+
decode(Transcoder::Xml)
|
|
99
159
|
end
|
|
100
160
|
|
|
101
161
|
private
|
|
102
162
|
|
|
103
|
-
|
|
163
|
+
# decodes the response payload using the given +transcoder+, which implements the decoding logic.
|
|
164
|
+
#
|
|
165
|
+
# +transcoder+ must implement the internal transcoder API, i.e. respond to <tt>decode(HTTPX::Response response)</tt>,
|
|
166
|
+
# which returns a decoder which responds to <tt>call(HTTPX::Response response, **kwargs)</tt>
|
|
167
|
+
def decode(transcoder, *args)
|
|
104
168
|
# TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
|
|
105
|
-
transcoder = Transcoder.registry(format)
|
|
106
|
-
|
|
107
|
-
raise Error, "no decoder available for \"#{format}\"" unless transcoder.respond_to?(:decode)
|
|
108
169
|
|
|
109
170
|
decoder = transcoder.decode(self)
|
|
110
171
|
|
|
111
|
-
raise Error, "no decoder available for \"#{
|
|
172
|
+
raise Error, "no decoder available for \"#{transcoder}\"" unless decoder
|
|
112
173
|
|
|
113
|
-
|
|
114
|
-
rescue Registry::Error
|
|
115
|
-
raise Error, "no decoder available for \"#{format}\""
|
|
116
|
-
end
|
|
174
|
+
@body.rewind
|
|
117
175
|
|
|
118
|
-
|
|
119
|
-
@status < 200 || # informational response
|
|
120
|
-
@status == 204 ||
|
|
121
|
-
@status == 205 ||
|
|
122
|
-
@status == 304 || begin
|
|
123
|
-
content_length = @headers["content-length"]
|
|
124
|
-
return false if content_length.nil?
|
|
125
|
-
|
|
126
|
-
content_length == "0"
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
class Body
|
|
131
|
-
def initialize(response, options)
|
|
132
|
-
@response = response
|
|
133
|
-
@headers = response.headers
|
|
134
|
-
@options = options
|
|
135
|
-
@threshold_size = options.body_threshold_size
|
|
136
|
-
@window_size = options.window_size
|
|
137
|
-
@encoding = response.content_type.charset || Encoding::BINARY
|
|
138
|
-
@length = 0
|
|
139
|
-
@buffer = nil
|
|
140
|
-
@state = :idle
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def closed?
|
|
144
|
-
@state == :closed
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def write(chunk)
|
|
148
|
-
return if @state == :closed
|
|
149
|
-
|
|
150
|
-
@length += chunk.bytesize
|
|
151
|
-
transition
|
|
152
|
-
@buffer.write(chunk)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def read(*args)
|
|
156
|
-
return unless @buffer
|
|
157
|
-
|
|
158
|
-
rewind
|
|
159
|
-
|
|
160
|
-
@buffer.read(*args)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def bytesize
|
|
164
|
-
@length
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def each
|
|
168
|
-
return enum_for(__method__) unless block_given?
|
|
169
|
-
|
|
170
|
-
begin
|
|
171
|
-
if @buffer
|
|
172
|
-
rewind
|
|
173
|
-
while (chunk = @buffer.read(@window_size))
|
|
174
|
-
yield(chunk.force_encoding(@encoding))
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
ensure
|
|
178
|
-
close
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def to_s
|
|
183
|
-
case @buffer
|
|
184
|
-
when StringIO
|
|
185
|
-
begin
|
|
186
|
-
@buffer.string.force_encoding(@encoding)
|
|
187
|
-
rescue ArgumentError
|
|
188
|
-
@buffer.string
|
|
189
|
-
end
|
|
190
|
-
when Tempfile
|
|
191
|
-
rewind
|
|
192
|
-
content = _with_same_buffer_pos { @buffer.read }
|
|
193
|
-
begin
|
|
194
|
-
content.force_encoding(@encoding)
|
|
195
|
-
rescue ArgumentError # ex: unknown encoding name - utf
|
|
196
|
-
content
|
|
197
|
-
end
|
|
198
|
-
when nil
|
|
199
|
-
"".b
|
|
200
|
-
else
|
|
201
|
-
@buffer
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
alias_method :to_str, :to_s
|
|
205
|
-
|
|
206
|
-
def empty?
|
|
207
|
-
@length.zero?
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def copy_to(dest)
|
|
211
|
-
return unless @buffer
|
|
212
|
-
|
|
213
|
-
rewind
|
|
214
|
-
|
|
215
|
-
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
|
216
|
-
FileUtils.mv(@buffer.path, dest.path)
|
|
217
|
-
else
|
|
218
|
-
::IO.copy_stream(@buffer, dest)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# closes/cleans the buffer, resets everything
|
|
223
|
-
def close
|
|
224
|
-
if @buffer
|
|
225
|
-
@buffer.close
|
|
226
|
-
@buffer.unlink if @buffer.respond_to?(:unlink)
|
|
227
|
-
@buffer = nil
|
|
228
|
-
end
|
|
229
|
-
@length = 0
|
|
230
|
-
@state = :closed
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def ==(other)
|
|
234
|
-
object_id == other.object_id || begin
|
|
235
|
-
if other.respond_to?(:read)
|
|
236
|
-
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
|
|
237
|
-
else
|
|
238
|
-
to_s == other.to_s
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
# :nocov:
|
|
244
|
-
def inspect
|
|
245
|
-
"#<HTTPX::Response::Body:#{object_id} " \
|
|
246
|
-
"@state=#{@state} " \
|
|
247
|
-
"@length=#{@length}>"
|
|
248
|
-
end
|
|
249
|
-
# :nocov:
|
|
250
|
-
|
|
251
|
-
private
|
|
252
|
-
|
|
253
|
-
def rewind
|
|
254
|
-
return unless @buffer
|
|
255
|
-
|
|
256
|
-
@buffer.rewind
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def transition
|
|
260
|
-
case @state
|
|
261
|
-
when :idle
|
|
262
|
-
if @length > @threshold_size
|
|
263
|
-
@state = :buffer
|
|
264
|
-
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
|
265
|
-
else
|
|
266
|
-
@state = :memory
|
|
267
|
-
@buffer = StringIO.new("".b)
|
|
268
|
-
end
|
|
269
|
-
when :memory
|
|
270
|
-
# @type ivar @buffer: StringIO | Tempfile
|
|
271
|
-
if @length > @threshold_size
|
|
272
|
-
aux = @buffer
|
|
273
|
-
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
|
274
|
-
aux.rewind
|
|
275
|
-
::IO.copy_stream(aux, @buffer)
|
|
276
|
-
# (this looks like a bug from Ruby < 2.3
|
|
277
|
-
@buffer.pos = aux.pos ##################
|
|
278
|
-
########################################
|
|
279
|
-
aux.close
|
|
280
|
-
@state = :buffer
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
return unless %i[memory buffer].include?(@state)
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
def _with_same_buffer_pos
|
|
288
|
-
return yield unless @buffer && @buffer.respond_to?(:pos)
|
|
289
|
-
|
|
290
|
-
# @type ivar @buffer: StringIO | Tempfile
|
|
291
|
-
current_pos = @buffer.pos
|
|
292
|
-
@buffer.rewind
|
|
293
|
-
begin
|
|
294
|
-
yield
|
|
295
|
-
ensure
|
|
296
|
-
@buffer.pos = current_pos
|
|
297
|
-
end
|
|
298
|
-
end
|
|
176
|
+
decoder.call(self, *args)
|
|
299
177
|
end
|
|
300
178
|
end
|
|
301
179
|
|
|
180
|
+
# Helper class which decodes the HTTP "content-type" header.
|
|
302
181
|
class ContentType
|
|
303
182
|
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
|
|
304
183
|
CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
|
|
@@ -307,6 +186,9 @@ module HTTPX
|
|
|
307
186
|
@header_value = header_value
|
|
308
187
|
end
|
|
309
188
|
|
|
189
|
+
# returns the mime type declared in the header.
|
|
190
|
+
#
|
|
191
|
+
# ContentType.new("application/json; charset=utf-8").mime_type #=> "application/json"
|
|
310
192
|
def mime_type
|
|
311
193
|
return @mime_type if defined?(@mime_type)
|
|
312
194
|
|
|
@@ -314,6 +196,10 @@ module HTTPX
|
|
|
314
196
|
m && @mime_type = m.strip.downcase
|
|
315
197
|
end
|
|
316
198
|
|
|
199
|
+
# returns the charset declared in the header.
|
|
200
|
+
#
|
|
201
|
+
# ContentType.new("application/json; charset=utf-8").charset #=> "utf-8"
|
|
202
|
+
# ContentType.new("text/plain").charset #=> nil
|
|
317
203
|
def charset
|
|
318
204
|
return @charset if defined?(@charset)
|
|
319
205
|
|
|
@@ -322,45 +208,61 @@ module HTTPX
|
|
|
322
208
|
end
|
|
323
209
|
end
|
|
324
210
|
|
|
211
|
+
# Wraps an error which has happened while processing an HTTP Request. It has partial
|
|
212
|
+
# public API parity with HTTPX::Response, so users should rely on it to infer whether
|
|
213
|
+
# the returned response is one or the other.
|
|
214
|
+
#
|
|
215
|
+
# response = HTTPX.get("https://some-domain/path") #=> response is HTTPX::Response or HTTPX::ErrorResponse
|
|
216
|
+
# response.raise_for_status #=> raises if it wraps an error
|
|
325
217
|
class ErrorResponse
|
|
326
218
|
include Loggable
|
|
327
219
|
extend Forwardable
|
|
328
220
|
|
|
329
|
-
|
|
221
|
+
# the corresponding HTTPX::Request instance.
|
|
222
|
+
attr_reader :request
|
|
223
|
+
|
|
224
|
+
# the HTTPX::Response instance, when there is one (i.e. error happens fetching the response).
|
|
225
|
+
attr_reader :response
|
|
330
226
|
|
|
227
|
+
# the wrapped exception.
|
|
228
|
+
attr_reader :error
|
|
229
|
+
|
|
230
|
+
# the request uri
|
|
331
231
|
def_delegator :@request, :uri
|
|
332
232
|
|
|
233
|
+
# the IP address of the peer server.
|
|
234
|
+
def_delegator :@request, :peer_address
|
|
235
|
+
|
|
333
236
|
def initialize(request, error, options)
|
|
334
237
|
@request = request
|
|
238
|
+
@response = request.response if request.response.is_a?(Response)
|
|
335
239
|
@error = error
|
|
336
240
|
@options = Options.new(options)
|
|
337
241
|
log_exception(@error)
|
|
338
242
|
end
|
|
339
243
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
@error.
|
|
244
|
+
# returns the exception full message.
|
|
245
|
+
def to_s
|
|
246
|
+
@error.full_message(highlight: false)
|
|
343
247
|
end
|
|
344
248
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
end
|
|
349
|
-
else
|
|
350
|
-
def to_s
|
|
351
|
-
"#{@error.message} (#{@error.class})\n" \
|
|
352
|
-
"#{@error.backtrace.join("\n") if @error.backtrace}"
|
|
353
|
-
end
|
|
249
|
+
# closes the error resources.
|
|
250
|
+
def close
|
|
251
|
+
@response.close if @response && @response.respond_to?(:close)
|
|
354
252
|
end
|
|
355
253
|
|
|
254
|
+
# always true for error responses.
|
|
356
255
|
def finished?
|
|
357
256
|
true
|
|
358
257
|
end
|
|
359
258
|
|
|
259
|
+
# raises the wrapped exception.
|
|
360
260
|
def raise_for_status
|
|
361
261
|
raise @error
|
|
362
262
|
end
|
|
363
263
|
end
|
|
364
264
|
end
|
|
365
265
|
|
|
366
|
-
|
|
266
|
+
require_relative "response/body"
|
|
267
|
+
require_relative "response/buffer"
|
|
268
|
+
require_relative "pmatch_extensions" if RUBY_VERSION >= "2.7.0"
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -9,8 +9,6 @@ class HTTPX::Selector
|
|
|
9
9
|
private_constant :READABLE
|
|
10
10
|
private_constant :WRITABLE
|
|
11
11
|
|
|
12
|
-
using HTTPX::IOExtensions
|
|
13
|
-
|
|
14
12
|
def initialize
|
|
15
13
|
@selectables = []
|
|
16
14
|
end
|
|
@@ -75,7 +73,7 @@ class HTTPX::Selector
|
|
|
75
73
|
readers, writers = IO.select(r, w, nil, interval)
|
|
76
74
|
|
|
77
75
|
if readers.nil? && writers.nil? && interval
|
|
78
|
-
[*r, *w].each { |io| io.
|
|
76
|
+
[*r, *w].each { |io| io.handle_socket_timeout(interval) }
|
|
79
77
|
return
|
|
80
78
|
end
|
|
81
79
|
rescue IOError, SystemCallError
|
|
@@ -112,7 +110,7 @@ class HTTPX::Selector
|
|
|
112
110
|
end
|
|
113
111
|
|
|
114
112
|
unless result || interval.nil?
|
|
115
|
-
io.
|
|
113
|
+
io.handle_socket_timeout(interval)
|
|
116
114
|
return
|
|
117
115
|
end
|
|
118
116
|
# raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
|