httpx 1.6.3 → 1.7.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_11_0.md +3 -3
  3. data/doc/release_notes/1_6_3.md +2 -2
  4. data/doc/release_notes/1_7_0.md +149 -0
  5. data/lib/httpx/adapters/datadog.rb +1 -1
  6. data/lib/httpx/adapters/faraday.rb +1 -1
  7. data/lib/httpx/altsvc.rb +3 -1
  8. data/lib/httpx/connection/http1.rb +5 -6
  9. data/lib/httpx/connection/http2.rb +2 -0
  10. data/lib/httpx/connection.rb +3 -8
  11. data/lib/httpx/domain_name.rb +1 -1
  12. data/lib/httpx/headers.rb +2 -2
  13. data/lib/httpx/loggable.rb +2 -0
  14. data/lib/httpx/options.rb +40 -17
  15. data/lib/httpx/plugins/auth/digest.rb +44 -4
  16. data/lib/httpx/plugins/auth.rb +87 -4
  17. data/lib/httpx/plugins/aws_sdk_authentication.rb +0 -1
  18. data/lib/httpx/plugins/cookies/cookie.rb +1 -0
  19. data/lib/httpx/plugins/digest_auth.rb +4 -5
  20. data/lib/httpx/plugins/fiber_concurrency.rb +16 -1
  21. data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -1
  22. data/lib/httpx/plugins/grpc.rb +2 -2
  23. data/lib/httpx/plugins/internal_telemetry.rb +1 -1
  24. data/lib/httpx/plugins/ntlm_auth.rb +5 -3
  25. data/lib/httpx/plugins/oauth.rb +162 -56
  26. data/lib/httpx/plugins/rate_limiter.rb +2 -2
  27. data/lib/httpx/plugins/response_cache.rb +3 -7
  28. data/lib/httpx/plugins/retries.rb +55 -16
  29. data/lib/httpx/plugins/ssrf_filter.rb +1 -1
  30. data/lib/httpx/plugins/stream.rb +59 -8
  31. data/lib/httpx/plugins/stream_bidi.rb +73 -17
  32. data/lib/httpx/pool.rb +12 -2
  33. data/lib/httpx/request.rb +10 -1
  34. data/lib/httpx/resolver/https.rb +67 -17
  35. data/lib/httpx/resolver/multi.rb +4 -0
  36. data/lib/httpx/resolver/native.rb +26 -4
  37. data/lib/httpx/resolver/resolver.rb +2 -2
  38. data/lib/httpx/resolver.rb +97 -29
  39. data/lib/httpx/response/body.rb +2 -0
  40. data/lib/httpx/response.rb +22 -6
  41. data/lib/httpx/selector.rb +9 -0
  42. data/lib/httpx/session.rb +6 -6
  43. data/lib/httpx/transcoder/body.rb +1 -1
  44. data/lib/httpx/transcoder/json.rb +1 -1
  45. data/lib/httpx/transcoder/multipart/decoder.rb +4 -4
  46. data/lib/httpx/transcoder/multipart/encoder.rb +1 -1
  47. data/lib/httpx/transcoder/multipart.rb +16 -8
  48. data/lib/httpx/transcoder.rb +4 -6
  49. data/lib/httpx/version.rb +1 -1
  50. data/sig/altsvc.rbs +3 -0
  51. data/sig/chainable.rbs +3 -3
  52. data/sig/connection.rbs +1 -3
  53. data/sig/options.rbs +1 -1
  54. data/sig/plugins/auth/digest.rbs +6 -0
  55. data/sig/plugins/auth.rbs +28 -4
  56. data/sig/plugins/basic_auth.rbs +3 -3
  57. data/sig/plugins/digest_auth.rbs +2 -4
  58. data/sig/plugins/fiber_concurrency.rbs +6 -0
  59. data/sig/plugins/ntlm_auth.rbs +2 -2
  60. data/sig/plugins/oauth.rbs +46 -15
  61. data/sig/plugins/rate_limiter.rbs +1 -1
  62. data/sig/plugins/response_cache/file_store.rbs +2 -0
  63. data/sig/plugins/response_cache.rbs +4 -0
  64. data/sig/plugins/retries.rbs +8 -2
  65. data/sig/plugins/stream.rbs +13 -3
  66. data/sig/plugins/stream_bidi.rbs +2 -2
  67. data/sig/pool.rbs +1 -1
  68. data/sig/resolver/https.rbs +5 -0
  69. data/sig/resolver/multi.rbs +2 -0
  70. data/sig/resolver/native.rbs +2 -0
  71. data/sig/resolver.rbs +12 -3
  72. data/sig/response.rbs +3 -0
  73. data/sig/session.rbs +3 -5
  74. data/sig/transcoder/multipart.rbs +4 -2
  75. data/sig/transcoder.rbs +5 -1
  76. metadata +3 -1
