httpx 0.24.5 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +0 -48
- data/README.md +4 -13
- data/doc/release_notes/0_24_4.md +3 -3
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/lib/httpx/adapters/datadog.rb +28 -97
- data/lib/httpx/adapters/faraday.rb +9 -52
- data/lib/httpx/adapters/webmock.rb +2 -7
- data/lib/httpx/altsvc.rb +4 -22
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/chainable.rb +2 -25
- data/lib/httpx/connection.rb +11 -32
- data/lib/httpx/domain_name.rb +5 -12
- data/lib/httpx/errors.rb +0 -2
- data/lib/httpx/extensions.rb +0 -124
- data/lib/httpx/io/ssl.rb +26 -59
- data/lib/httpx/io/tcp.rb +27 -68
- data/lib/httpx/io/udp.rb +13 -48
- data/lib/httpx/io/unix.rb +1 -8
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +24 -84
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +2 -5
- 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_sigv4.rb +0 -1
- data/lib/httpx/plugins/{basic_authentication.rb → basic_auth.rb} +5 -6
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +40 -16
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +16 -5
- data/lib/httpx/plugins/circuit_breaker.rb +11 -4
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +1 -1
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +5 -5
- data/lib/httpx/plugins/follow_redirects.rb +21 -24
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +82 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -39
- data/lib/httpx/plugins/grpc.rb +15 -21
- data/lib/httpx/plugins/h2c.rb +0 -1
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +5 -5
- data/lib/httpx/plugins/oauth.rb +2 -2
- data/lib/httpx/plugins/proxy/http.rb +0 -2
- data/lib/httpx/plugins/proxy/socks4.rb +0 -4
- data/lib/httpx/plugins/proxy/socks5.rb +1 -5
- data/lib/httpx/plugins/proxy.rb +3 -32
- data/lib/httpx/plugins/retries.rb +3 -4
- data/lib/httpx/plugins/stream.rb +4 -6
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +145 -0
- data/lib/httpx/request.rb +2 -119
- data/lib/httpx/resolver/https.rb +1 -1
- data/lib/httpx/resolver/native.rb +6 -14
- data/lib/httpx/resolver/resolver.rb +1 -1
- data/lib/httpx/resolver/system.rb +11 -9
- data/lib/httpx/response/body.rb +206 -0
- data/lib/httpx/response/buffer.rb +90 -0
- data/lib/httpx/response.rb +5 -208
- data/lib/httpx/selector.rb +0 -2
- data/lib/httpx/session.rb +3 -10
- data/lib/httpx/transcoder/body.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -32
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +2 -4
- 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/xml.rb +0 -2
- data/lib/httpx/transcoder.rb +2 -2
- data/lib/httpx/utils.rb +10 -20
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +0 -8
- data/sig/chainable.rbs +5 -6
- data/sig/connection.rbs +0 -1
- data/sig/errors.rbs +0 -3
- data/sig/httpx.rbs +2 -1
- data/sig/io/unix.rbs +1 -1
- data/sig/options.rbs +12 -8
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- 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/circuit_breaker.rbs +7 -3
- data/sig/plugins/compression.rbs +0 -2
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +0 -1
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +33 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +2 -32
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +1 -1
- data/sig/plugins/proxy/socks4.rbs +2 -3
- data/sig/plugins/proxy/socks5.rbs +0 -1
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/response_cache.rbs +5 -2
- data/sig/request/body.rbs +42 -0
- data/sig/request.rbs +1 -27
- data/sig/resolver/resolver.rbs +1 -1
- data/sig/response/body.rbs +52 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +0 -39
- data/sig/session.rbs +2 -0
- 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 -10
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder.rbs +18 -2
- metadata +52 -34
- data/lib/httpx/plugins/authentication.rb +0 -24
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -54
- data/lib/httpx/plugins/compression/gzip.rb +0 -90
- data/lib/httpx/plugins/compression.rb +0 -165
- data/lib/httpx/plugins/multipart/decoder.rb +0 -137
- data/lib/httpx/plugins/multipart.rb +0 -96
- data/sig/plugins/authentication.rbs +0 -13
- 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/plugins/{authentication → auth}/digest.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
class Response::Body
|
5
|
+
attr_reader :encoding, :encodings
|
6
|
+
|
7
|
+
def initialize(response, options)
|
8
|
+
@response = response
|
9
|
+
@headers = response.headers
|
10
|
+
@options = options
|
11
|
+
@threshold_size = options.body_threshold_size
|
12
|
+
@window_size = options.window_size
|
13
|
+
@encoding = response.content_type.charset || Encoding::BINARY
|
14
|
+
@encodings = []
|
15
|
+
@length = 0
|
16
|
+
@buffer = nil
|
17
|
+
@state = :idle
|
18
|
+
initialize_inflaters
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize_dup(other)
|
22
|
+
super
|
23
|
+
|
24
|
+
@buffer = other.instance_variable_get(:@buffer).dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def closed?
|
28
|
+
@state == :closed
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(chunk)
|
32
|
+
return if @state == :closed
|
33
|
+
|
34
|
+
@inflaters.reverse_each do |inflater|
|
35
|
+
chunk = inflater.call(chunk)
|
36
|
+
end if @inflaters
|
37
|
+
|
38
|
+
size = chunk.bytesize
|
39
|
+
@length += size
|
40
|
+
transition(:open)
|
41
|
+
@buffer.write(chunk)
|
42
|
+
|
43
|
+
@response.emit(:chunk_received, chunk)
|
44
|
+
size
|
45
|
+
end
|
46
|
+
|
47
|
+
def read(*args)
|
48
|
+
return unless @buffer
|
49
|
+
|
50
|
+
unless @reader
|
51
|
+
rewind
|
52
|
+
@reader = @buffer
|
53
|
+
end
|
54
|
+
|
55
|
+
@reader.read(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def bytesize
|
59
|
+
@length
|
60
|
+
end
|
61
|
+
|
62
|
+
def each
|
63
|
+
return enum_for(__method__) unless block_given?
|
64
|
+
|
65
|
+
begin
|
66
|
+
if @buffer
|
67
|
+
rewind
|
68
|
+
while (chunk = @buffer.read(@window_size))
|
69
|
+
yield(chunk.force_encoding(@encoding))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
close
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def filename
|
78
|
+
return unless @headers.key?("content-disposition")
|
79
|
+
|
80
|
+
Utils.get_filename(@headers["content-disposition"])
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
return "".b unless @buffer
|
85
|
+
|
86
|
+
@buffer.to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :to_str, :to_s
|
90
|
+
|
91
|
+
def empty?
|
92
|
+
@length.zero?
|
93
|
+
end
|
94
|
+
|
95
|
+
def copy_to(dest)
|
96
|
+
return unless @buffer
|
97
|
+
|
98
|
+
rewind
|
99
|
+
|
100
|
+
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
101
|
+
FileUtils.mv(@buffer.path, dest.path)
|
102
|
+
else
|
103
|
+
::IO.copy_stream(@buffer, dest)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# closes/cleans the buffer, resets everything
|
108
|
+
def close
|
109
|
+
if @buffer
|
110
|
+
@buffer.close
|
111
|
+
@buffer = nil
|
112
|
+
end
|
113
|
+
@length = 0
|
114
|
+
transition(:closed)
|
115
|
+
end
|
116
|
+
|
117
|
+
def ==(other)
|
118
|
+
object_id == other.object_id || begin
|
119
|
+
if other.respond_to?(:read)
|
120
|
+
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
|
121
|
+
else
|
122
|
+
to_s == other.to_s
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# :nocov:
|
128
|
+
def inspect
|
129
|
+
"#<HTTPX::Response::Body:#{object_id} " \
|
130
|
+
"@state=#{@state} " \
|
131
|
+
"@length=#{@length}>"
|
132
|
+
end
|
133
|
+
# :nocov:
|
134
|
+
|
135
|
+
def rewind
|
136
|
+
return unless @buffer
|
137
|
+
|
138
|
+
# in case there's some reading going on
|
139
|
+
@reader = nil
|
140
|
+
|
141
|
+
@buffer.rewind
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def initialize_inflaters
|
147
|
+
return unless @headers.key?("content-encoding")
|
148
|
+
|
149
|
+
return unless @options.decompress_response_body
|
150
|
+
|
151
|
+
@inflaters = @headers.get("content-encoding").filter_map do |encoding|
|
152
|
+
next if encoding == "identity"
|
153
|
+
|
154
|
+
inflater = self.class.initialize_inflater_by_encoding(encoding, @response)
|
155
|
+
|
156
|
+
# do not uncompress if there is no decoder available. In fact, we can't reliably
|
157
|
+
# continue decompressing beyond that, so ignore.
|
158
|
+
break unless inflater
|
159
|
+
|
160
|
+
@encodings << encoding
|
161
|
+
inflater
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def transition(nextstate)
|
166
|
+
case nextstate
|
167
|
+
when :open
|
168
|
+
return unless @state == :idle
|
169
|
+
|
170
|
+
@buffer = Response::Buffer.new(
|
171
|
+
threshold_size: @threshold_size,
|
172
|
+
bytesize: @length,
|
173
|
+
encoding: @encoding
|
174
|
+
)
|
175
|
+
when :closed
|
176
|
+
return if @state == :closed
|
177
|
+
end
|
178
|
+
|
179
|
+
@state = nextstate
|
180
|
+
end
|
181
|
+
|
182
|
+
def _with_same_buffer_pos
|
183
|
+
return yield unless @buffer && @buffer.respond_to?(:pos)
|
184
|
+
|
185
|
+
# @type ivar @buffer: StringIO | Tempfile
|
186
|
+
current_pos = @buffer.pos
|
187
|
+
@buffer.rewind
|
188
|
+
begin
|
189
|
+
yield
|
190
|
+
ensure
|
191
|
+
@buffer.pos = current_pos
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class << self
|
196
|
+
def initialize_inflater_by_encoding(encoding, response, **kwargs)
|
197
|
+
case encoding
|
198
|
+
when "gzip"
|
199
|
+
Transcoder::GZIP.decode(response, **kwargs)
|
200
|
+
when "deflate"
|
201
|
+
Transcoder::Deflate.decode(response, **kwargs)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
require "stringio"
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module HTTPX
|
8
|
+
class Response::Buffer < SimpleDelegator
|
9
|
+
def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY)
|
10
|
+
@threshold_size = threshold_size
|
11
|
+
@bytesize = bytesize
|
12
|
+
@encoding = encoding
|
13
|
+
try_upgrade_buffer
|
14
|
+
super(@buffer)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_dup(other)
|
18
|
+
super
|
19
|
+
|
20
|
+
@buffer = other.instance_variable_get(:@buffer).dup
|
21
|
+
end
|
22
|
+
|
23
|
+
def size
|
24
|
+
@bytesize
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(chunk)
|
28
|
+
@bytesize += chunk.bytesize
|
29
|
+
try_upgrade_buffer
|
30
|
+
@buffer.write(chunk)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
case @buffer
|
35
|
+
when StringIO
|
36
|
+
begin
|
37
|
+
@buffer.string.force_encoding(@encoding)
|
38
|
+
rescue ArgumentError
|
39
|
+
@buffer.string
|
40
|
+
end
|
41
|
+
when Tempfile
|
42
|
+
rewind
|
43
|
+
content = _with_same_buffer_pos { @buffer.read }
|
44
|
+
begin
|
45
|
+
content.force_encoding(@encoding)
|
46
|
+
rescue ArgumentError # ex: unknown encoding name - utf
|
47
|
+
content
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
@buffer.close
|
54
|
+
@buffer.unlink if @buffer.respond_to?(:unlink)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def try_upgrade_buffer
|
60
|
+
if !@buffer.is_a?(Tempfile) && @bytesize > @threshold_size
|
61
|
+
aux = @buffer
|
62
|
+
|
63
|
+
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
64
|
+
|
65
|
+
if aux
|
66
|
+
aux.rewind
|
67
|
+
::IO.copy_stream(aux, @buffer)
|
68
|
+
aux.close
|
69
|
+
end
|
70
|
+
|
71
|
+
else
|
72
|
+
return if @buffer
|
73
|
+
|
74
|
+
@buffer = StringIO.new("".b)
|
75
|
+
|
76
|
+
end
|
77
|
+
__setobj__(@buffer)
|
78
|
+
end
|
79
|
+
|
80
|
+
def _with_same_buffer_pos
|
81
|
+
current_pos = @buffer.pos
|
82
|
+
@buffer.rewind
|
83
|
+
begin
|
84
|
+
yield
|
85
|
+
ensure
|
86
|
+
@buffer.pos = current_pos
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/httpx/response.rb
CHANGED
@@ -124,199 +124,6 @@ module HTTPX
|
|
124
124
|
content_length == "0"
|
125
125
|
end
|
126
126
|
end
|
127
|
-
|
128
|
-
class Body
|
129
|
-
attr_reader :encoding
|
130
|
-
|
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 initialize_dup(other)
|
144
|
-
super
|
145
|
-
|
146
|
-
@buffer = other.instance_variable_get(:@buffer).dup
|
147
|
-
end
|
148
|
-
|
149
|
-
def closed?
|
150
|
-
@state == :closed
|
151
|
-
end
|
152
|
-
|
153
|
-
def write(chunk)
|
154
|
-
return if @state == :closed
|
155
|
-
|
156
|
-
size = chunk.bytesize
|
157
|
-
@length += size
|
158
|
-
transition
|
159
|
-
@buffer.write(chunk)
|
160
|
-
|
161
|
-
@response.emit(:chunk_received, chunk)
|
162
|
-
size
|
163
|
-
end
|
164
|
-
|
165
|
-
def read(*args)
|
166
|
-
return unless @buffer
|
167
|
-
|
168
|
-
unless @reader
|
169
|
-
rewind
|
170
|
-
@reader = @buffer
|
171
|
-
end
|
172
|
-
|
173
|
-
@reader.read(*args)
|
174
|
-
end
|
175
|
-
|
176
|
-
def bytesize
|
177
|
-
@length
|
178
|
-
end
|
179
|
-
|
180
|
-
def each
|
181
|
-
return enum_for(__method__) unless block_given?
|
182
|
-
|
183
|
-
begin
|
184
|
-
if @buffer
|
185
|
-
rewind
|
186
|
-
while (chunk = @buffer.read(@window_size))
|
187
|
-
yield(chunk.force_encoding(@encoding))
|
188
|
-
end
|
189
|
-
end
|
190
|
-
ensure
|
191
|
-
close
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def filename
|
196
|
-
return unless @headers.key?("content-disposition")
|
197
|
-
|
198
|
-
Utils.get_filename(@headers["content-disposition"])
|
199
|
-
end
|
200
|
-
|
201
|
-
def to_s
|
202
|
-
case @buffer
|
203
|
-
when StringIO
|
204
|
-
begin
|
205
|
-
@buffer.string.force_encoding(@encoding)
|
206
|
-
rescue ArgumentError
|
207
|
-
@buffer.string
|
208
|
-
end
|
209
|
-
when Tempfile
|
210
|
-
rewind
|
211
|
-
content = _with_same_buffer_pos { @buffer.read }
|
212
|
-
begin
|
213
|
-
content.force_encoding(@encoding)
|
214
|
-
rescue ArgumentError # ex: unknown encoding name - utf
|
215
|
-
content
|
216
|
-
end
|
217
|
-
else
|
218
|
-
"".b
|
219
|
-
end
|
220
|
-
end
|
221
|
-
alias_method :to_str, :to_s
|
222
|
-
|
223
|
-
def empty?
|
224
|
-
@length.zero?
|
225
|
-
end
|
226
|
-
|
227
|
-
def copy_to(dest)
|
228
|
-
return unless @buffer
|
229
|
-
|
230
|
-
rewind
|
231
|
-
|
232
|
-
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
233
|
-
FileUtils.mv(@buffer.path, dest.path)
|
234
|
-
else
|
235
|
-
::IO.copy_stream(@buffer, dest)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# closes/cleans the buffer, resets everything
|
240
|
-
def close
|
241
|
-
if @buffer
|
242
|
-
@buffer.close
|
243
|
-
@buffer.unlink if @buffer.respond_to?(:unlink)
|
244
|
-
@buffer = nil
|
245
|
-
end
|
246
|
-
@length = 0
|
247
|
-
@state = :closed
|
248
|
-
end
|
249
|
-
|
250
|
-
def ==(other)
|
251
|
-
object_id == other.object_id || begin
|
252
|
-
if other.respond_to?(:read)
|
253
|
-
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
|
254
|
-
else
|
255
|
-
to_s == other.to_s
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
# :nocov:
|
261
|
-
def inspect
|
262
|
-
"#<HTTPX::Response::Body:#{object_id} " \
|
263
|
-
"@state=#{@state} " \
|
264
|
-
"@length=#{@length}>"
|
265
|
-
end
|
266
|
-
# :nocov:
|
267
|
-
|
268
|
-
def rewind
|
269
|
-
return unless @buffer
|
270
|
-
|
271
|
-
# in case there's some reading going on
|
272
|
-
@reader = nil
|
273
|
-
|
274
|
-
@buffer.rewind
|
275
|
-
end
|
276
|
-
|
277
|
-
private
|
278
|
-
|
279
|
-
def transition
|
280
|
-
case @state
|
281
|
-
when :idle
|
282
|
-
if @length > @threshold_size
|
283
|
-
@state = :buffer
|
284
|
-
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
285
|
-
else
|
286
|
-
@state = :memory
|
287
|
-
@buffer = StringIO.new("".b)
|
288
|
-
end
|
289
|
-
when :memory
|
290
|
-
# @type ivar @buffer: StringIO | Tempfile
|
291
|
-
if @length > @threshold_size
|
292
|
-
aux = @buffer
|
293
|
-
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
294
|
-
aux.rewind
|
295
|
-
::IO.copy_stream(aux, @buffer)
|
296
|
-
# (this looks like a bug from Ruby < 2.3
|
297
|
-
@buffer.pos = aux.pos ##################
|
298
|
-
########################################
|
299
|
-
aux.close
|
300
|
-
@state = :buffer
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
nil unless %i[memory buffer].include?(@state)
|
305
|
-
end
|
306
|
-
|
307
|
-
def _with_same_buffer_pos
|
308
|
-
return yield unless @buffer && @buffer.respond_to?(:pos)
|
309
|
-
|
310
|
-
# @type ivar @buffer: StringIO | Tempfile
|
311
|
-
current_pos = @buffer.pos
|
312
|
-
@buffer.rewind
|
313
|
-
begin
|
314
|
-
yield
|
315
|
-
ensure
|
316
|
-
@buffer.pos = current_pos
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
127
|
end
|
321
128
|
|
322
129
|
class ContentType
|
@@ -358,20 +165,8 @@ module HTTPX
|
|
358
165
|
log_exception(@error)
|
359
166
|
end
|
360
167
|
|
361
|
-
def
|
362
|
-
|
363
|
-
@error.message
|
364
|
-
end
|
365
|
-
|
366
|
-
if Exception.method_defined?(:full_message)
|
367
|
-
def to_s
|
368
|
-
@error.full_message(highlight: false)
|
369
|
-
end
|
370
|
-
else
|
371
|
-
def to_s
|
372
|
-
"#{@error.message} (#{@error.class})\n" \
|
373
|
-
"#{@error.backtrace.join("\n") if @error.backtrace}"
|
374
|
-
end
|
168
|
+
def to_s
|
169
|
+
@error.full_message(highlight: false)
|
375
170
|
end
|
376
171
|
|
377
172
|
def close
|
@@ -388,4 +183,6 @@ module HTTPX
|
|
388
183
|
end
|
389
184
|
end
|
390
185
|
|
391
|
-
|
186
|
+
require_relative "response/body"
|
187
|
+
require_relative "response/buffer"
|
188
|
+
require_relative "pmatch_extensions" if RUBY_VERSION >= "3.0.0"
|
data/lib/httpx/selector.rb
CHANGED
data/lib/httpx/session.rb
CHANGED
@@ -339,16 +339,9 @@ module HTTPX
|
|
339
339
|
end
|
340
340
|
self
|
341
341
|
end
|
342
|
-
|
343
|
-
# :nocov:
|
344
|
-
def plugins(pls)
|
345
|
-
warn ":#{__method__} is deprecated, use :plugin instead"
|
346
|
-
pls.each do |pl|
|
347
|
-
plugin(pl)
|
348
|
-
end
|
349
|
-
self
|
350
|
-
end
|
351
|
-
# :nocov:
|
352
342
|
end
|
353
343
|
end
|
344
|
+
|
345
|
+
# session may be overridden by certain adapters.
|
346
|
+
S = Session
|
354
347
|
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,57 +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
78
|
end
|