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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_16_0.md +93 -0
  3. data/doc/release_notes/0_16_1.md +5 -0
  4. data/doc/release_notes/0_17_0.md +49 -0
  5. data/doc/release_notes/0_18_0.md +69 -0
  6. data/lib/httpx/adapters/datadog.rb +1 -1
  7. data/lib/httpx/adapters/faraday.rb +8 -14
  8. data/lib/httpx/adapters/webmock.rb +9 -3
  9. data/lib/httpx/altsvc.rb +2 -2
  10. data/lib/httpx/buffer.rb +1 -1
  11. data/lib/httpx/callbacks.rb +1 -1
  12. data/lib/httpx/chainable.rb +18 -11
  13. data/lib/httpx/connection/http1.rb +21 -13
  14. data/lib/httpx/connection/http2.rb +20 -25
  15. data/lib/httpx/connection.rb +73 -77
  16. data/lib/httpx/domain_name.rb +1 -1
  17. data/lib/httpx/errors.rb +11 -11
  18. data/lib/httpx/extensions.rb +50 -4
  19. data/lib/httpx/headers.rb +1 -1
  20. data/lib/httpx/io/ssl.rb +3 -3
  21. data/lib/httpx/io/tls.rb +8 -8
  22. data/lib/httpx/loggable.rb +5 -5
  23. data/lib/httpx/options.rb +108 -81
  24. data/lib/httpx/parser/http1.rb +11 -7
  25. data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
  26. data/lib/httpx/plugins/aws_sigv4.rb +19 -20
  27. data/lib/httpx/plugins/compression.rb +17 -14
  28. data/lib/httpx/plugins/cookies/cookie.rb +4 -2
  29. data/lib/httpx/plugins/cookies/jar.rb +21 -2
  30. data/lib/httpx/plugins/cookies.rb +20 -7
  31. data/lib/httpx/plugins/digest_authentication.rb +19 -15
  32. data/lib/httpx/plugins/expect.rb +26 -18
  33. data/lib/httpx/plugins/follow_redirects.rb +9 -9
  34. data/lib/httpx/plugins/grpc/call.rb +4 -1
  35. data/lib/httpx/plugins/grpc/message.rb +2 -2
  36. data/lib/httpx/plugins/grpc.rb +72 -46
  37. data/lib/httpx/plugins/h2c.rb +7 -3
  38. data/lib/httpx/plugins/internal_telemetry.rb +8 -8
  39. data/lib/httpx/plugins/multipart/decoder.rb +187 -0
  40. data/lib/httpx/plugins/multipart/mime_type_detector.rb +3 -3
  41. data/lib/httpx/plugins/multipart/part.rb +2 -2
  42. data/lib/httpx/plugins/multipart.rb +16 -2
  43. data/lib/httpx/plugins/ntlm_authentication.rb +12 -10
  44. data/lib/httpx/plugins/proxy/socks4.rb +2 -1
  45. data/lib/httpx/plugins/proxy/socks5.rb +2 -1
  46. data/lib/httpx/plugins/proxy/ssh.rb +20 -13
  47. data/lib/httpx/plugins/proxy.rb +10 -10
  48. data/lib/httpx/plugins/response_cache/store.rb +55 -0
  49. data/lib/httpx/plugins/response_cache.rb +88 -0
  50. data/lib/httpx/plugins/retries.rb +46 -23
  51. data/lib/httpx/plugins/stream.rb +3 -4
  52. data/lib/httpx/plugins/upgrade.rb +7 -6
  53. data/lib/httpx/pool.rb +39 -13
  54. data/lib/httpx/registry.rb +2 -2
  55. data/lib/httpx/request.rb +16 -25
  56. data/lib/httpx/resolver/https.rb +4 -8
  57. data/lib/httpx/resolver/native.rb +19 -5
  58. data/lib/httpx/resolver/resolver_mixin.rb +2 -1
  59. data/lib/httpx/resolver/system.rb +2 -0
  60. data/lib/httpx/resolver.rb +2 -2
  61. data/lib/httpx/response.rb +91 -48
  62. data/lib/httpx/selector.rb +11 -24
  63. data/lib/httpx/session.rb +41 -23
  64. data/lib/httpx/session2.rb +23 -0
  65. data/lib/httpx/timers.rb +84 -0
  66. data/lib/httpx/transcoder/body.rb +3 -2
  67. data/lib/httpx/transcoder/chunker.rb +2 -1
  68. data/lib/httpx/transcoder/form.rb +20 -0
  69. data/lib/httpx/transcoder/json.rb +12 -0
  70. data/lib/httpx/transcoder.rb +62 -1
  71. data/lib/httpx/utils.rb +10 -2
  72. data/lib/httpx/version.rb +1 -1
  73. data/lib/httpx.rb +7 -3
  74. data/sig/buffer.rbs +3 -1
  75. data/sig/chainable.rbs +31 -29
  76. data/sig/connection/http1.rbs +11 -5
  77. data/sig/connection/http2.rbs +16 -5
  78. data/sig/connection.rbs +31 -13
  79. data/sig/errors.rbs +35 -1
  80. data/sig/headers.rbs +20 -19
  81. data/sig/httpx.rbs +4 -1
  82. data/sig/loggable.rbs +3 -1
  83. data/sig/options.rbs +45 -34
  84. data/sig/parser/http1.rbs +3 -3
  85. data/sig/plugins/authentication.rbs +1 -1
  86. data/sig/plugins/aws_sdk_authentication.rbs +25 -3
  87. data/sig/plugins/aws_sigv4.rbs +13 -5
  88. data/sig/plugins/basic_authentication.rbs +1 -1
  89. data/sig/plugins/compression.rbs +4 -6
  90. data/sig/plugins/cookies/cookie.rbs +5 -7
  91. data/sig/plugins/cookies/jar.rbs +9 -10
  92. data/sig/plugins/cookies.rbs +4 -5
  93. data/sig/plugins/digest_authentication.rbs +2 -3
  94. data/sig/plugins/expect.rbs +2 -4
  95. data/sig/plugins/follow_redirects.rbs +3 -5
  96. data/sig/plugins/grpc.rbs +4 -7
  97. data/sig/plugins/h2c.rbs +0 -2
  98. data/sig/plugins/multipart.rbs +64 -10
  99. data/sig/plugins/ntlm_authentication.rbs +2 -3
  100. data/sig/plugins/persistent.rbs +3 -8
  101. data/sig/plugins/proxy/ssh.rbs +4 -4
  102. data/sig/plugins/proxy.rbs +13 -13
  103. data/sig/plugins/push_promise.rbs +0 -2
  104. data/sig/plugins/response_cache.rbs +35 -0
  105. data/sig/plugins/retries.rbs +7 -8
  106. data/sig/plugins/stream.rbs +1 -1
  107. data/sig/plugins/upgrade.rbs +2 -3
  108. data/sig/pool.rbs +7 -2
  109. data/sig/registry.rbs +1 -1
  110. data/sig/request.rbs +11 -8
  111. data/sig/resolver/native.rbs +10 -5
  112. data/sig/resolver/resolver_mixin.rbs +4 -5
  113. data/sig/resolver/system.rbs +4 -0
  114. data/sig/resolver.rbs +7 -0
  115. data/sig/response.rbs +26 -13
  116. data/sig/selector.rbs +11 -9
  117. data/sig/session.rbs +22 -23
  118. data/sig/timers.rbs +32 -0
  119. data/sig/transcoder/body.rbs +6 -1
  120. data/sig/transcoder/chunker.rbs +8 -2
  121. data/sig/transcoder/form.rbs +3 -1
  122. data/sig/transcoder/json.rbs +2 -0
  123. data/sig/transcoder.rbs +13 -5
  124. data/sig/utils.rbs +6 -0
  125. metadata +18 -18
  126. 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 block_given?
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, options)
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
- request = rklass.new(verb, uri, @options.merge(options).merge(persistent: @persistent))
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, options)
178
- request_options = @options.merge(options)
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, options)
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, options)
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
- pool.next_tick until (response = fetch_response(request, connections, options))
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
- close(connections) unless @persistent
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.dup
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, *args|
287
- plugin(pl, *args)
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
@@ -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 = Class.new(HTTPX::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.map(&:bytesize).reduce(0, :+)
25
+ @raw.sum(&:bytesize)
25
26
  elsif @raw.respond_to?(:size)
26
27
  @raw.size || Float::INFINITY
27
28
  elsif @raw.respond_to?(:length)
@@ -4,7 +4,8 @@ require "forwardable"
4
4
 
5
5
  module HTTPX::Transcoder
6
6
  module Chunker
7
- Error = Class.new(HTTPX::Error)
7
+ class Error < HTTPX::Error; end
8
+
8
9
  CRLF = "\r\n".b
9
10
 
10
11
  class Encoder
@@ -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
@@ -4,7 +4,11 @@ module HTTPX
4
4
  module Transcoder
5
5
  extend Registry
6
6
 
7
- def self.normalize_keys(key, value, cond = nil, &block)
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 Kernel.URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
39
+ return URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
32
40
 
33
- uri = Kernel.URI(URIParser.escape(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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.15.4"
4
+ VERSION = "0.18.0"
5
5
  end
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
- @plugins[name] = mod
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) -> void
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: (*untyped, **untyped) -> (response | Array[response])
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) -> Session} -> 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
- | (Symbol | Module, ?options?) { (Class) -> void } -> Session
35
- | (Symbol | Module, ?options?) -> Session
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) -> Session } -> Session
43
+ | (options) { (Session) -> void } -> Session
42
44
  end
43
45
  end
@@ -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: (string chunk) -> void
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: () -> Integer
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[headers_key, String]
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[headers_key, String] headers) -> void
73
+ def join_headers2: (_Each[[String, String]] headers) -> void
68
74
 
69
75
  def join_body: (Request request) -> void
70
76