@@ -5,7 +5,10 @@ require "resolv"
5
5
 
6
6
  module HTTPX
7
7
  module Resolver
8
+ extend self
9
+
8
10
  RESOLVE_TIMEOUT = [2, 3].freeze
11
+ MAX_CACHE_SIZE = 512
9
12
 
10
13
  require "httpx/resolver/entry"
11
14
  require "httpx/resolver/resolver"
@@ -15,26 +18,19 @@ module HTTPX
15
18
  require "httpx/resolver/multi"
16
19
 
17
20
  @lookup_mutex = Thread::Mutex.new
21
+ @hostnames = []
18
22
  @lookups = Hash.new { |h, k| h[k] = [] }
19
23
 
20
24
  @identifier_mutex = Thread::Mutex.new
21
25
  @identifier = 1
22
26
  @hosts_resolver = Resolv::Hosts.new
23
27
 
24
- module_function
25
-
26
28
  def supported_ip_families
27
- @supported_ip_families ||= begin
28
- # https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
29
- list = Socket.ip_address_list
30
- if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
31
- [Socket::AF_INET6, Socket::AF_INET]
32
- else
33
- [Socket::AF_INET]
34
- end
35
- rescue NotImplementedError
36
- [Socket::AF_INET]
37
- end.freeze
29
+ if in_ractor?
30
+ Ractor.store_if_absent(:httpx_supported_ip_families) { find_supported_ip_families }
31
+ else
32
+ @supported_ip_families ||= find_supported_ip_families
33
+ end
38
34
  end
39
35
 
40
36
  def resolver_for(resolver_type, options)
@@ -63,7 +59,12 @@ module HTTPX
63
59
  # matches +hostname+ to entries in the hosts file, returns <tt>nil</nil> if none is
64
60
  # found, or there is no hosts file.
65
61
  def hosts_resolve(hostname)
66
- ips = @hosts_resolver.getaddresses(hostname)
62
+ ips = if in_ractor?
63
+ Ractor.store_if_absent(:httpx_hosts_resolver) { Resolv::Hosts.new }
64
+ else
65
+ @hosts_resolver
66
+ end.getaddresses(hostname)
67
+
67
68
  return if ips.empty?
68
69
 
69
70
  ips.map { |ip| Entry.new(ip) }
@@ -72,13 +73,20 @@ module HTTPX
72
73
 
73
74
  def cached_lookup(hostname)
74
75
  now = Utils.now
75
- lookup_synchronize do |lookups|
76
- lookup(hostname, lookups, now)
76
+ lookup_synchronize do |lookups, hostnames|
77
+ lookup(hostname, lookups, hostnames, now)
77
78
  end
78
79
  end
79
80
 
80
81
  def cached_lookup_set(hostname, family, entries)
81
- lookup_synchronize do |lookups|
82
+ lookup_synchronize do |lookups, hostnames|
83
+ # lru cleanup
84
+ while lookups.size >= MAX_CACHE_SIZE
85
+ hs = hostnames.shift
86
+ lookups.delete(hs)
87
+ end
88
+ hostnames << hostname
89
+
82
90
  case family
