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/session.rb
CHANGED
|
@@ -11,18 +11,17 @@ module HTTPX
|
|
|
11
11
|
@options = self.class.default_options.merge(options)
|
|
12
12
|
@responses = {}
|
|
13
13
|
@persistent = @options.persistent
|
|
14
|
-
wrap(&blk) if
|
|
14
|
+
wrap(&blk) if blk
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def wrap
|
|
18
|
-
return unless block_given?
|
|
19
|
-
|
|
20
18
|
begin
|
|
21
19
|
prev_persistent = @persistent
|
|
22
20
|
@persistent = true
|
|
23
21
|
yield self
|
|
24
22
|
ensure
|
|
25
23
|
@persistent = prev_persistent
|
|
24
|
+
close unless @persistent
|
|
26
25
|
end
|
|
27
26
|
end
|
|
28
27
|
|
|
@@ -31,8 +30,10 @@ module HTTPX
|
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def request(*args, **options)
|
|
33
|
+
raise ArgumentError, "must perform at least one request" if args.empty?
|
|
34
|
+
|
|
34
35
|
requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
|
|
35
|
-
responses = send_requests(*requests
|
|
36
|
+
responses = send_requests(*requests)
|
|
36
37
|
return responses.first if responses.size == 1
|
|
37
38
|
|
|
38
39
|
responses
|
|
@@ -40,7 +41,8 @@ module HTTPX
|
|
|
40
41
|
|
|
41
42
|
def build_request(verb, uri, options = EMPTY_HASH)
|
|
42
43
|
rklass = @options.request_class
|
|
43
|
-
|
|
44
|
+
options = @options.merge(options) unless options.is_a?(Options)
|
|
45
|
+
request = rklass.new(verb, uri, options.merge(persistent: @persistent))
|
|
44
46
|
request.on(:response, &method(:on_response).curry(2)[request])
|
|
45
47
|
request.on(:promise, &method(:on_promise))
|
|
46
48
|
request
|
|
@@ -174,37 +176,38 @@ module HTTPX
|
|
|
174
176
|
end
|
|
175
177
|
end
|
|
176
178
|
|
|
177
|
-
def send_requests(*requests
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
connections = _send_requests(requests, request_options)
|
|
181
|
-
receive_requests(requests, connections, request_options)
|
|
179
|
+
def send_requests(*requests)
|
|
180
|
+
connections = _send_requests(requests)
|
|
181
|
+
receive_requests(requests, connections)
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
-
def _send_requests(requests
|
|
184
|
+
def _send_requests(requests)
|
|
185
185
|
connections = []
|
|
186
186
|
|
|
187
187
|
requests.each do |request|
|
|
188
188
|
error = catch(:resolve_error) do
|
|
189
|
-
connection = find_connection(request, connections, options)
|
|
189
|
+
connection = find_connection(request, connections, request.options)
|
|
190
190
|
connection.send(request)
|
|
191
191
|
end
|
|
192
192
|
next unless error.is_a?(ResolveError)
|
|
193
193
|
|
|
194
|
-
request.emit(:response, ErrorResponse.new(request, error, options))
|
|
194
|
+
request.emit(:response, ErrorResponse.new(request, error, request.options))
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
connections
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
-
def receive_requests(requests, connections
|
|
200
|
+
def receive_requests(requests, connections)
|
|
201
201
|
responses = []
|
|
202
202
|
|
|
203
203
|
begin
|
|
204
204
|
# guarantee ordered responses
|
|
205
205
|
loop do
|
|
206
206
|
request = requests.first
|
|
207
|
-
|
|
207
|
+
|
|
208
|
+
return responses unless request
|
|
209
|
+
|
|
210
|
+
pool.next_tick until (response = fetch_response(request, connections, request.options))
|
|
208
211
|
|
|
209
212
|
responses << response
|
|
210
213
|
requests.shift
|
|
@@ -218,13 +221,17 @@ module HTTPX
|
|
|
218
221
|
# opportunity to traverse the requests, hence we're returning only a fraction of the errors
|
|
219
222
|
# we were supposed to. This effectively fetches the existing responses and return them.
|
|
220
223
|
while (request = requests.shift)
|
|
221
|
-
responses << fetch_response(request, connections, options)
|
|
224
|
+
responses << fetch_response(request, connections, request.options)
|
|
222
225
|
end
|
|
223
226
|
break
|
|
224
227
|
end
|
|
225
228
|
responses
|
|
226
229
|
ensure
|
|
227
|
-
|
|
230
|
+
if @persistent
|
|
231
|
+
pool.deactivate(connections)
|
|
232
|
+
else
|
|
233
|
+
close(connections)
|
|
234
|
+
end
|
|
228
235
|
end
|
|
229
236
|
end
|
|
230
237
|
|
|
@@ -247,9 +254,8 @@ module HTTPX
|
|
|
247
254
|
if !@plugins.include?(pl)
|
|
248
255
|
@plugins << pl
|
|
249
256
|
pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
|
|
257
|
+
|
|
250
258
|
@default_options = @default_options.dup
|
|
251
|
-
@default_options = pl.extra_options(@default_options, &block) if pl.respond_to?(:extra_options)
|
|
252
|
-
@default_options = @default_options.merge(options) if options
|
|
253
259
|
|
|
254
260
|
include(pl::InstanceMethods) if defined?(pl::InstanceMethods)
|
|
255
261
|
extend(pl::ClassMethods) if defined?(pl::ClassMethods)
|
|
@@ -266,14 +272,26 @@ module HTTPX
|
|
|
266
272
|
opts.response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
|
267
273
|
opts.response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
|
268
274
|
opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
|
|
275
|
+
if defined?(pl::OptionsMethods)
|
|
276
|
+
opts.options_class.__send__(:include, pl::OptionsMethods)
|
|
277
|
+
|
|
278
|
+
(pl::OptionsMethods.instance_methods - Object.instance_methods).each do |meth|
|
|
279
|
+
opts.options_class.method_added(meth)
|
|
280
|
+
end
|
|
281
|
+
@default_options = opts.options_class.new(opts)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
@default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
|
|
285
|
+
@default_options = @default_options.merge(options) if options
|
|
286
|
+
|
|
269
287
|
pl.configure(self, &block) if pl.respond_to?(:configure)
|
|
270
288
|
|
|
271
289
|
@default_options.freeze
|
|
272
290
|
elsif options
|
|
273
291
|
# this can happen when two plugins are loaded, an one of them calls the other under the hood,
|
|
274
292
|
# albeit changing some default.
|
|
275
|
-
@default_options = @default_options.
|
|
276
|
-
@default_options = @default_options.merge(options)
|
|
293
|
+
@default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
|
|
294
|
+
@default_options = @default_options.merge(options) if options
|
|
277
295
|
|
|
278
296
|
@default_options.freeze
|
|
279
297
|
end
|
|
@@ -283,8 +301,8 @@ module HTTPX
|
|
|
283
301
|
# :nocov:
|
|
284
302
|
def plugins(pls)
|
|
285
303
|
warn ":#{__method__} is deprecated, use :plugin instead"
|
|
286
|
-
pls.each do |pl
|
|
287
|
-
plugin(pl
|
|
304
|
+
pls.each do |pl|
|
|
305
|
+
plugin(pl)
|
|
288
306
|
end
|
|
289
307
|
self
|
|
290
308
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "session"
|
|
4
|
+
module HTTPX
|
|
5
|
+
class Session
|
|
6
|
+
def initialize(options = EMPTY_HASH, &blk)
|
|
7
|
+
@options = self.class.default_options.merge(options)
|
|
8
|
+
@responses = {}
|
|
9
|
+
@persistent = @options.persistent
|
|
10
|
+
wrap(&blk) if blk
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def wrap
|
|
14
|
+
begin
|
|
15
|
+
prev_persistent = @persistent
|
|
16
|
+
@persistent = true
|
|
17
|
+
yield self
|
|
18
|
+
ensure
|
|
19
|
+
@persistent = prev_persistent
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/httpx/timers.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
class Timers
|
|
5
|
+
def initialize
|
|
6
|
+
@intervals = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def after(interval_in_secs, &blk)
|
|
10
|
+
return unless interval_in_secs
|
|
11
|
+
|
|
12
|
+
# I'm assuming here that most requests will have the same
|
|
13
|
+
# request timeout, as in most cases they share common set of
|
|
14
|
+
# options. A user setting different request timeouts for 100s of
|
|
15
|
+
# requests will already have a hard time dealing with that.
|
|
16
|
+
unless (interval = @intervals.find { |t| t == interval_in_secs })
|
|
17
|
+
interval = Interval.new(interval_in_secs)
|
|
18
|
+
@intervals << interval
|
|
19
|
+
@intervals.sort!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
interval << blk
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def wait_interval
|
|
26
|
+
return if @intervals.empty?
|
|
27
|
+
|
|
28
|
+
@next_interval_at = Utils.now
|
|
29
|
+
|
|
30
|
+
@intervals.first.interval
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fire(error = nil)
|
|
34
|
+
raise error if error && error.timeout != @intervals.first
|
|
35
|
+
return if @intervals.empty? || !@next_interval_at
|
|
36
|
+
|
|
37
|
+
elapsed_time = Utils.elapsed_time(@next_interval_at)
|
|
38
|
+
|
|
39
|
+
@intervals.delete_if { |interval| interval.elapse(elapsed_time) <= 0 }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cancel
|
|
43
|
+
@intervals.clear
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Interval
|
|
47
|
+
include Comparable
|
|
48
|
+
|
|
49
|
+
attr_reader :interval
|
|
50
|
+
|
|
51
|
+
def initialize(interval)
|
|
52
|
+
@interval = interval
|
|
53
|
+
@callbacks = []
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def <=>(other)
|
|
57
|
+
@interval <=> other.interval
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ==(other)
|
|
61
|
+
return @interval == other if other.is_a?(Numeric)
|
|
62
|
+
|
|
63
|
+
@interval == other.to_f # rubocop:disable Lint/FloatComparison
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def to_f
|
|
67
|
+
@interval
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def <<(callback)
|
|
71
|
+
@callbacks << callback
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def elapse(elapsed)
|
|
75
|
+
@interval -= elapsed
|
|
76
|
+
|
|
77
|
+
@callbacks.each(&:call) if @interval <= 0
|
|
78
|
+
|
|
79
|
+
@interval
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
private_constant :Interval
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -4,11 +4,12 @@ require "forwardable"
|
|
|
4
4
|
|
|
5
5
|
module HTTPX::Transcoder
|
|
6
6
|
module Body
|
|
7
|
-
Error
|
|
7
|
+
class Error < HTTPX::Error; end
|
|
8
8
|
|
|
9
9
|
module_function
|
|
10
10
|
|
|
11
11
|
class Encoder
|
|
12
|
+
using HTTPX::ArrayExtensions
|
|
12
13
|
extend Forwardable
|
|
13
14
|
|
|
14
15
|
def_delegator :@raw, :to_s
|
|
@@ -21,7 +22,7 @@ module HTTPX::Transcoder
|
|
|
21
22
|
if @raw.respond_to?(:bytesize)
|
|
22
23
|
@raw.bytesize
|
|
23
24
|
elsif @raw.respond_to?(:to_ary)
|
|
24
|
-
@raw.
|
|
25
|
+
@raw.sum(&:bytesize)
|
|
25
26
|
elsif @raw.respond_to?(:size)
|
|
26
27
|
@raw.size || Float::INFINITY
|
|
27
28
|
elsif @raw.respond_to?(:length)
|
|
@@ -7,6 +7,8 @@ module HTTPX::Transcoder
|
|
|
7
7
|
module Form
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
+
PARAM_DEPTH_LIMIT = 32
|
|
11
|
+
|
|
10
12
|
class Encoder
|
|
11
13
|
extend Forwardable
|
|
12
14
|
|
|
@@ -31,9 +33,27 @@ module HTTPX::Transcoder
|
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
module Decoder
|
|
37
|
+
module_function
|
|
38
|
+
|
|
39
|
+
def call(response, _)
|
|
40
|
+
URI.decode_www_form(response.to_s).each_with_object({}) do |(field, value), params|
|
|
41
|
+
HTTPX::Transcoder.normalize_query(params, field, value, PARAM_DEPTH_LIMIT)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
34
46
|
def encode(form)
|
|
35
47
|
Encoder.new(form)
|
|
36
48
|
end
|
|
49
|
+
|
|
50
|
+
def decode(response)
|
|
51
|
+
content_type = response.content_type.mime_type
|
|
52
|
+
|
|
53
|
+
raise HTTPX::Error, "invalid form mime type (#{content_type})" unless content_type == "application/x-www-form-urlencoded"
|
|
54
|
+
|
|
55
|
+
Decoder
|
|
56
|
+
end
|
|
37
57
|
end
|
|
38
58
|
register "form", Form
|
|
39
59
|
end
|
|
@@ -5,6 +5,10 @@ require "json"
|
|
|
5
5
|
|
|
6
6
|
module HTTPX::Transcoder
|
|
7
7
|
module JSON
|
|
8
|
+
JSON_REGEX = %r{\bapplication/(?:vnd\.api\+)?json\b}i.freeze
|
|
9
|
+
|
|
10
|
+
using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?)
|
|
11
|
+
|
|
8
12
|
module_function
|
|
9
13
|
|
|
10
14
|
class Encoder
|
|
@@ -27,6 +31,14 @@ module HTTPX::Transcoder
|
|
|
27
31
|
def encode(json)
|
|
28
32
|
Encoder.new(json)
|
|
29
33
|
end
|
|
34
|
+
|
|
35
|
+
def decode(response)
|
|
36
|
+
content_type = response.content_type.mime_type
|
|
37
|
+
|
|
38
|
+
raise HTTPX::Error, "invalid json mime type (#{content_type})" unless JSON_REGEX.match?(content_type)
|
|
39
|
+
|
|
40
|
+
::JSON.method(:parse)
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
register "json", JSON
|
|
32
44
|
end
|
data/lib/httpx/transcoder.rb
CHANGED
|
@@ -4,7 +4,11 @@ module HTTPX
|
|
|
4
4
|
module Transcoder
|
|
5
5
|
extend Registry
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
using RegexpExtensions unless Regexp.method_defined?(:match?)
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def normalize_keys(key, value, cond = nil, &block)
|
|
8
12
|
if (cond && cond.call(value))
|
|
9
13
|
block.call(key.to_s, value)
|
|
10
14
|
elsif value.respond_to?(:to_ary)
|
|
@@ -23,6 +27,63 @@ module HTTPX
|
|
|
23
27
|
block.call(key.to_s, value)
|
|
24
28
|
end
|
|
25
29
|
end
|
|
30
|
+
|
|
31
|
+
# based on https://github.com/rack/rack/blob/d15dd728440710cfc35ed155d66a98dc2c07ae42/lib/rack/query_parser.rb#L82
|
|
32
|
+
def normalize_query(params, name, v, depth)
|
|
33
|
+
raise Error, "params depth surpasses what's supported" if depth <= 0
|
|
34
|
+
|
|
35
|
+
name =~ /\A[\[\]]*([^\[\]]+)\]*/
|
|
36
|
+
k = Regexp.last_match(1) || ""
|
|
37
|
+
after = Regexp.last_match ? Regexp.last_match.post_match : ""
|
|
38
|
+
|
|
39
|
+
if k.empty?
|
|
40
|
+
return Array(v) if !v.empty? && name == "[]"
|
|
41
|
+
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
case after
|
|
46
|
+
when ""
|
|
47
|
+
params[k] = v
|
|
48
|
+
when "["
|
|
49
|
+
params[name] = v
|
|
50
|
+
when "[]"
|
|
51
|
+
params[k] ||= []
|
|
52
|
+
raise Error, "expected Array (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Array)
|
|
53
|
+
|
|
54
|
+
params[k] << v
|
|
55
|
+
when /^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/
|
|
56
|
+
child_key = Regexp.last_match(1)
|
|
57
|
+
params[k] ||= []
|
|
58
|
+
raise Error, "expected Array (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Array)
|
|
59
|
+
|
|
60
|
+
if params[k].last.is_a?(Hash) && !params_hash_has_key?(params[k].last, child_key)
|
|
61
|
+
normalize_query(params[k].last, child_key, v, depth - 1)
|
|
62
|
+
else
|
|
63
|
+
params[k] << normalize_query({}, child_key, v, depth - 1)
|
|
64
|
+
end
|
|
65
|
+
else
|
|
66
|
+
params[k] ||= {}
|
|
67
|
+
raise Error, "expected Hash (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Hash)
|
|
68
|
+
|
|
69
|
+
params[k] = normalize_query(params[k], after, v, depth - 1)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
params
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def params_hash_has_key?(hash, key)
|
|
76
|
+
return false if /\[\]/.match?(key)
|
|
77
|
+
|
|
78
|
+
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
|
79
|
+
next h if part == ""
|
|
80
|
+
return false unless h.is_a?(Hash) && h.key?(part)
|
|
81
|
+
|
|
82
|
+
h[part]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
true
|
|
86
|
+
end
|
|
26
87
|
end
|
|
27
88
|
end
|
|
28
89
|
|
data/lib/httpx/utils.rb
CHANGED
|
@@ -6,6 +6,14 @@ module HTTPX
|
|
|
6
6
|
|
|
7
7
|
module_function
|
|
8
8
|
|
|
9
|
+
def now
|
|
10
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def elapsed_time(monotonic_timestamp)
|
|
14
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - monotonic_timestamp
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
# The value of this field can be either an HTTP-date or a number of
|
|
10
18
|
# seconds to delay after the response is received.
|
|
11
19
|
def parse_retry_after(retry_after)
|
|
@@ -28,9 +36,9 @@ module HTTPX
|
|
|
28
36
|
URIParser = URI::RFC2396_Parser.new
|
|
29
37
|
|
|
30
38
|
def to_uri(uri)
|
|
31
|
-
return
|
|
39
|
+
return URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
|
|
32
40
|
|
|
33
|
-
uri =
|
|
41
|
+
uri = URI(URIParser.escape(uri))
|
|
34
42
|
|
|
35
43
|
non_ascii_hostname = URIParser.unescape(uri.host)
|
|
36
44
|
|
data/lib/httpx/version.rb
CHANGED
data/lib/httpx.rb
CHANGED
|
@@ -13,6 +13,7 @@ require "httpx/callbacks"
|
|
|
13
13
|
require "httpx/loggable"
|
|
14
14
|
require "httpx/registry"
|
|
15
15
|
require "httpx/transcoder"
|
|
16
|
+
require "httpx/timers"
|
|
16
17
|
require "httpx/pool"
|
|
17
18
|
require "httpx/headers"
|
|
18
19
|
require "httpx/request"
|
|
@@ -20,6 +21,7 @@ require "httpx/response"
|
|
|
20
21
|
require "httpx/options"
|
|
21
22
|
require "httpx/chainable"
|
|
22
23
|
|
|
24
|
+
require "mutex_m"
|
|
23
25
|
# Top-Level Namespace
|
|
24
26
|
#
|
|
25
27
|
module HTTPX
|
|
@@ -28,15 +30,16 @@ module HTTPX
|
|
|
28
30
|
#
|
|
29
31
|
module Plugins
|
|
30
32
|
@plugins = {}
|
|
33
|
+
@plugins.extend(Mutex_m)
|
|
31
34
|
|
|
32
35
|
# Loads a plugin based on a name. If the plugin hasn't been loaded, tries to load
|
|
33
36
|
# it from the load path under "httpx/plugins/" directory.
|
|
34
37
|
#
|
|
35
38
|
def self.load_plugin(name)
|
|
36
39
|
h = @plugins
|
|
37
|
-
unless (plugin = h[name])
|
|
40
|
+
unless (plugin = h.synchronize { h[name] })
|
|
38
41
|
require "httpx/plugins/#{name}"
|
|
39
|
-
raise "Plugin #{name} hasn't been registered" unless (plugin = h[name])
|
|
42
|
+
raise "Plugin #{name} hasn't been registered" unless (plugin = h.synchronize { h[name] })
|
|
40
43
|
end
|
|
41
44
|
plugin
|
|
42
45
|
end
|
|
@@ -44,7 +47,8 @@ module HTTPX
|
|
|
44
47
|
# Registers a plugin (+mod+) in the central store indexed by +name+.
|
|
45
48
|
#
|
|
46
49
|
def self.register_plugin(name, mod)
|
|
47
|
-
|
|
50
|
+
h = @plugins
|
|
51
|
+
h.synchronize { h[name] = mod }
|
|
48
52
|
end
|
|
49
53
|
end
|
|
50
54
|
|
data/sig/buffer.rbs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module HTTPX
|
|
2
2
|
class Buffer
|
|
3
|
+
extend Forwardable
|
|
4
|
+
|
|
3
5
|
include _ToS
|
|
4
6
|
include _ToStr
|
|
5
7
|
|
|
@@ -11,7 +13,7 @@ module HTTPX
|
|
|
11
13
|
def shift!: (Integer) -> void
|
|
12
14
|
|
|
13
15
|
# delegated
|
|
14
|
-
def <<: (string data) ->
|
|
16
|
+
def <<: (string data) -> String
|
|
15
17
|
def empty?: () -> bool
|
|
16
18
|
def bytesize: () -> Integer
|
|
17
19
|
def clear: () -> void
|
data/sig/chainable.rbs
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
module HTTPX
|
|
2
2
|
module Chainable
|
|
3
|
-
def request: (*
|
|
3
|
+
def request: (*Request, **untyped) -> Array[response]
|
|
4
|
+
| (Request, **untyped) -> response
|
|
5
|
+
| (verb | string, uri | [uri], **untyped) -> response
|
|
6
|
+
| (Array[[verb | string, uri] | [verb | string, uri, options]], **untyped) -> Array[response]
|
|
7
|
+
| (verb | string, _Each[uri | [uri, options]], **untyped) -> Array[response]
|
|
8
|
+
|
|
4
9
|
def accept: (String) -> Session
|
|
5
10
|
def wrap: () { (Session) -> void } -> void
|
|
6
|
-
| () -> void
|
|
7
11
|
|
|
8
12
|
def with: (options) -> Session
|
|
9
|
-
| (options) { (Session) ->
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
| (options) { (Session) -> void } -> void
|
|
13
14
|
|
|
14
|
-
def plugin: (:authentication) -> Plugins::sessionAuthentication
|
|
15
|
-
| (:basic_authentication) -> Plugins::sessionBasicAuthentication
|
|
16
|
-
| (:digest_authentication) -> Plugins::sessionDigestAuthentication
|
|
17
|
-
| (:ntlm_authentication) -> Plugins::sessionNTLMAuthentication
|
|
18
|
-
| (:aws_sdk_authentication) -> Plugins::sessionAwsSdkAuthentication
|
|
19
|
-
| (:compression) -> Session
|
|
20
|
-
| (:cookies) -> Plugins::sessionCookies
|
|
21
|
-
| (:expect) -> Session
|
|
22
|
-
| (:follow_redirects) -> Plugins::sessionFollowRedirects
|
|
23
|
-
| (:upgrade) -> Session
|
|
24
|
-
| (:h2c) -> Session
|
|
25
|
-
| (:multipart) -> Session
|
|
26
|
-
| (:persistent) -> Plugins::sessionPersistent
|
|
27
|
-
| (:proxy) -> Plugins::sessionProxy
|
|
28
|
-
| (:push_promise) -> Plugins::sessionPushPromise
|
|
29
|
-
| (:retries) -> Plugins::sessionRetries
|
|
30
|
-
| (:rate_limiter) -> Session
|
|
31
|
-
| (:stream) -> Plugins::sessionStream
|
|
32
|
-
| (:aws_sigv4) -> Plugins::awsSigV4Session
|
|
33
|
-
| (:grpc) -> Plugins::grpcSession
|
|
34
|
-
| (
|
|
35
|
-
| (Symbol | Module, ?options
|
|
15
|
+
def plugin: (:authentication, ?options) -> Plugins::sessionAuthentication
|
|
16
|
+
| (:basic_authentication, ?options) -> Plugins::sessionBasicAuthentication
|
|
17
|
+
| (:digest_authentication, ?options) -> Plugins::sessionDigestAuthentication
|
|
18
|
+
| (:ntlm_authentication, ?options) -> Plugins::sessionNTLMAuthentication
|
|
19
|
+
| (:aws_sdk_authentication, ?options) -> Plugins::sessionAwsSdkAuthentication
|
|
20
|
+
| (:compression, ?options) -> Session
|
|
21
|
+
| (:cookies, ?options) -> Plugins::sessionCookies
|
|
22
|
+
| (:expect, ?options) -> Session
|
|
23
|
+
| (:follow_redirects, ?options) -> Plugins::sessionFollowRedirects
|
|
24
|
+
| (:upgrade, ?options) -> Session
|
|
25
|
+
| (:h2c, ?options) -> Session
|
|
26
|
+
| (:multipart, ?options) -> Session
|
|
27
|
+
| (:persistent, ?options) -> Plugins::sessionPersistent
|
|
28
|
+
| (:proxy, ?options) -> Plugins::sessionProxy
|
|
29
|
+
| (:push_promise, ?options) -> Plugins::sessionPushPromise
|
|
30
|
+
| (:retries, ?options) -> Plugins::sessionRetries
|
|
31
|
+
| (:rate_limiter, ?options) -> Session
|
|
32
|
+
| (:stream, ?options) -> Plugins::sessionStream
|
|
33
|
+
| (:aws_sigv4, ?options) -> Plugins::awsSigV4Session
|
|
34
|
+
| (:grpc, ?options) -> Plugins::grpcSession
|
|
35
|
+
| (:response_cache, ?options) -> Plugins::sessionResponseCache
|
|
36
|
+
| (Symbol | Module, ?options) { (Class) -> void } -> Session
|
|
37
|
+
| (Symbol | Module, ?options) -> Session
|
|
36
38
|
|
|
37
39
|
private
|
|
38
40
|
|
|
39
41
|
def default_options: () -> Options
|
|
40
42
|
def branch: (options) -> Session
|
|
41
|
-
| (options) { (Session) ->
|
|
43
|
+
| (options) { (Session) -> void } -> Session
|
|
42
44
|
end
|
|
43
45
|
end
|
data/sig/connection/http1.rbs
CHANGED
|
@@ -3,13 +3,17 @@ module HTTPX
|
|
|
3
3
|
include Callbacks
|
|
4
4
|
include Loggable
|
|
5
5
|
|
|
6
|
+
UPCASED: Hash[String, String]
|
|
7
|
+
MAX_REQUESTS: Integer
|
|
8
|
+
CRLF: String
|
|
9
|
+
|
|
6
10
|
attr_reader pending: Array[Request]
|
|
7
11
|
attr_reader requests: Array[Request]
|
|
8
12
|
|
|
9
13
|
@options: Options
|
|
10
14
|
@max_concurrent_requests: Integer
|
|
11
15
|
@max_requests: Integer
|
|
12
|
-
@parser: HTTP1
|
|
16
|
+
@parser: Parser::HTTP1
|
|
13
17
|
@buffer: Buffer
|
|
14
18
|
|
|
15
19
|
def interests: () -> io_interests?
|
|
@@ -30,11 +34,13 @@ module HTTPX
|
|
|
30
34
|
|
|
31
35
|
def handle_error: (StandardError ex) -> void
|
|
32
36
|
|
|
37
|
+
def on_start: () -> void
|
|
38
|
+
|
|
33
39
|
def on_headers: (Hash[String, Array[String]] headers) -> void
|
|
34
40
|
|
|
35
41
|
def on_trailers: (Hash[String, Array[String]] headers) -> void
|
|
36
42
|
|
|
37
|
-
def on_data: (
|
|
43
|
+
def on_data: (String chunk) -> void
|
|
38
44
|
|
|
39
45
|
def on_complete: () -> void
|
|
40
46
|
|
|
@@ -42,7 +48,7 @@ module HTTPX
|
|
|
42
48
|
|
|
43
49
|
def ping: () -> void
|
|
44
50
|
|
|
45
|
-
def timeout: () ->
|
|
51
|
+
def timeout: () -> Numeric
|
|
46
52
|
|
|
47
53
|
private
|
|
48
54
|
|
|
@@ -54,7 +60,7 @@ module HTTPX
|
|
|
54
60
|
|
|
55
61
|
def disable_pipelining: () -> void
|
|
56
62
|
|
|
57
|
-
def set_protocol_headers: (Request) -> _Each[
|
|
63
|
+
def set_protocol_headers: (Request) -> _Each[[String, String]]
|
|
58
64
|
|
|
59
65
|
def headline_uri: (Request) -> String
|
|
60
66
|
|
|
@@ -64,7 +70,7 @@ module HTTPX
|
|
|
64
70
|
|
|
65
71
|
def join_trailers: (Request request) -> void
|
|
66
72
|
|
|
67
|
-
def join_headers2: (_Each[
|
|
73
|
+
def join_headers2: (_Each[[String, String]] headers) -> void
|
|
68
74
|
|
|
69
75
|
def join_body: (Request request) -> void
|
|
70
76
|
|