httpx 0.24.6 → 1.0.0
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 +4 -13
- data/doc/release_notes/0_24_4.md +3 -3
- 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 +0 -23
- 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 +0 -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/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 +50 -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
|
@@ -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
|