83
91
  when Socket::AF_INET6
84
92
  lookups[hostname].concat(entries)
@@ -86,13 +94,14 @@ module HTTPX
86
94
  lookups[hostname].unshift(*entries)
87
95
  end
88
96
  entries.each do |entry|
89
- next unless entry["name"] != hostname
97
+ name = entry["name"]
98
+ next unless name != hostname
90
99
 
91
100
  case family
92
101
  when Socket::AF_INET6
93
- lookups[entry["name"]] << entry
102
+ lookups[name] << entry
94
103
  when Socket::AF_INET
95
- lookups[entry["name"]].unshift(entry)
104
+ lookups[name].unshift(entry)
96
105
  end
97
106
  end
98
107
  end
@@ -101,26 +110,38 @@ module HTTPX
101
110
  def cached_lookup_evict(hostname, ip)
102
111
  ip = ip.to_s
103
112
 
104
- lookup_synchronize do |lookups|
113
+ lookup_synchronize do |lookups, hostnames|
105
114
  entries = lookups[hostname]
106
115
 
107
116
  return unless entries
108
117
 
109
- lookups.delete_if { |entry| entry["data"] == ip }
118
+ entries.delete_if { |entry| entry["data"] == ip }
119
+
120
+ if entries.empty?
121
+ lookups.delete(hostname)
122
+ hostnames.delete(hostname)
123
+ end
110
124
  end
111
125
  end
112
126
 
113
127
  # do not use directly!
114
- def lookup(hostname, lookups, ttl)
128
+ def lookup(hostname, lookups, hostnames, ttl)
115
129
  return unless lookups.key?(hostname)
116
130
 
117
- entries = lookups[hostname] = lookups[hostname].select do |address|
118
- address["TTL"] > ttl
131
+ entries = lookups[hostname]
132
+
133
+ entries.delete_if do |address|
134
+ address["TTL"] < ttl
135
+ end
136
+
137
+ if entries.empty?
138
+ lookups.delete(hostname)
139
+ hostnames.delete(hostname)
119
140
  end
120
141
 
121
142
  ips = entries.flat_map do |address|
122
143
  if (als = address["alias"])
123
- lookup(als, lookups, ttl)
144
+ lookup(als, lookups, hostnames, ttl)
124
145
  else
125
146
  Entry.new(address["data"], address["TTL"])
126
147
  end
@@ -130,7 +151,12 @@ module HTTPX
130
151
  end
131
152
 
132
153
  def generate_id
133
- id_synchronize { @identifier = (@identifier + 1) & 0xFFFF }
154
+ if in_ractor?
155
+ identifier = Ractor.store_if_absent(:httpx_resolver_identifier) { -1 }
156
+ Ractor.current[:httpx_resolver_identifier] = (identifier + 1) & 0xFFFF
157
+ else
158
+ id_synchronize { @identifier = (@identifier + 1) & 0xFFFF }
159
+ end
134
160
  end
135
161
 
136
162
  def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id)
@@ -152,7 +178,14 @@ module HTTPX
152
178
 
153
179
  return :message_truncated if message.tc == 1
154
180
 
155
- return :dns_error, message.rcode if message.rcode != Resolv::DNS::RCode::NoError
181
+ if message.rcode != Resolv::DNS::RCode::NoError
182
+ case message.rcode
183
+ when Resolv::DNS::RCode::ServFail
184
+ return :retriable_error, message.rcode
185
+ else
186
+ return :dns_error, message.rcode
187
+ end
188
+ end
156
189
 
157
190
  addresses = []
158
191
 
@@ -178,12 +211,47 @@ module HTTPX
178
211
  [:ok, addresses]
179
212
  end
180
213
 
214
+ private
215
+
181
216
  def lookup_synchronize
