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
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
# Implementation of the HTTP Response body as a buffer which implements the IO writer protocol
|
|
5
|
+
# (for buffering the response payload), the IO reader protocol (for consuming the response payload),
|
|
6
|
+
# and can be iterated over (via #each, which yields the payload in chunks).
|
|
7
|
+
class Response::Body
|
|
8
|
+
# the payload encoding (i.e. "utf-8", "ASCII-8BIT")
|
|
9
|
+
attr_reader :encoding
|
|
10
|
+
|
|
11
|
+
# Array of encodings contained in the response "content-encoding" header.
|
|
12
|
+
attr_reader :encodings
|
|
13
|
+
|
|
14
|
+
# initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+.
|
|
15
|
+
def initialize(response, options)
|
|
16
|
+
@response = response
|
|
17
|
+
@headers = response.headers
|
|
18
|
+
@options = options
|
|
19
|
+
@window_size = options.window_size
|
|
20
|
+
@encoding = response.content_type.charset || Encoding::BINARY
|
|
21
|
+
@encodings = []
|
|
22
|
+
@length = 0
|
|
23
|
+
@buffer = nil
|
|
24
|
+
@reader = nil
|
|
25
|
+
@state = :idle
|
|
26
|
+
initialize_inflaters
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize_dup(other)
|
|
30
|
+
super
|
|
31
|
+
|
|
32
|
+
@buffer = other.instance_variable_get(:@buffer).dup
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def closed?
|
|
36
|
+
@state == :closed
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# write the response payload +chunk+ into the buffer. Inflates the chunk when required
|
|
40
|
+
# and supported.
|
|
41
|
+
def write(chunk)
|
|
42
|
+
return if @state == :closed
|
|
43
|
+
|
|
44
|
+
return 0 if chunk.empty?
|
|
45
|
+
|
|
46
|
+
chunk = decode_chunk(chunk)
|
|
47
|
+
|
|
48
|
+
size = chunk.bytesize
|
|
49
|
+
@length += size
|
|
50
|
+
transition(:open)
|
|
51
|
+
@buffer.write(chunk)
|
|
52
|
+
|
|
53
|
+
@response.emit(:chunk_received, chunk)
|
|
54
|
+
size
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# reads a chunk from the payload (implementation of the IO reader protocol).
|
|
58
|
+
def read(*args)
|
|
59
|
+
return unless @buffer
|
|
60
|
+
|
|
61
|
+
unless @reader
|
|
62
|
+
rewind
|
|
63
|
+
@reader = @buffer
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@reader.read(*args)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# size of the decoded response payload. May differ from "content-length" header if
|
|
70
|
+
# response was encoded over-the-wire.
|
|
71
|
+
def bytesize
|
|
72
|
+
@length
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# yields the payload in chunks.
|
|
76
|
+
def each
|
|
77
|
+
return enum_for(__method__) unless block_given?
|
|
78
|
+
|
|
79
|
+
begin
|
|
80
|
+
if @buffer
|
|
81
|
+
rewind
|
|
82
|
+
while (chunk = @buffer.read(@window_size))
|
|
83
|
+
yield(chunk.force_encoding(@encoding))
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
ensure
|
|
87
|
+
close
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# returns the declared filename in the "contennt-disposition" header, when present.
|
|
92
|
+
def filename
|
|
93
|
+
return unless @headers.key?("content-disposition")
|
|
94
|
+
|
|
95
|
+
Utils.get_filename(@headers["content-disposition"])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# returns the full response payload as a string.
|
|
99
|
+
def to_s
|
|
100
|
+
return "".b unless @buffer
|
|
101
|
+
|
|
102
|
+
@buffer.to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
alias_method :to_str, :to_s
|
|
106
|
+
|
|
107
|
+
# whether the payload is empty.
|
|
108
|
+
def empty?
|
|
109
|
+
@length.zero?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# copies the payload to +dest+.
|
|
113
|
+
#
|
|
114
|
+
# body.copy_to("path/to/file")
|
|
115
|
+
# body.copy_to(Pathname.new("path/to/file"))
|
|
116
|
+
# body.copy_to(File.new("path/to/file"))
|
|
117
|
+
def copy_to(dest)
|
|
118
|
+
return unless @buffer
|
|
119
|
+
|
|
120
|
+
rewind
|
|
121
|
+
|
|
122
|
+
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
|
123
|
+
FileUtils.mv(@buffer.path, dest.path)
|
|
124
|
+
else
|
|
125
|
+
::IO.copy_stream(@buffer, dest)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# closes/cleans the buffer, resets everything
|
|
130
|
+
def close
|
|
131
|
+
if @buffer
|
|
132
|
+
@buffer.close
|
|
133
|
+
@buffer = nil
|
|
134
|
+
end
|
|
135
|
+
@length = 0
|
|
136
|
+
transition(:closed)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def ==(other)
|
|
140
|
+
object_id == other.object_id || begin
|
|
141
|
+
if other.respond_to?(:read)
|
|
142
|
+
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
|
|
143
|
+
else
|
|
144
|
+
to_s == other.to_s
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# :nocov:
|
|
150
|
+
def inspect
|
|
151
|
+
"#<HTTPX::Response::Body:#{object_id} " \
|
|
152
|
+
"@state=#{@state} " \
|
|
153
|
+
"@length=#{@length}>"
|
|
154
|
+
end
|
|
155
|
+
# :nocov:
|
|
156
|
+
|
|
157
|
+
# rewinds the response payload buffer.
|
|
158
|
+
def rewind
|
|
159
|
+
return unless @buffer
|
|
160
|
+
|
|
161
|
+
# in case there's some reading going on
|
|
162
|
+
@reader = nil
|
|
163
|
+
|
|
164
|
+
@buffer.rewind
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
# prepares inflaters for the advertised encodings in "content-encoding" header.
|
|
170
|
+
def initialize_inflaters
|
|
171
|
+
@inflaters = nil
|
|
172
|
+
|
|
173
|
+
return unless @headers.key?("content-encoding")
|
|
174
|
+
|
|
175
|
+
return unless @options.decompress_response_body
|
|
176
|
+
|
|
177
|
+
@inflaters = @headers.get("content-encoding").filter_map do |encoding|
|
|
178
|
+
next if encoding == "identity"
|
|
179
|
+
|
|
180
|
+
inflater = self.class.initialize_inflater_by_encoding(encoding, @response)
|
|
181
|
+
|
|
182
|
+
# do not uncompress if there is no decoder available. In fact, we can't reliably
|
|
183
|
+
# continue decompressing beyond that, so ignore.
|
|
184
|
+
break unless inflater
|
|
185
|
+
|
|
186
|
+
@encodings << encoding
|
|
187
|
+
inflater
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# passes the +chunk+ through all inflaters to decode it.
|
|
192
|
+
def decode_chunk(chunk)
|
|
193
|
+
@inflaters.reverse_each do |inflater|
|
|
194
|
+
chunk = inflater.call(chunk)
|
|
195
|
+
end if @inflaters
|
|
196
|
+
|
|
197
|
+
chunk
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# tries transitioning the body STM to the +nextstate+.
|
|
201
|
+
def transition(nextstate)
|
|
202
|
+
case nextstate
|
|
203
|
+
when :open
|
|
204
|
+
return unless @state == :idle
|
|
205
|
+
|
|
206
|
+
@buffer = Response::Buffer.new(
|
|
207
|
+
threshold_size: @options.body_threshold_size,
|
|
208
|
+
bytesize: @length,
|
|
209
|
+
encoding: @encoding
|
|
210
|
+
)
|
|
211
|
+
when :closed
|
|
212
|
+
return if @state == :closed
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
@state = nextstate
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def _with_same_buffer_pos # :nodoc:
|
|
219
|
+
return yield unless @buffer && @buffer.respond_to?(:pos)
|
|
220
|
+
|
|
221
|
+
# @type ivar @buffer: StringIO | Tempfile
|
|
222
|
+
current_pos = @buffer.pos
|
|
223
|
+
@buffer.rewind
|
|
224
|
+
begin
|
|
225
|
+
yield
|
|
226
|
+
ensure
|
|
227
|
+
@buffer.pos = current_pos
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
class << self
|
|
232
|
+
def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc:
|
|
233
|
+
case encoding
|
|
234
|
+
when "gzip"
|
|
235
|
+
Transcoder::GZIP.decode(response, **kwargs)
|
|
236
|
+
when "deflate"
|
|
237
|
+
Transcoder::Deflate.decode(response, **kwargs)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "delegate"
|
|
4
|
+
require "stringio"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
|
|
7
|
+
module HTTPX
|
|
8
|
+
# wraps and delegates to an internal buffer, which can be a StringIO or a Tempfile.
|
|
9
|
+
class Response::Buffer < SimpleDelegator
|
|
10
|
+
# initializes buffer with the +threshold_size+ over which the payload gets buffer to a tempfile,
|
|
11
|
+
# the initial +bytesize+, and the +encoding+.
|
|
12
|
+
def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY)
|
|
13
|
+
@threshold_size = threshold_size
|
|
14
|
+
@bytesize = bytesize
|
|
15
|
+
@encoding = encoding
|
|
16
|
+
@buffer = StringIO.new("".b)
|
|
17
|
+
super(@buffer)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize_dup(other)
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
@buffer = other.instance_variable_get(:@buffer).dup
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# size in bytes of the buffered content.
|
|
27
|
+
def size
|
|
28
|
+
@bytesize
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# writes the +chunk+ into the buffer.
|
|
32
|
+
def write(chunk)
|
|
33
|
+
@bytesize += chunk.bytesize
|
|
34
|
+
try_upgrade_buffer
|
|
35
|
+
@buffer.write(chunk)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# returns the buffered content as a string.
|
|
39
|
+
def to_s
|
|
40
|
+
case @buffer
|
|
41
|
+
when StringIO
|
|
42
|
+
begin
|
|
43
|
+
@buffer.string.force_encoding(@encoding)
|
|
44
|
+
rescue ArgumentError
|
|
45
|
+
@buffer.string
|
|
46
|
+
end
|
|
47
|
+
when Tempfile
|
|
48
|
+
rewind
|
|
49
|
+
content = _with_same_buffer_pos { @buffer.read }
|
|
50
|
+
begin
|
|
51
|
+
content.force_encoding(@encoding)
|
|
52
|
+
rescue ArgumentError # ex: unknown encoding name - utf
|
|
53
|
+
content
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# closes the buffer.
|
|
59
|
+
def close
|
|
60
|
+
@buffer.close
|
|
61
|
+
@buffer.unlink if @buffer.respond_to?(:unlink)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# initializes the buffer into a StringIO, or turns it into a Tempfile when the threshold
|
|
67
|
+
# has been reached.
|
|
68
|
+
def try_upgrade_buffer
|
|
69
|
+
return unless @bytesize > @threshold_size
|
|
70
|
+
|
|
71
|
+
return if @buffer.is_a?(Tempfile)
|
|
72
|
+
|
|
73
|
+
aux = @buffer
|
|
74
|
+
|
|
75
|
+
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
|
76
|
+
|
|
77
|
+
if aux
|
|
78
|
+
aux.rewind
|
|
79
|
+
::IO.copy_stream(aux, @buffer)
|
|
80
|
+
aux.close
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
__setobj__(@buffer)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def _with_same_buffer_pos # :nodoc:
|
|
87
|
+
current_pos = @buffer.pos
|
|
88
|
+
@buffer.rewind
|
|
89
|
+
begin
|
|
90
|
+
yield
|
|
91
|
+
ensure
|
|
92
|
+
@buffer.pos = current_pos
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|