httpx 0.15.4 → 0.18.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/doc/release_notes/0_16_0.md +93 -0
- data/doc/release_notes/0_16_1.md +5 -0
- data/doc/release_notes/0_17_0.md +49 -0
- data/doc/release_notes/0_18_0.md +69 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +8 -14
- data/lib/httpx/adapters/webmock.rb +9 -3
- data/lib/httpx/altsvc.rb +2 -2
- data/lib/httpx/buffer.rb +1 -1
- data/lib/httpx/callbacks.rb +1 -1
- data/lib/httpx/chainable.rb +18 -11
- data/lib/httpx/connection/http1.rb +21 -13
- data/lib/httpx/connection/http2.rb +20 -25
- data/lib/httpx/connection.rb +73 -77
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/errors.rb +11 -11
- data/lib/httpx/extensions.rb +50 -4
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io/ssl.rb +3 -3
- data/lib/httpx/io/tls.rb +8 -8
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +108 -81
- data/lib/httpx/parser/http1.rb +11 -7
- data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
- data/lib/httpx/plugins/aws_sigv4.rb +19 -20
- data/lib/httpx/plugins/compression.rb +17 -14
- data/lib/httpx/plugins/cookies/cookie.rb +4 -2
- data/lib/httpx/plugins/cookies/jar.rb +21 -2
- data/lib/httpx/plugins/cookies.rb +20 -7
- data/lib/httpx/plugins/digest_authentication.rb +19 -15
- data/lib/httpx/plugins/expect.rb +26 -18
- data/lib/httpx/plugins/follow_redirects.rb +9 -9
- data/lib/httpx/plugins/grpc/call.rb +4 -1
- data/lib/httpx/plugins/grpc/message.rb +2 -2
- data/lib/httpx/plugins/grpc.rb +72 -46
- data/lib/httpx/plugins/h2c.rb +7 -3
- data/lib/httpx/plugins/internal_telemetry.rb +8 -8
- data/lib/httpx/plugins/multipart/decoder.rb +187 -0
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +3 -3
- data/lib/httpx/plugins/multipart/part.rb +2 -2
- data/lib/httpx/plugins/multipart.rb +16 -2
- data/lib/httpx/plugins/ntlm_authentication.rb +12 -10
- data/lib/httpx/plugins/proxy/socks4.rb +2 -1
- data/lib/httpx/plugins/proxy/socks5.rb +2 -1
- data/lib/httpx/plugins/proxy/ssh.rb +20 -13
- data/lib/httpx/plugins/proxy.rb +10 -10
- data/lib/httpx/plugins/response_cache/store.rb +55 -0
- data/lib/httpx/plugins/response_cache.rb +88 -0
- data/lib/httpx/plugins/retries.rb +46 -23
- data/lib/httpx/plugins/stream.rb +3 -4
- data/lib/httpx/plugins/upgrade.rb +7 -6
- data/lib/httpx/pool.rb +39 -13
- data/lib/httpx/registry.rb +2 -2
- data/lib/httpx/request.rb +16 -25
- data/lib/httpx/resolver/https.rb +4 -8
- data/lib/httpx/resolver/native.rb +19 -5
- data/lib/httpx/resolver/resolver_mixin.rb +2 -1
- data/lib/httpx/resolver/system.rb +2 -0
- data/lib/httpx/resolver.rb +2 -2
- data/lib/httpx/response.rb +91 -48
- data/lib/httpx/selector.rb +11 -24
- data/lib/httpx/session.rb +41 -23
- data/lib/httpx/session2.rb +23 -0
- data/lib/httpx/timers.rb +84 -0
- data/lib/httpx/transcoder/body.rb +3 -2
- data/lib/httpx/transcoder/chunker.rb +2 -1
- data/lib/httpx/transcoder/form.rb +20 -0
- data/lib/httpx/transcoder/json.rb +12 -0
- data/lib/httpx/transcoder.rb +62 -1
- data/lib/httpx/utils.rb +10 -2
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +7 -3
- data/sig/buffer.rbs +3 -1
- data/sig/chainable.rbs +31 -29
- data/sig/connection/http1.rbs +11 -5
- data/sig/connection/http2.rbs +16 -5
- data/sig/connection.rbs +31 -13
- data/sig/errors.rbs +35 -1
- data/sig/headers.rbs +20 -19
- data/sig/httpx.rbs +4 -1
- data/sig/loggable.rbs +3 -1
- data/sig/options.rbs +45 -34
- data/sig/parser/http1.rbs +3 -3
- data/sig/plugins/authentication.rbs +1 -1
- data/sig/plugins/aws_sdk_authentication.rbs +25 -3
- data/sig/plugins/aws_sigv4.rbs +13 -5
- data/sig/plugins/basic_authentication.rbs +1 -1
- data/sig/plugins/compression.rbs +4 -6
- data/sig/plugins/cookies/cookie.rbs +5 -7
- data/sig/plugins/cookies/jar.rbs +9 -10
- data/sig/plugins/cookies.rbs +4 -5
- data/sig/plugins/digest_authentication.rbs +2 -3
- data/sig/plugins/expect.rbs +2 -4
- data/sig/plugins/follow_redirects.rbs +3 -5
- data/sig/plugins/grpc.rbs +4 -7
- data/sig/plugins/h2c.rbs +0 -2
- data/sig/plugins/multipart.rbs +64 -10
- data/sig/plugins/ntlm_authentication.rbs +2 -3
- data/sig/plugins/persistent.rbs +3 -8
- data/sig/plugins/proxy/ssh.rbs +4 -4
- data/sig/plugins/proxy.rbs +13 -13
- data/sig/plugins/push_promise.rbs +0 -2
- data/sig/plugins/response_cache.rbs +35 -0
- data/sig/plugins/retries.rbs +7 -8
- data/sig/plugins/stream.rbs +1 -1
- data/sig/plugins/upgrade.rbs +2 -3
- data/sig/pool.rbs +7 -2
- data/sig/registry.rbs +1 -1
- data/sig/request.rbs +11 -8
- data/sig/resolver/native.rbs +10 -5
- data/sig/resolver/resolver_mixin.rbs +4 -5
- data/sig/resolver/system.rbs +4 -0
- data/sig/resolver.rbs +7 -0
- data/sig/response.rbs +26 -13
- data/sig/selector.rbs +11 -9
- data/sig/session.rbs +22 -23
- data/sig/timers.rbs +32 -0
- data/sig/transcoder/body.rbs +6 -1
- data/sig/transcoder/chunker.rbs +8 -2
- data/sig/transcoder/form.rbs +3 -1
- data/sig/transcoder/json.rbs +2 -0
- data/sig/transcoder.rbs +13 -5
- data/sig/utils.rbs +6 -0
- metadata +18 -18
- data/lib/httpx/request2.rb +0 -14
data/lib/httpx/request.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "delegate"
|
|
3
4
|
require "forwardable"
|
|
4
5
|
|
|
5
6
|
module HTTPX
|
|
@@ -40,16 +41,15 @@ module HTTPX
|
|
|
40
41
|
|
|
41
42
|
def_delegator :@body, :empty?
|
|
42
43
|
|
|
43
|
-
def_delegator :@body, :chunk!
|
|
44
|
-
|
|
45
44
|
def initialize(verb, uri, options = {})
|
|
46
45
|
@verb = verb.to_s.downcase.to_sym
|
|
47
46
|
@options = Options.new(options)
|
|
48
47
|
@uri = Utils.to_uri(uri)
|
|
49
48
|
if @uri.relative?
|
|
50
|
-
|
|
49
|
+
origin = @options.origin
|
|
50
|
+
raise(Error, "invalid URI: #{@uri}") unless origin
|
|
51
51
|
|
|
52
|
-
@uri =
|
|
52
|
+
@uri = origin.merge(@uri)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
raise(Error, "unknown method: #{verb}") unless METHODS.include?(@verb)
|
|
@@ -97,7 +97,7 @@ module HTTPX
|
|
|
97
97
|
def response=(response)
|
|
98
98
|
return unless response
|
|
99
99
|
|
|
100
|
-
if response.status == 100
|
|
100
|
+
if response.is_a?(Response) && response.status == 100
|
|
101
101
|
@informational_status = response.status
|
|
102
102
|
return
|
|
103
103
|
end
|
|
@@ -148,16 +148,16 @@ module HTTPX
|
|
|
148
148
|
# :nocov:
|
|
149
149
|
def inspect
|
|
150
150
|
"#<HTTPX::Request:#{object_id} " \
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
"#{@verb.to_s.upcase} " \
|
|
152
|
+
"#{uri} " \
|
|
153
|
+
"@headers=#{@headers} " \
|
|
154
|
+
"@body=#{@body}>"
|
|
155
155
|
end
|
|
156
156
|
# :nocov:
|
|
157
157
|
|
|
158
|
-
class Body
|
|
158
|
+
class Body < SimpleDelegator
|
|
159
159
|
class << self
|
|
160
|
-
def new(
|
|
160
|
+
def new(_, options)
|
|
161
161
|
return options.body if options.body.is_a?(self)
|
|
162
162
|
|
|
163
163
|
super
|
|
@@ -177,10 +177,11 @@ module HTTPX
|
|
|
177
177
|
|
|
178
178
|
@headers["content-type"] ||= @body.content_type
|
|
179
179
|
@headers["content-length"] = @body.bytesize unless unbounded_body?
|
|
180
|
+
super(@body)
|
|
180
181
|
end
|
|
181
182
|
|
|
182
183
|
def each(&block)
|
|
183
|
-
return enum_for(__method__) unless
|
|
184
|
+
return enum_for(__method__) unless block
|
|
184
185
|
return if @body.nil?
|
|
185
186
|
|
|
186
187
|
body = stream(@body)
|
|
@@ -214,14 +215,14 @@ module HTTPX
|
|
|
214
215
|
|
|
215
216
|
def stream(body)
|
|
216
217
|
encoded = body
|
|
217
|
-
encoded = Transcoder.registry("chunker").encode(body) if chunked?
|
|
218
|
+
encoded = Transcoder.registry("chunker").encode(body.enum_for(:each)) if chunked?
|
|
218
219
|
encoded
|
|
219
220
|
end
|
|
220
221
|
|
|
221
222
|
def unbounded_body?
|
|
222
223
|
return @unbounded_body if defined?(@unbounded_body)
|
|
223
224
|
|
|
224
|
-
@unbounded_body = (chunked? || @body.bytesize == Float::INFINITY)
|
|
225
|
+
@unbounded_body = !@body.nil? && (chunked? || @body.bytesize == Float::INFINITY)
|
|
225
226
|
end
|
|
226
227
|
|
|
227
228
|
def chunked?
|
|
@@ -235,19 +236,9 @@ module HTTPX
|
|
|
235
236
|
# :nocov:
|
|
236
237
|
def inspect
|
|
237
238
|
"#<HTTPX::Request::Body:#{object_id} " \
|
|
238
|
-
|
|
239
|
+
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
|
239
240
|
end
|
|
240
241
|
# :nocov:
|
|
241
|
-
|
|
242
|
-
def respond_to_missing?(meth, *args)
|
|
243
|
-
@body.respond_to?(meth, *args) || super
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def method_missing(meth, *args, &block)
|
|
247
|
-
return super unless @body.respond_to?(meth)
|
|
248
|
-
|
|
249
|
-
@body.__send__(meth, *args, &block)
|
|
250
|
-
end
|
|
251
242
|
end
|
|
252
243
|
|
|
253
244
|
def transition(nextstate)
|
data/lib/httpx/resolver/https.rb
CHANGED
|
@@ -24,9 +24,9 @@ module HTTPX
|
|
|
24
24
|
record_types: RECORD_TYPES.keys,
|
|
25
25
|
}.freeze
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
attr_writer :pool
|
|
30
30
|
|
|
31
31
|
def initialize(options)
|
|
32
32
|
@options = Options.new(options)
|
|
@@ -65,15 +65,11 @@ module HTTPX
|
|
|
65
65
|
|
|
66
66
|
private
|
|
67
67
|
|
|
68
|
-
def pool
|
|
69
|
-
Thread.current[:httpx_connection_pool] ||= Pool.new
|
|
70
|
-
end
|
|
71
|
-
|
|
72
68
|
def resolver_connection
|
|
73
|
-
@resolver_connection ||= pool.find_connection(@uri, @options) || begin
|
|
69
|
+
@resolver_connection ||= @pool.find_connection(@uri, @options) || begin
|
|
74
70
|
@building_connection = true
|
|
75
71
|
connection = @options.connection_class.new("ssl", @uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
|
|
76
|
-
pool.init_connection(connection, @options)
|
|
72
|
+
@pool.init_connection(connection, @options)
|
|
77
73
|
emit_addresses(connection, @uri_addresses)
|
|
78
74
|
@building_connection = false
|
|
79
75
|
connection
|
|
@@ -47,6 +47,8 @@ module HTTPX
|
|
|
47
47
|
|
|
48
48
|
def_delegator :@connections, :empty?
|
|
49
49
|
|
|
50
|
+
attr_reader :state
|
|
51
|
+
|
|
50
52
|
def initialize(options)
|
|
51
53
|
@options = Options.new(options)
|
|
52
54
|
@ns_index = 0
|
|
@@ -120,7 +122,7 @@ module HTTPX
|
|
|
120
122
|
def timeout
|
|
121
123
|
return if @connections.empty?
|
|
122
124
|
|
|
123
|
-
@start_timeout =
|
|
125
|
+
@start_timeout = Utils.now
|
|
124
126
|
hosts = @queries.keys
|
|
125
127
|
@timeouts.values_at(*hosts).reject(&:empty?).map(&:first).min
|
|
126
128
|
end
|
|
@@ -140,7 +142,7 @@ module HTTPX
|
|
|
140
142
|
def do_retry
|
|
141
143
|
return if @queries.empty?
|
|
142
144
|
|
|
143
|
-
loop_time =
|
|
145
|
+
loop_time = Utils.elapsed_time(@start_timeout)
|
|
144
146
|
connections = []
|
|
145
147
|
queries = {}
|
|
146
148
|
while (query = @queries.shift)
|
|
@@ -217,15 +219,27 @@ module HTTPX
|
|
|
217
219
|
end
|
|
218
220
|
else
|
|
219
221
|
address = addresses.first
|
|
220
|
-
|
|
221
|
-
|
|
222
|
+
name = address["name"]
|
|
223
|
+
|
|
224
|
+
connection = @queries.delete(name)
|
|
225
|
+
|
|
226
|
+
unless connection
|
|
227
|
+
# absolute name
|
|
228
|
+
name_labels = Resolv::DNS::Name.create(name).to_a
|
|
229
|
+
name = @queries.keys.first { |hname| name_labels == Resolv::DNS::Name.create(hname).to_a }
|
|
230
|
+
|
|
231
|
+
# probably a retried query for which there's an answer
|
|
232
|
+
return unless name
|
|
233
|
+
|
|
234
|
+
address["name"] = name
|
|
235
|
+
connection = @queries.delete(name)
|
|
236
|
+
end
|
|
222
237
|
|
|
223
238
|
if address.key?("alias") # CNAME
|
|
224
239
|
if early_resolve(connection, hostname: address["alias"])
|
|
225
240
|
@connections.delete(connection)
|
|
226
241
|
else
|
|
227
242
|
resolve(connection, address["alias"])
|
|
228
|
-
@queries.delete(address["name"])
|
|
229
243
|
return
|
|
230
244
|
end
|
|
231
245
|
else
|
|
@@ -9,7 +9,7 @@ module HTTPX
|
|
|
9
9
|
include Callbacks
|
|
10
10
|
include Loggable
|
|
11
11
|
|
|
12
|
-
CHECK_IF_IP =
|
|
12
|
+
CHECK_IF_IP = lambda do |name|
|
|
13
13
|
begin
|
|
14
14
|
IPAddr.new(name)
|
|
15
15
|
true
|
|
@@ -55,6 +55,7 @@ module HTTPX
|
|
|
55
55
|
return if ips.empty?
|
|
56
56
|
|
|
57
57
|
ips.map { |ip| IPAddr.new(ip) }
|
|
58
|
+
rescue IOError
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def emit_resolve_error(connection, hostname = connection.origin.host, ex = nil)
|
data/lib/httpx/resolver.rb
CHANGED
|
@@ -26,14 +26,14 @@ module HTTPX
|
|
|
26
26
|
module_function
|
|
27
27
|
|
|
28
28
|
def cached_lookup(hostname)
|
|
29
|
-
now =
|
|
29
|
+
now = Utils.now
|
|
30
30
|
@lookup_mutex.synchronize do
|
|
31
31
|
lookup(hostname, now)
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def cached_lookup_set(hostname, entries)
|
|
36
|
-
now =
|
|
36
|
+
now = Utils.now
|
|
37
37
|
entries.each do |entry|
|
|
38
38
|
entry["TTL"] += now
|
|
39
39
|
end
|
data/lib/httpx/response.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "objspace"
|
|
3
4
|
require "stringio"
|
|
4
5
|
require "tempfile"
|
|
5
6
|
require "fileutils"
|
|
@@ -13,6 +14,8 @@ module HTTPX
|
|
|
13
14
|
|
|
14
15
|
def_delegator :@body, :to_s
|
|
15
16
|
|
|
17
|
+
def_delegator :@body, :to_str
|
|
18
|
+
|
|
16
19
|
def_delegator :@body, :read
|
|
17
20
|
|
|
18
21
|
def_delegator :@body, :copy_to
|
|
@@ -44,7 +47,7 @@ module HTTPX
|
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def content_type
|
|
47
|
-
ContentType.
|
|
50
|
+
@content_type ||= ContentType.new(@headers["content-type"])
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
def complete?
|
|
@@ -54,21 +57,50 @@ module HTTPX
|
|
|
54
57
|
# :nocov:
|
|
55
58
|
def inspect
|
|
56
59
|
"#<Response:#{object_id} "\
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
"HTTP/#{version} " \
|
|
61
|
+
"@status=#{@status} " \
|
|
62
|
+
"@headers=#{@headers} " \
|
|
63
|
+
"@body=#{@body.bytesize}>"
|
|
61
64
|
end
|
|
62
65
|
# :nocov:
|
|
63
66
|
|
|
64
|
-
def
|
|
67
|
+
def error
|
|
65
68
|
return if @status < 400
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
HTTPError.new(self)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def raise_for_status
|
|
74
|
+
return self unless (err = error)
|
|
75
|
+
|
|
76
|
+
raise err
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def json(options = nil)
|
|
80
|
+
decode("json", options)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def form
|
|
84
|
+
decode("form")
|
|
68
85
|
end
|
|
69
86
|
|
|
70
87
|
private
|
|
71
88
|
|
|
89
|
+
def decode(format, options = nil)
|
|
90
|
+
# TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
|
|
91
|
+
transcoder = Transcoder.registry(format)
|
|
92
|
+
|
|
93
|
+
raise Error, "no decoder available for \"#{format}\"" unless transcoder.respond_to?(:decode)
|
|
94
|
+
|
|
95
|
+
decoder = transcoder.decode(self)
|
|
96
|
+
|
|
97
|
+
raise Error, "no decoder available for \"#{format}\"" unless decoder
|
|
98
|
+
|
|
99
|
+
decoder.call(self, options)
|
|
100
|
+
rescue Registry::Error
|
|
101
|
+
raise Error, "no decoder available for \"#{format}\""
|
|
102
|
+
end
|
|
103
|
+
|
|
72
104
|
def no_data?
|
|
73
105
|
@status < 200 ||
|
|
74
106
|
@status == 204 ||
|
|
@@ -134,18 +166,26 @@ module HTTPX
|
|
|
134
166
|
end
|
|
135
167
|
|
|
136
168
|
def to_s
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
169
|
+
case @buffer
|
|
170
|
+
when StringIO
|
|
171
|
+
begin
|
|
172
|
+
@buffer.string.force_encoding(@encoding)
|
|
173
|
+
rescue ArgumentError
|
|
174
|
+
@buffer.string
|
|
175
|
+
end
|
|
176
|
+
when Tempfile, File
|
|
177
|
+
rewind
|
|
178
|
+
content = _with_same_buffer_pos { @buffer.read }
|
|
140
179
|
begin
|
|
141
|
-
|
|
180
|
+
content.force_encoding(@encoding)
|
|
142
181
|
rescue ArgumentError # ex: unknown encoding name - utf
|
|
143
|
-
|
|
182
|
+
content
|
|
144
183
|
end
|
|
184
|
+
when nil
|
|
185
|
+
"".b
|
|
186
|
+
else
|
|
187
|
+
@buffer
|
|
145
188
|
end
|
|
146
|
-
"".b
|
|
147
|
-
ensure
|
|
148
|
-
close
|
|
149
189
|
end
|
|
150
190
|
alias_method :to_str, :to_s
|
|
151
191
|
|
|
@@ -177,14 +217,20 @@ module HTTPX
|
|
|
177
217
|
end
|
|
178
218
|
|
|
179
219
|
def ==(other)
|
|
180
|
-
|
|
220
|
+
object_id == other.object_id || begin
|
|
221
|
+
if other.respond_to?(:read)
|
|
222
|
+
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
|
|
223
|
+
else
|
|
224
|
+
to_s == other.to_s
|
|
225
|
+
end
|
|
226
|
+
end
|
|
181
227
|
end
|
|
182
228
|
|
|
183
229
|
# :nocov:
|
|
184
230
|
def inspect
|
|
185
231
|
"#<HTTPX::Response::Body:#{object_id} " \
|
|
186
|
-
|
|
187
|
-
|
|
232
|
+
"@state=#{@state} " \
|
|
233
|
+
"@length=#{@length}>"
|
|
188
234
|
end
|
|
189
235
|
# :nocov:
|
|
190
236
|
|
|
@@ -204,7 +250,7 @@ module HTTPX
|
|
|
204
250
|
@buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
|
205
251
|
else
|
|
206
252
|
@state = :memory
|
|
207
|
-
@buffer = StringIO.new("".b
|
|
253
|
+
@buffer = StringIO.new("".b)
|
|
208
254
|
end
|
|
209
255
|
when :memory
|
|
210
256
|
if @length > @threshold_size
|
|
@@ -222,6 +268,18 @@ module HTTPX
|
|
|
222
268
|
|
|
223
269
|
return unless %i[memory buffer].include?(@state)
|
|
224
270
|
end
|
|
271
|
+
|
|
272
|
+
def _with_same_buffer_pos
|
|
273
|
+
return yield unless @buffer && @buffer.respond_to?(:pos)
|
|
274
|
+
|
|
275
|
+
current_pos = @buffer.pos
|
|
276
|
+
@buffer.rewind
|
|
277
|
+
begin
|
|
278
|
+
yield
|
|
279
|
+
rescue StandardError
|
|
280
|
+
@buffer.pos = current_pos
|
|
281
|
+
end
|
|
282
|
+
end
|
|
225
283
|
end
|
|
226
284
|
end
|
|
227
285
|
|
|
@@ -229,30 +287,22 @@ module HTTPX
|
|
|
229
287
|
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
|
|
230
288
|
CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
|
|
231
289
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def initialize(mime_type, charset)
|
|
235
|
-
@mime_type = mime_type
|
|
236
|
-
@charset = charset
|
|
290
|
+
def initialize(header_value)
|
|
291
|
+
@header_value = header_value
|
|
237
292
|
end
|
|
238
293
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def parse(str)
|
|
242
|
-
new(mime_type(str), charset(str))
|
|
243
|
-
end
|
|
294
|
+
def mime_type
|
|
295
|
+
return @mime_type if defined?(@mime_type)
|
|
244
296
|
|
|
245
|
-
|
|
297
|
+
m = @header_value.to_s[MIME_TYPE_RE, 1]
|
|
298
|
+
m && @mime_type = m.strip.downcase
|
|
299
|
+
end
|
|
246
300
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
m && m.strip.downcase
|
|
250
|
-
end
|
|
301
|
+
def charset
|
|
302
|
+
return @charset if defined?(@charset)
|
|
251
303
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
m && m.strip.delete('"')
|
|
255
|
-
end
|
|
304
|
+
m = @header_value.to_s[CHARSET_RE, 1]
|
|
305
|
+
m && @charset = m.strip.delete('"')
|
|
256
306
|
end
|
|
257
307
|
end
|
|
258
308
|
|
|
@@ -269,31 +319,24 @@ module HTTPX
|
|
|
269
319
|
end
|
|
270
320
|
|
|
271
321
|
def status
|
|
322
|
+
warn ":#{__method__} is deprecated, use :error.message instead"
|
|
272
323
|
@error.message
|
|
273
324
|
end
|
|
274
325
|
|
|
275
326
|
if Exception.method_defined?(:full_message)
|
|
276
327
|
def to_s
|
|
277
|
-
@error.full_message
|
|
328
|
+
@error.full_message(highlight: false)
|
|
278
329
|
end
|
|
279
330
|
else
|
|
280
331
|
def to_s
|
|
281
332
|
"#{@error.message} (#{@error.class})\n" \
|
|
282
|
-
|
|
333
|
+
"#{@error.backtrace.join("\n") if @error.backtrace}"
|
|
283
334
|
end
|
|
284
335
|
end
|
|
285
336
|
|
|
286
337
|
def raise_for_status
|
|
287
338
|
raise @error
|
|
288
339
|
end
|
|
289
|
-
|
|
290
|
-
# rubocop:disable Style/MissingRespondToMissing
|
|
291
|
-
def method_missing(meth, *, &block)
|
|
292
|
-
raise NoMethodError, "undefined response method `#{meth}' for error response" if @options.response_class.public_method_defined?(meth)
|
|
293
|
-
|
|
294
|
-
super
|
|
295
|
-
end
|
|
296
|
-
# rubocop:enable Style/MissingRespondToMissing
|
|
297
340
|
end
|
|
298
341
|
end
|
|
299
342
|
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -2,20 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "io/wait"
|
|
4
4
|
|
|
5
|
-
module IOExtensions
|
|
6
|
-
refine IO do
|
|
7
|
-
# provides a fallback for rubies where IO#wait isn't implemented,
|
|
8
|
-
# but IO#wait_readable and IO#wait_writable are.
|
|
9
|
-
def wait(timeout = nil, _mode = :read_write)
|
|
10
|
-
r, w = IO.select([self], [self], nil, timeout)
|
|
11
|
-
|
|
12
|
-
return unless r || w
|
|
13
|
-
|
|
14
|
-
self
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
5
|
class HTTPX::Selector
|
|
20
6
|
READABLE = %i[rw r].freeze
|
|
21
7
|
WRITABLE = %i[rw w].freeze
|
|
@@ -23,7 +9,7 @@ class HTTPX::Selector
|
|
|
23
9
|
private_constant :READABLE
|
|
24
10
|
private_constant :WRITABLE
|
|
25
11
|
|
|
26
|
-
using IOExtensions
|
|
12
|
+
using HTTPX::IOExtensions
|
|
27
13
|
|
|
28
14
|
def initialize
|
|
29
15
|
@selectables = []
|
|
@@ -43,9 +29,6 @@ class HTTPX::Selector
|
|
|
43
29
|
|
|
44
30
|
private
|
|
45
31
|
|
|
46
|
-
READ_INTERESTS = %i[r rw].freeze
|
|
47
|
-
WRITE_INTERESTS = %i[w rw].freeze
|
|
48
|
-
|
|
49
32
|
def select_many(interval, &block)
|
|
50
33
|
selectables, r, w = nil
|
|
51
34
|
|
|
@@ -61,11 +44,13 @@ class HTTPX::Selector
|
|
|
61
44
|
selectables = @selectables
|
|
62
45
|
@selectables = []
|
|
63
46
|
|
|
64
|
-
selectables.
|
|
47
|
+
selectables.delete_if do |io|
|
|
65
48
|
interests = io.interests
|
|
66
49
|
|
|
67
|
-
(r ||= []) << io if
|
|
68
|
-
(w ||= []) << io if
|
|
50
|
+
(r ||= []) << io if READABLE.include?(interests)
|
|
51
|
+
(w ||= []) << io if WRITABLE.include?(interests)
|
|
52
|
+
|
|
53
|
+
io.state == :closed
|
|
69
54
|
end
|
|
70
55
|
|
|
71
56
|
if @selectables.empty?
|
|
@@ -73,7 +58,7 @@ class HTTPX::Selector
|
|
|
73
58
|
|
|
74
59
|
# do not run event loop if there's nothing to wait on.
|
|
75
60
|
# this might happen if connect failed and connection was unregistered.
|
|
76
|
-
return if (!r || r.empty?) && (!w || w.empty?)
|
|
61
|
+
return if (!r || r.empty?) && (!w || w.empty?) && !selectables.empty?
|
|
77
62
|
|
|
78
63
|
break
|
|
79
64
|
else
|
|
@@ -89,7 +74,7 @@ class HTTPX::Selector
|
|
|
89
74
|
|
|
90
75
|
readers, writers = IO.select(r, w, nil, interval)
|
|
91
76
|
|
|
92
|
-
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
|
77
|
+
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil? && interval
|
|
93
78
|
rescue IOError, SystemCallError
|
|
94
79
|
@selectables.reject!(&:closed?)
|
|
95
80
|
retry
|
|
@@ -112,6 +97,8 @@ class HTTPX::Selector
|
|
|
112
97
|
def select_one(interval)
|
|
113
98
|
io = @selectables.first
|
|
114
99
|
|
|
100
|
+
return unless io
|
|
101
|
+
|
|
115
102
|
interests = io.interests
|
|
116
103
|
|
|
117
104
|
result = case interests
|
|
@@ -121,7 +108,7 @@ class HTTPX::Selector
|
|
|
121
108
|
when nil then return
|
|
122
109
|
end
|
|
123
110
|
|
|
124
|
-
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result
|
|
111
|
+
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result || interval.nil?
|
|
125
112
|
|
|
126
113
|
yield io
|
|
127
114
|
rescue IOError, SystemCallError
|