182
- @lookup_mutex.synchronize { yield(@lookups) }
217
+ if in_ractor?
218
+ lookups = Ractor.store_if_absent(:httpx_resolver_lookups) { Hash.new { |h, k| h[k] = [] } }
219
+ hostnames = Ractor.store_if_absent(:httpx_resolver_hostnames) { [] }
220
+ return yield(lookups, hostnames)
221
+ end
222
+
223
+ @lookup_mutex.synchronize { yield(@lookups, @hostnames) }
183
224
  end
184
225
 
185
226
  def id_synchronize(&block)
186
227
  @identifier_mutex.synchronize(&block)
187
228
  end
229
+
230
+ def find_supported_ip_families
231
+ list = Socket.ip_address_list
232
+
233
+ begin
234
+ if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
235
+ [Socket::AF_INET6, Socket::AF_INET]
236
+ else
237
+ [Socket::AF_INET]
238
+ end
239
+ rescue NotImplementedError
240
+ [Socket::AF_INET]
241
+ end.freeze
242
+ end
243
+
244
+ if defined?(Ractor) &&
245
+ # no ractor support for 3.0
246
+ RUBY_VERSION >= "3.1.0"
247
+
248
+ def in_ractor?
249
+ Ractor.main != Ractor.current
250
+ end
251
+ else
252
+ def in_ractor?
253
+ false
254
+ end
255
+ end
188
256
  end
189
257
  end
@@ -138,6 +138,8 @@ module HTTPX
138
138
  else
139
139
  IO.copy_stream(@buffer, dest)
140
140
  end
141
+ ensure
142
+ close
141
143
  end
142
144
 
143
145
  # closes/cleans the buffer, resets everything
@@ -211,16 +211,19 @@ module HTTPX
211
211
 
212
212
  def initialize(header_value)
213
213
  @header_value = header_value
214
+ @mime_type = @charset = nil
215
+ @initialized = false
214
216
  end
215
217
 
216
218
  # returns the mime type declared in the header.
217
219
  #
218
220
  # ContentType.new("application/json; charset=utf-8").mime_type #=> "application/json"
219
221
  def mime_type
220
- return @mime_type if defined?(@mime_type)
222
+ return @mime_type if @initialized
221
223
 
222
- m = @header_value.to_s[MIME_TYPE_RE, 1]
223
- m && @mime_type = m.strip.downcase
224
+ load
225
+
226
+ @mime_type
224
227
  end
225
228
 
226
229
  # returns the charset declared in the header.
@@ -228,10 +231,23 @@ module HTTPX
228
231
  # ContentType.new("application/json; charset=utf-8").charset #=> "utf-8"
229
232
  # ContentType.new("text/plain").charset #=> nil
230
233
  def charset
231
- return @charset if defined?(@charset)
234
+ return @charset if @initialized
235
+
236
+ load
237
+
238
+ @charset
239
+ end
240
+
241
+ private
242
+
243
+ def load
244
+ m = @header_value.to_s[MIME_TYPE_RE, 1]
245
+ m && @mime_type = m.strip.downcase
246
+
247
+ c = @header_value.to_s[CHARSET_RE, 1]
248
+ c && @charset = c.strip.delete('"')
232
249
 
233
- m = @header_value.to_s[CHARSET_RE, 1]
234
- m && @charset = m.strip.delete('"')
250
+ @initialized = true
235
251
  end
236
252
  end
237
253
 
@@ -199,6 +199,12 @@ module HTTPX
199
199
  def select_many(r, w, interval, &block)
200
200
  begin
201
201
  readers, writers = ::IO.select(r, w, nil, interval)
202
+ rescue IOError => e
203
+ (Array(r) + Array(w)).each do |sel|
204
+ # TODO: is there a way to cheaply find the IO associated with the error?
205
+ sel.on_error(e)
206
+ sel.force_close(true)
207
+ end
202
208
  rescue StandardError => e
203
209
  (Array(r) + Array(w)).each do |sel|
204
210
  sel.on_error(e)
@@ -240,6 +246,9 @@ module HTTPX
240
246
  when :w then io.to_io.wait_writable(interval)
