httpx 0.15.4 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|