241
247
  when :rw then rw_wait(io, interval)
242
248
  end
249
+ rescue IOError => e
250
+ io.on_error(e)
251
+ io.force_close(true)
243
252
  rescue StandardError => e
244
253
  io.on_error(e)
245
254
 
data/lib/httpx/session.rb CHANGED
@@ -177,6 +177,7 @@ module HTTPX
177
177
 
178
178
  # returns the HTTPX::Connection through which the +request+ should be sent through.
179
179
  def find_connection(request_uri, selector, options)
180
+ log(level: 2) { "finding connection for ##{request_uri}..." }
180
181
  if (connection = selector.find_connection(request_uri, options))
181
182
  connection.idling if connection.state == :closed
182
183
  connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
@@ -224,7 +225,7 @@ module HTTPX
224
225
 
225
226
  # tries deactivating connections in the +selector+, deregistering the ones that have been deactivated.
226
227
  def deactivate(selector)
227
- selector.each_connection.each(&:deactivate)
228
+ selector.each_connection.to_a.each(&:deactivate)
228
229
  end
229
230
 
230
231
  # callback executed when an HTTP/2 promise frame has been received.
@@ -415,12 +416,12 @@ module HTTPX
415
416
 
416
417
  def find_resolver_for(connection, selector)
417
418
  if (resolver = selector.find_resolver(connection.options))
418
- resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
419
+ resolver.log(level: 2) { "found resolver##{resolver.object_id}(#{resolver.state}) in selector##{selector.object_id}" }
419
420
  return resolver
420
421
  end
421
422
 
422
423
  resolver = @pool.checkout_resolver(connection.options)
423
- resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
424
+ resolver.log(level: 2) { "found resolver##{resolver.object_id}(#{resolver.state}) in pool##{@pool.object_id}" }
424
425
  pin(resolver, selector)
425
426
 
426
427
  resolver
@@ -436,14 +437,13 @@ module HTTPX
436
437
  conn1.log(level: 2) { "check-in connection##{conn1.object_id}(#{conn1.state}) in pool##{@pool.object_id}" }
437
438
  @pool.checkin_connection(conn1)
438
439
  end
439
- return false
440
+ return
440
441
  end
441
442
 
442
443
  conn2.log(level: 2) { "coalescing with connection##{conn1.object_id}[#{conn1.origin}])" }
443
444
  select_connection(conn1, selector) if from_pool
444
445
  conn2.coalesce!(conn1)
445
446
  conn2.disconnect
446
- true
447
447
  end
448
448
 
449
449
  def get_current_selector
@@ -472,6 +472,7 @@ module HTTPX
472
472
  th.thread_variable_get(:httpx_persistent_selector_store)
473
473
  end
474
474
 
475
+ Options.freeze
475
476
  @default_options = Options.new
476
477
  @default_options.freeze
477
478
  @plugins = []
@@ -493,7 +494,6 @@ module HTTPX
493
494
  #
494
495
  def plugin(pl, options = nil, &block)
495
496
  label = pl
496
- # raise Error, "Cannot add a plugin to a frozen config" if frozen?
497
497
  pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
498
498
  raise ArgumentError, "Invalid plugin type: #{pl.class.inspect}" unless pl.is_a?(Module)
499
499
 
@@ -12,7 +12,7 @@ module HTTPX::Transcoder
12
12
  def initialize(body)
13
13
  body = body.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && body.is_a?(Pathname)
14
14
  @body = body
15
- super(body)
15
+ super
16
16
  end
17
17
 
18
18
  def bytesize
@@ -64,7 +64,7 @@ module HTTPX::Transcoder
64
64
  else
65
65
  require "json"
66
66
  def json_load(*args); ::JSON.parse(*args); end
67
- def json_dump(*args); ::JSON.dump(*args); end
67
+ def json_dump(*args); ::JSON.generate(*args); end
68
68
  end
69
69
  # rubocop:enable Style/SingleLineMethods
70
70
  end
@@ -60,7 +60,7 @@ module HTTPX
60
60
  when :idle
61
61
  raise Error, "payload does not start with boundary" unless @buffer.start_with?("#{@intermediate_boundary}#{CRLF}")
62
62
 
63
- @buffer = @buffer.byteslice(@intermediate_boundary.bytesize + 2..-1)
63
+ @buffer = @buffer.byteslice((@intermediate_boundary.bytesize + 2)..-1)
64
64
 
65
65
  @state = :part_header
66
66
  when :part_header
@@ -70,7 +70,7 @@ module HTTPX
70
70
  return unless idx
71
71
 
72
72
  # @type var head: String
73
- head = @buffer.byteslice(0..idx + 4 - 1)
73
+ head = @buffer.byteslice(0..(idx + 4 - 1))
74
74
 
75
75
  @buffer = @buffer.byteslice(head.bytesize..-1)
76
76
 
@@ -107,8 +107,8 @@ module HTTPX
107
107
  idx = @buffer.index(body_separator)
108
108
 
109
109
  if idx
110
- payload = @buffer.byteslice(0..idx - 1)
111
- @buffer = @buffer.byteslice(idx + body_separator.bytesize..-1)
110
+ payload = @buffer.byteslice(0..(idx - 1))
111
+ @buffer = @buffer.byteslice((idx + body_separator.bytesize)..-1)
112
112
  part << payload
113
113
  part.rewind if part.respond_to?(:rewind)
114
114
  @state = :parse_boundary
@@ -53,7 +53,7 @@ module HTTPX
53
53
 
54
54
  def to_parts(form)
55
55
  params = form.each_with_object([]) do |(key, val), aux|
56
- Transcoder.normalize_keys(key, val, MULTIPART_VALUE_COND) do |k, v|
56
+ Transcoder::Multipart.normalize_keys(key, val) do |k, v|
57
57
  next if v.nil?
58
58
 
59
59
  value, content_type, filename = Part.call(v)
@@ -7,20 +7,28 @@ require_relative "multipart/mime_type_detector"
7
7
 
8
8
  module HTTPX::Transcoder
9
9
  module Multipart
10
- MULTIPART_VALUE_COND = lambda do |value|
10
+ module_function
11
+
12
+ def multipart?(form_data)
13
+ form_data.any? do |_, v|
14
+ multipart_value?(v) ||
15
+ (v.respond_to?(:to_ary) && v.to_ary.any? { |av| multipart_value?(av) }) ||
16
+ (v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| multipart_value?(e) })
17
+ end
18
+ end
19
+
20
+ def multipart_value?(value)
11
21
  value.respond_to?(:read) ||
12
22
  (value.respond_to?(:to_hash) &&
13
23
  value.key?(:body) &&
14
24
  (value.key?(:filename) || value.key?(:content_type)))
15
25
  end
16
26
 
17
- module_function
18
-
19
- def multipart?(form_data)
20
- form_data.any? do |_, v|
21
- Multipart::MULTIPART_VALUE_COND.call(v) ||
22
- (v.respond_to?(:to_ary) && v.to_ary.any?(&Multipart::MULTIPART_VALUE_COND)) ||
23
- (v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| Multipart::MULTIPART_VALUE_COND.call(e) })
27
+ def normalize_keys(key, value, transcoder = self, &block)
28
+ if multipart_value?(value)
29
+ block.call(key.to_s, value)
30
+ else
31
+ HTTPX::Transcoder.normalize_keys(key, value, transcoder, &block)
24
32
  end
25
33
  end
26
34
 
@@ -4,20 +4,18 @@ module HTTPX
4
4
  module Transcoder
5
5
  module_function
6
6
 
7
- def normalize_keys(key, value, cond = nil, &block)
8
- if cond && cond.call(value)
9
- block.call(key.to_s, value)
10
- elsif value.respond_to?(:to_ary)
7
+ def normalize_keys(key, value, transcoder = self, &block)
8
+ if value.respond_to?(:to_ary)
11
9
  if value.empty?
12
10
  block.call("#{key}[]")
13
11
  else
14
12
  value.to_ary.each do |element|
15
- normalize_keys("#{key}[]", element, cond, &block)
13
+ transcoder.normalize_keys("#{key}[]", element, transcoder, &block)
16
14
  end
17
15
  end
18
16
  elsif value.respond_to?(:to_hash)
19
17
  value.to_hash.each do |child_key, child_value|
20
- normalize_keys("#{key}[#{child_key}]", child_value, cond, &block)
18
+ transcoder.normalize_keys("#{key}[#{child_key}]", child_value, transcoder, &block)
21
19
  end
22
20
  else
23
21
  block.call(key.to_s, value)
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "1.6.3"
4
+ VERSION = "1.7.0"
5
5
  end
data/sig/altsvc.rbs CHANGED
@@ -1,6 +1,7 @@
1
1
  module HTTPX
2
2
  module AltSvc
3
3
  module ConnectionMixin
4
+ H2_ALTSVC_SCHEMES: Array[String]
4
5
 
5
6
  def send: (Request request) -> void
6
7
 
@@ -11,6 +12,8 @@ module HTTPX
11
12
  def match_altsvcs?: (URI::Generic uri) -> bool
12
13
 
13
14
  def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
15
+
16
+ def altsvc_match?: (uri uri, uri other_uri) -> bool
14
17
  end
15
18
 
16
19
  type altsvc_params = Hash[String, untyped]
data/sig/chainable.rbs CHANGED
@@ -1,7 +1,7 @@
1
1
  module HTTPX
2
2
  module Chainable
3
- def request: (*Request, **untyped) -> Array[response]
4
- | (Request, **untyped) -> response
3
+ def request: (Request, **untyped) -> response
4
+ | (*Request, **untyped) -> Array[response]
5
5
  | (verb, uri | [uri], **untyped) -> response
6
6
  | (Array[[verb, uri] | [verb, uri, request_params]], **untyped) -> Array[response]
7
7
  | (verb, _Each[uri | [uri, request_params]], **untyped) -> Array[response]
@@ -12,7 +12,7 @@ module HTTPX
12
12
  def with: (options) -> Session
13
13
  | (options) { (Session) -> void } -> void
14
14
 
15
- def plugin: (:auth, ?options) -> Plugins::sessionAuthorization
15
+ def plugin: (:auth, ?options) -> Plugins::sessionAuth
16
16
  | (:basic_auth, ?options) -> Plugins::sessionBasicAuth
17
17
  | (:digest_auth, ?options) -> Plugins::sessionDigestAuth
18
18
  | (:ntlm_auth, ?options) -> Plugins::sessionNTLMAuth
data/sig/connection.rbs CHANGED
@@ -61,7 +61,7 @@ module HTTPX
61
61
 
62
62
  def addresses?: () -> boolish
63
63
 
64
- def match?: (URI::Generic uri, Options options) -> bool
64
+ def match?: (http_uri uri, Options options) -> bool
65
65
 
66
66
  def mergeable?: (Connection connection) -> bool
67
67
 
@@ -71,8 +71,6 @@ module HTTPX
71
71
 
72
72
  def coalescable?: (Connection connection) -> bool
73
73
 
74
- def create_idle: (?Hash[Symbol, untyped] options) -> instance
75
-
76
74
  def merge: (Connection connection) -> void
77
75
 
78
76
  def purge_pending: () { (Request request) -> void } -> void
data/sig/options.rbs CHANGED
@@ -138,7 +138,7 @@ module HTTPX
138
138
 
139
139
  def options_equals?: (Options other, ?Array[Symbol] ignore_ivars) -> bool
140
140
 
141
- def merge: (_ToHash[Symbol, untyped] other) -> instance
141
+ def merge: (Object & _ToHash[Symbol, untyped] other) -> (instance | self)
142
142
 
143
143
  def to_hash: () -> Hash[Symbol, untyped]
144
144
 
@@ -2,9 +2,13 @@ module HTTPX
2
2
  module Plugins
3
3
  module Authentication
4
4
  class Digest
5
+ Error: singleton(HTTPX::Error)
6
+
7
+
5
8
  @user: String
6
9
  @password: String
7
10
  @hashed: bool
11
+ @nonce: Integer
8
12
 
9
13
  def can_authenticate?: (String? authenticate) -> boolish
10
14
 
@@ -19,6 +23,8 @@ module HTTPX
19
23
  def make_cnonce: () -> String
20
24
 
21
25
  def next_nonce: () -> Integer
26
+
27
+ def raise_format_error: () -> void
22
28
  end
23
29
  end
24
30
  end
data/sig/plugins/auth.rbs CHANGED
@@ -1,13 +1,37 @@
1
1
  module HTTPX
2
2
  module Plugins
3
- module Authorization
3
+ module Auth
4
+ interface _AuthOptions
5
+ def auth_header_value: () -> (String | ^(Request request) -> string)?
6
+
7
+ def auth_header_type: () -> String?
8
+ end
9
+
4
10
  module InstanceMethods
5
- def authorization: (string token) -> instance
11
+ @auth_header_value: String
12
+ @skip_auth_header_value: bool
13
+
14
+ def authorization: (?string token, ?auth_header_type: string) ?{ (Request) -> string } -> instance
15
+
16
+ def bearer_auth: (?string token) ?{ (Request) -> string } -> instance
17
+
18
+ def skip_auth_header: [T] { () -> T } -> T
19
+
20
+ def reset_auth_header_value!: () -> void
21
+
22
+ private
23
+
24
+ def generate_auth_token: () -> String?
25
+ end
26
+
27
+ module RequestMethods
28
+ def authorize: (String auth_value) -> void
29
+ end
6
30
 
7
- def bearer_auth: (string token) -> instance
31
+ module AuthRetries
8
32
  end
9
33
  end
10
34
 
11
- type sessionAuthorization = Session & Authorization::InstanceMethods
35
+ type sessionAuth = Session & Auth::InstanceMethods
12
36
  end
13
37
  end
@@ -1,15 +1,15 @@
1
1
  module HTTPX
2
2
  module Plugins
3
3
  module BasicAuth
4
- def self.load_dependencies: (singleton(Session)) -> void
4
+ def self.load_dependencies: (singleton(Session) klass) -> void
5
5
 
6
- def self.configure: (singleton(Session)) -> void
6
+ def self.configure: (singleton(Session) klass) -> void
7
7
 
8
8
  module InstanceMethods
9
9
  def basic_auth: (string user, string password) -> instance
10
10
  end
11
11
  end
12
12
 
13
- type sessionBasicAuth = sessionAuthorization & BasicAuth::InstanceMethods
13
+ type sessionBasicAuth = sessionAuth & BasicAuth::InstanceMethods
14
14
  end
15
15
  end
@@ -1,21 +1,19 @@
1
1
  module HTTPX
2
2
  module Plugins
3
3
  module DigestAuth
4
- DigestError: singleton(Error)
5
-
6
4
  interface _DigestOptions
7
5
  def digest: () -> Authentication::Digest?
8
6
  end
9
7
 
10
8
  def self.extra_options: (Options) -> (Options & _DigestOptions)
11
9
 
12
- def self.load_dependencies: (*untyped) -> void
10
+ def self.load_dependencies: (singleton(Session) klass) -> void
13
11
 
14
12
  module InstanceMethods
15
13
  def digest_auth: (string user, string password, ?hashed: bool) -> instance
16
14
  end
17
15
  end
18
16
 
19
- type sessionDigestAuth = sessionAuthorization & DigestAuth::InstanceMethods
17
+ type sessionDigestAuth = sessionAuth & DigestAuth::InstanceMethods
20
18
  end
21
19
  end