httpx 0.22.4 → 0.23.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_22_5.md +6 -0
  3. data/doc/release_notes/0_23_0.md +42 -0
  4. data/lib/httpx/adapters/datadog.rb +12 -2
  5. data/lib/httpx/adapters/faraday.rb +1 -1
  6. data/lib/httpx/adapters/sentry.rb +15 -5
  7. data/lib/httpx/adapters/webmock.rb +2 -2
  8. data/lib/httpx/buffer.rb +4 -0
  9. data/lib/httpx/callbacks.rb +3 -3
  10. data/lib/httpx/chainable.rb +4 -4
  11. data/lib/httpx/connection/http1.rb +2 -2
  12. data/lib/httpx/connection/http2.rb +2 -3
  13. data/lib/httpx/connection.rb +31 -11
  14. data/lib/httpx/io/udp.rb +2 -0
  15. data/lib/httpx/io/unix.rb +1 -5
  16. data/lib/httpx/io.rb +0 -10
  17. data/lib/httpx/options.rb +16 -2
  18. data/lib/httpx/plugins/authentication/digest.rb +1 -1
  19. data/lib/httpx/plugins/aws_sdk_authentication.rb +1 -3
  20. data/lib/httpx/plugins/aws_sigv4.rb +1 -1
  21. data/lib/httpx/plugins/compression/brotli.rb +4 -4
  22. data/lib/httpx/plugins/compression/deflate.rb +12 -7
  23. data/lib/httpx/plugins/compression/gzip.rb +7 -5
  24. data/lib/httpx/plugins/compression.rb +9 -8
  25. data/lib/httpx/plugins/digest_authentication.rb +1 -4
  26. data/lib/httpx/plugins/follow_redirects.rb +1 -1
  27. data/lib/httpx/plugins/grpc/message.rb +3 -1
  28. data/lib/httpx/plugins/grpc.rb +3 -3
  29. data/lib/httpx/plugins/h2c.rb +5 -9
  30. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  31. data/lib/httpx/plugins/multipart.rb +14 -2
  32. data/lib/httpx/plugins/proxy/http.rb +4 -4
  33. data/lib/httpx/plugins/proxy.rb +65 -31
  34. data/lib/httpx/plugins/response_cache.rb +2 -2
  35. data/lib/httpx/plugins/retries.rb +49 -2
  36. data/lib/httpx/plugins/upgrade/h2.rb +3 -3
  37. data/lib/httpx/plugins/upgrade.rb +4 -7
  38. data/lib/httpx/plugins/webdav.rb +7 -7
  39. data/lib/httpx/pool.rb +7 -3
  40. data/lib/httpx/request.rb +23 -13
  41. data/lib/httpx/resolver/https.rb +37 -20
  42. data/lib/httpx/resolver/multi.rb +1 -6
  43. data/lib/httpx/resolver/native.rb +128 -36
  44. data/lib/httpx/resolver.rb +26 -14
  45. data/lib/httpx/response.rb +14 -16
  46. data/lib/httpx/session.rb +1 -0
  47. data/lib/httpx/transcoder/body.rb +0 -1
  48. data/lib/httpx/transcoder/chunker.rb +0 -1
  49. data/lib/httpx/transcoder/form.rb +0 -1
  50. data/lib/httpx/transcoder/json.rb +0 -1
  51. data/lib/httpx/transcoder/xml.rb +0 -1
  52. data/lib/httpx/transcoder.rb +0 -2
  53. data/lib/httpx/version.rb +1 -1
  54. data/lib/httpx.rb +0 -1
  55. data/sig/buffer.rbs +1 -0
  56. data/sig/chainable.rbs +3 -3
  57. data/sig/connection.rbs +17 -6
  58. data/sig/errors.rbs +9 -0
  59. data/sig/httpx.rbs +3 -3
  60. data/sig/io/ssl.rbs +17 -0
  61. data/sig/io/tcp.rbs +57 -0
  62. data/sig/io/udp.rbs +20 -0
  63. data/sig/io/unix.rbs +10 -0
  64. data/sig/options.rbs +5 -1
  65. data/sig/plugins/compression.rbs +6 -2
  66. data/sig/plugins/cookies/jar.rbs +2 -2
  67. data/sig/plugins/grpc.rbs +3 -3
  68. data/sig/plugins/h2c.rbs +1 -1
  69. data/sig/plugins/proxy.rbs +1 -5
  70. data/sig/plugins/response_cache.rbs +1 -1
  71. data/sig/plugins/retries.rbs +28 -8
  72. data/sig/plugins/upgrade.rbs +5 -3
  73. data/sig/request.rbs +6 -2
  74. data/sig/resolver/https.rbs +3 -1
  75. data/sig/resolver/native.rbs +7 -2
  76. data/sig/resolver/resolver.rbs +0 -2
  77. data/sig/resolver/system.rbs +2 -0
  78. data/sig/resolver.rbs +8 -4
  79. data/sig/response.rbs +6 -2
  80. data/sig/session.rbs +10 -10
  81. data/sig/transcoder/xml.rbs +1 -1
  82. data/sig/transcoder.rbs +4 -5
  83. metadata +11 -5
  84. data/lib/httpx/registry.rb +0 -85
  85. data/sig/registry.rbs +0 -13
@@ -104,7 +104,7 @@ module HTTPX
104
104
  resolver_connection.send(request)
105
105
  @connections << connection
106
106
  rescue ResolveError, Resolv::DNS::EncodeError => e
107
- @queries.delete(hostname)
107
+ reset_hostname(hostname)
108
108
  emit_resolve_error(connection, connection.origin.host, e)
109
109
  end
110
110
  end
@@ -113,7 +113,7 @@ module HTTPX
113
113
  response.raise_for_status
114
114
  rescue StandardError => e
115
115
  hostname = @requests.delete(request)
116
- connection = @queries.delete(hostname)
116
+ connection = reset_hostname(hostname)
117
117
  emit_resolve_error(connection, connection.origin.host, e)
118
118
  else
119
119
  # @type var response: HTTPX::Response
@@ -128,30 +128,35 @@ module HTTPX
128
128
  end
129
129
 
130
130
  def parse(request, response)
131
- begin
132
- answers = decode_response_body(response)
133
- rescue Resolv::DNS::DecodeError => e
134
- host, connection = @queries.first
135
- @queries.delete(host)
136
- emit_resolve_error(connection, connection.origin.host, e)
137
- return
138
- end
131
+ code, result = decode_response_body(response)
139
132
 
140
- if answers.nil?
133
+ case code
134
+ when :ok
135
+ parse_addresses(result)
136
+ when :no_domain_found
141
137
  # Indicates no such domain was found.
142
138
 
143
139
  host = @requests.delete(request)
144
- connection = @queries.delete(host)
140
+ connection = reset_hostname(host)
145
141
 
146
- emit_resolve_error(connection) unless @queries.value?(connection)
147
- elsif answers.empty?
148
- # no address found, eliminate candidates
142
+ emit_resolve_error(connection)
143
+ when :dns_error
149
144
  host = @requests.delete(request)
150
- connection = @queries.delete(host)
145
+ connection = reset_hostname(host)
151
146
 
152
- # eliminate other candidates
153
- @queries.delete_if { |_, conn| connection == conn }
147
+ emit_resolve_error(connection)
148
+ when :decode_error
149
+ host, connection = @queries.first
150
+ reset_hostname(host)
151
+ emit_resolve_error(connection, connection.origin.host, result)
152
+ end
153
+ end
154
154
 
155
+ def parse_addresses(answers)
156
+ if answers.empty?
157
+ # no address found, eliminate candidates
158
+ host = @requests.delete(request)
159
+ connection = reset_hostname(host)
155
160
  emit_resolve_error(connection)
156
161
  return
157
162
 
@@ -162,7 +167,7 @@ module HTTPX
162
167
  if address.key?("alias")
163
168
  alias_address = answers[address["alias"]]
164
169
  if alias_address.nil?
165
- @queries.delete(address["name"])
170
+ reset_hostname(address["name"])
166
171
  if catch(:coalesced) { early_resolve(connection, hostname: address["alias"]) }
167
172
  @connections.delete(connection)
168
173
  else
@@ -179,7 +184,7 @@ module HTTPX
179
184
  next if addresses.empty?
180
185
 
181
186
  hostname.delete_suffix!(".") if hostname.end_with?(".")
182
- connection = @queries.delete(hostname)
187
+ connection = reset_hostname(hostname, reset_candidates: false)
183
188
  next unless connection # probably a retried query for which there's an answer
184
189
 
185
190
  @connections.delete(connection)
@@ -224,5 +229,17 @@ module HTTPX
224
229
  raise Error, "unsupported DNS mime-type (#{response.headers["content-type"]})"
225
230
  end
226
231
  end
232
+
233
+ def reset_hostname(hostname, reset_candidates: true)
234
+ connection = @queries.delete(hostname)
235
+
236
+ return connection unless connection && reset_candidates
237
+
238
+ # eliminate other candidates
239
+ candidates = @queries.select { |_, conn| connection == conn }.keys
240
+ @queries.delete_if { |h, _| candidates.include?(h) }
241
+
242
+ connection
243
+ end
227
244
  end
228
245
  end
@@ -64,12 +64,7 @@ module HTTPX
64
64
  end
65
65
 
66
66
  def on_resolver_error(connection, error)
67
- @errors[connection] << error
68
-
69
- return unless @errors[connection].size >= @resolvers.size
70
-
71
- errors = @errors.delete(connection)
72
- emit(:error, connection, errors.first)
67
+ emit(:error, connection, error)
73
68
  end
74
69
 
75
70
  def on_resolver_close(resolver)
@@ -33,6 +33,7 @@ module HTTPX
33
33
  super
34
34
  @ns_index = 0
35
35
  @resolver_options = DEFAULTS.merge(@options.resolver_options)
36
+ @socket_type = @resolver_options.fetch(:socket_type, :udp)
36
37
  @nameserver = Array(@resolver_options[:nameserver]) if @resolver_options[:nameserver]
37
38
  @ndots = @resolver_options[:ndots]
38
39
  @search = Array(@resolver_options[:search]).map { |srch| srch.scan(/[^.]+/) }
@@ -156,7 +157,7 @@ module HTTPX
156
157
  else
157
158
 
158
159
  @timeouts.delete(host)
159
- @queries.delete(h)
160
+ reset_hostname(h, reset_candidates: false)
160
161
 
161
162
  return unless @queries.empty?
162
163
 
@@ -169,10 +170,49 @@ module HTTPX
169
170
 
170
171
  def dread(wsize = @resolver_options[:packet_size])
171
172
  loop do
173
+ wsize = @large_packet.capacity if @large_packet
174
+
172
175
  siz = @io.read(wsize, @read_buffer)
173
- return unless siz && siz.positive?
174
176
 
175
- parse(@read_buffer)
177
+ unless siz
178
+ ex = EOFError.new("descriptor closed")
179
+ ex.set_backtrace(caller)
180
+ raise ex
181
+ end
182
+
183
+ return unless siz.positive?
184
+
185
+ if @socket_type == :tcp
186
+ # packet may be incomplete, need to keep draining from the socket
187
+ if @large_packet
188
+ # large packet buffer already exists, continue pumping
189
+ @large_packet << @read_buffer
190
+
191
+ next unless @large_packet.full?
192
+
193
+ parse(@large_packet.to_s)
194
+
195
+ @socket_type = @resolver_options.fetch(:socket_type, :udp)
196
+ @large_packet = nil
197
+ transition(:closed)
198
+ return
199
+ else
200
+ size = @read_buffer[0, 2].unpack1("n")
201
+
202
+ if size > @read_buffer.bytesize
203
+ # only do buffer logic if it's worth it, and the whole packet isn't here already
204
+ @large_packet = Buffer.new(size)
205
+ @large_packet << @read_buffer.byteslice(2..-1)
206
+
207
+ next
208
+ else
209
+ parse(@read_buffer)
210
+ end
211
+ end
212
+ else # udp
213
+ parse(@read_buffer)
214
+ end
215
+
176
216
  return if @state == :closed
177
217
  end
178
218
  end
@@ -182,41 +222,63 @@ module HTTPX
182
222
  return if @write_buffer.empty?
183
223
 
184
224
  siz = @io.write(@write_buffer)
185
- return unless siz && siz.positive?
225
+
226
+ unless siz
227
+ ex = EOFError.new("descriptor closed")
228
+ ex.set_backtrace(caller)
229
+ raise ex
230
+ end
231
+
232
+ return unless siz.positive?
186
233
 
187
234
  return if @state == :closed
188
235
  end
189
236
  end
190
237
 
191
238
  def parse(buffer)
192
- begin
193
- addresses = Resolver.decode_dns_answer(buffer)
194
- rescue Resolv::DNS::DecodeError => e
239
+ code, result = Resolver.decode_dns_answer(buffer)
240
+
241
+ case code
242
+ when :ok
243
+ parse_addresses(result)
244
+ when :no_domain_found
245
+ # Indicates no such domain was found.
195
246
  hostname, connection = @queries.first
196
- @queries.delete(hostname)
197
- @timeouts.delete(hostname)
247
+ reset_hostname(hostname)
248
+
198
249
  @connections.delete(connection)
199
- ex = NativeResolveError.new(connection, connection.origin.host, e.message)
250
+ raise NativeResolveError.new(connection, connection.origin.host, "name or service not known (#{hostname})")
251
+ when :message_truncated
252
+ # TODO: what to do if it's already tcp??
253
+ return if @socket_type == :tcp
254
+
255
+ @socket_type = :tcp
256
+
257
+ hostname, _ = @queries.first
258
+ reset_hostname(hostname)
259
+ transition(:closed)
260
+ when :dns_error
261
+ hostname, connection = @queries.first
262
+ reset_hostname(hostname)
263
+ @connections.delete(connection)
264
+ ex = NativeResolveError.new(connection, connection.origin.host, "unknown DNS error (error code #{result})")
200
265
  ex.set_backtrace(e.backtrace)
201
266
  raise ex
202
- end
203
-
204
- if addresses.nil?
205
- # Indicates no such domain was found.
267
+ when :decode_error
206
268
  hostname, connection = @queries.first
207
- @queries.delete(hostname)
208
- @timeouts.delete(hostname)
269
+ reset_hostname(hostname)
270
+ @connections.delete(connection)
271
+ ex = NativeResolveError.new(connection, connection.origin.host, result.message)
272
+ ex.set_backtrace(result.backtrace)
273
+ raise ex
274
+ end
275
+ end
209
276
 
210
- unless @queries.value?(connection)
211
- @connections.delete(connection)
212
- raise NativeResolveError.new(connection, connection.origin.host)
213
- end
214
- elsif addresses.empty?
277
+ def parse_addresses(addresses)
278
+ if addresses.empty?
215
279
  # no address found, eliminate candidates
216
280
  _, connection = @queries.first
217
- candidates = @queries.select { |_, conn| connection == conn }.keys
218
- @queries.delete_if { |hs, _| candidates.include?(hs) }
219
- @timeouts.delete_if { |hs, _| candidates.include?(hs) }
281
+ reset_hostname(hostname)
220
282
  @connections.delete(connection)
221
283
  raise NativeResolveError.new(connection, connection.origin.host)
222
284
  else
@@ -226,20 +288,21 @@ module HTTPX
226
288
  connection = @queries.delete(name)
227
289
 
228
290
  unless connection
291
+ orig_name = name
229
292
  # absolute name
230
293
  name_labels = Resolv::DNS::Name.create(name).to_a
231
- name = @queries.keys.first { |hname| name_labels == Resolv::DNS::Name.create(hname).to_a }
294
+ name = @queries.each_key.first { |hname| name_labels == Resolv::DNS::Name.create(hname).to_a }
232
295
 
233
296
  # probably a retried query for which there's an answer
234
- return unless name
297
+ unless name
298
+ @timeouts.delete(orig_name)
299
+ return
300
+ end
235
301
 
236
302
  address["name"] = name
237
303
  connection = @queries.delete(name)
238
304
  end
239
305
 
240
- # eliminate other candidates
241
- @queries.delete_if { |_, conn| connection == conn }
242
-
243
306
  if address.key?("alias") # CNAME
244
307
  # clean up intermediate queries
245
308
  @timeouts.delete(name) unless connection.origin.host == name
@@ -265,6 +328,7 @@ module HTTPX
265
328
 
266
329
  def resolve(connection = @connections.first, hostname = nil)
267
330
  raise Error, "no URI to resolve" unless connection
331
+
268
332
  return unless @write_buffer.empty?
269
333
 
270
334
  hostname ||= @queries.key(connection)
@@ -281,12 +345,19 @@ module HTTPX
281
345
  end
282
346
  log { "resolver: query #{@record_type.name.split("::").last} for #{hostname}" }
283
347
  begin
284
- @write_buffer << Resolver.encode_dns_query(hostname, type: @record_type)
348
+ @write_buffer << encode_dns_query(hostname)
285
349
  rescue Resolv::DNS::EncodeError => e
286
350
  emit_resolve_error(connection, hostname, e)
287
351
  end
288
352
  end
289
353
 
354
+ def encode_dns_query(hostname)
355
+ message_id = Resolver.generate_id
356
+ msg = Resolver.encode_dns_query(hostname, type: @record_type, message_id: message_id)
357
+ msg[0, 2] = [msg.size, message_id].pack("nn") if @socket_type == :tcp
358
+ msg
359
+ end
360
+
290
361
  def generate_candidates(name)
291
362
  return [name] if name.end_with?(".")
292
363
 
@@ -294,18 +365,25 @@ module HTTPX
294
365
  name_parts = name.scan(/[^.]+/)
295
366
  candidates = [name] if @ndots <= name_parts.size - 1
296
367
  candidates.concat(@search.map { |domain| [*name_parts, *domain].join(".") })
297
- candidates << name unless candidates.include?(name)
368
+ fname = "#{name}."
369
+ candidates << fname unless candidates.include?(fname)
298
370
 
299
371
  candidates
300
372
  end
301
373
 
302
374
  def build_socket
303
- return if @io
304
-
305
375
  ip, port = @nameserver[@ns_index]
306
376
  port ||= DNS_PORT
307
- log { "resolver: server: #{ip}:#{port}..." }
308
- @io = UDP.new(ip, port, @options)
377
+
378
+ case @socket_type
379
+ when :udp
380
+ log { "resolver: server: udp://#{ip}:#{port}..." }
381
+ UDP.new(ip, port, @options)
382
+ when :tcp
383
+ log { "resolver: server: tcp://#{ip}:#{port}..." }
384
+ origin = URI("tcp://#{ip}:#{port}")
385
+ TCP.new(origin, [ip], @options)
386
+ end
309
387
  end
310
388
 
311
389
  def transition(nextstate)
@@ -319,7 +397,7 @@ module HTTPX
319
397
  when :open
320
398
  return unless @state == :idle
321
399
 
322
- build_socket
400
+ @io ||= build_socket
323
401
 
324
402
  @io.connect
325
403
  return unless @io.connected?
@@ -346,5 +424,19 @@ module HTTPX
346
424
  end
347
425
  end
348
426
  end
427
+
428
+ def reset_hostname(hostname, reset_candidates: true)
429
+ @timeouts.delete(hostname)
430
+ connection = @queries.delete(hostname)
431
+ @timeouts.delete(hostname)
432
+
433
+ return unless connection && reset_candidates
434
+
435
+ # eliminate other candidates
436
+ candidates = @queries.select { |_, conn| connection == conn }.keys
437
+ @queries.delete_if { |h, _| candidates.include?(h) }
438
+ # reset timeouts
439
+ @timeouts.delete_if { |h, _| candidates.include?(h) }
440
+ end
349
441
  end
350
442
  end
@@ -5,8 +5,6 @@ require "ipaddr"
5
5
 
6
6
  module HTTPX
7
7
  module Resolver
8
- extend Registry
9
-
10
8
  RESOLVE_TIMEOUT = 5
11
9
 
12
10
  require "httpx/resolver/resolver"
@@ -15,10 +13,6 @@ module HTTPX
15
13
  require "httpx/resolver/https"
16
14
  require "httpx/resolver/multi"
17
15
 
18
- register :system, System
19
- register :native, Native
20
- register :https, HTTPS
21
-
22
16
  @lookup_mutex = Mutex.new
23
17
  @lookups = Hash.new { |h, k| h[k] = [] }
24
18
 
@@ -28,6 +22,18 @@ module HTTPX
28
22
 
29
23
  module_function
30
24
 
25
+ def resolver_for(resolver_type)
26
+ case resolver_type
27
+ when :native then Native
28
+ when :system then System
29
+ when :https then HTTPS
30
+ else
31
+ return resolver_type if resolver_type.is_a?(Class) && resolver_type < Resolver
32
+
33
+ raise Error, "unsupported resolver type (#{resolver_type})"
34
+ end
35
+ end
36
+
31
37
  def nolookup_resolve(hostname)
32
38
  ip_resolve(hostname) || cached_lookup(hostname) || system_resolve(hostname)
33
39
  end
@@ -98,24 +104,29 @@ module HTTPX
98
104
  @identifier_mutex.synchronize { @identifier = (@identifier + 1) & 0xFFFF }
99
105
  end
100
106
 
101
- def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A)
107
+ def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id)
102
108
  Resolv::DNS::Message.new.tap do |query|
103
- query.id = generate_id
109
+ query.id = message_id
104
110
  query.rd = 1
105
111
  query.add_question(hostname, type)
106
112
  end.encode
107
113
  end
108
114
 
109
115
  def decode_dns_answer(payload)
110
- message = Resolv::DNS::Message.decode(payload)
116
+ begin
117
+ message = Resolv::DNS::Message.decode(payload)
118
+ rescue Resolv::DNS::DecodeError => e
119
+ return :decode_error, e
120
+ end
111
121
 
112
122
  # no domain was found
113
- return if message.rcode == Resolv::DNS::RCode::NXDomain
123
+ return :no_domain_found if message.rcode == Resolv::DNS::RCode::NXDomain
114
124
 
115
- addresses = []
125
+ return :message_truncated if message.tc == 1
116
126
 
117
- # TODO: raise an "other dns OtherResolvError" type of error
118
- return addresses if message.rcode != Resolv::DNS::RCode::NoError
127
+ return :dns_error, message.rcode if message.rcode != Resolv::DNS::RCode::NoError
128
+
129
+ addresses = []
119
130
 
120
131
  message.each_answer do |question, _, value|
121
132
  case value
@@ -134,7 +145,8 @@ module HTTPX
134
145
  }
135
146
  end
136
147
  end
137
- addresses
148
+
149
+ [:ok, addresses]
138
150
  end
139
151
  end
140
152
  end
@@ -56,12 +56,12 @@ module HTTPX
56
56
  end
57
57
 
58
58
  def bodyless?
59
- @request.verb == :head ||
59
+ @request.verb == "HEAD" ||
60
60
  no_data?
61
61
  end
62
62
 
63
63
  def complete?
64
- bodyless? || (@request.verb == :connect && @status == 200)
64
+ bodyless? || (@request.verb == "CONNECT" && @status == 200)
65
65
  end
66
66
 
67
67
  # :nocov:
@@ -87,32 +87,27 @@ module HTTPX
87
87
  end
88
88
 
89
89
  def json(*args)
90
- decode("json", *args)
90
+ decode(Transcoder::JSON, *args)
91
91
  end
92
92
 
93
93
  def form
94
- decode("form")
94
+ decode(Transcoder::Form)
95
95
  end
96
96
 
97
97
  def xml
98
- decode("xml")
98
+ decode(Transcoder::Xml)
99
99
  end
100
100
 
101
101
  private
102
102
 
103
- def decode(format, *args)
103
+ def decode(transcoder, *args)
104
104
  # TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
105
- transcoder = Transcoder.registry(format)
106
-
107
- raise Error, "no decoder available for \"#{format}\"" unless transcoder.respond_to?(:decode)
108
105
 
109
106
  decoder = transcoder.decode(self)
110
107
 
111
- raise Error, "no decoder available for \"#{format}\"" unless decoder
108
+ raise Error, "no decoder available for \"#{transcoder}\"" unless decoder
112
109
 
113
110
  decoder.call(self, *args)
114
- rescue Registry::Error
115
- raise Error, "no decoder available for \"#{format}\""
116
111
  end
117
112
 
118
113
  def no_data?
@@ -203,10 +198,8 @@ module HTTPX
203
198
  rescue ArgumentError # ex: unknown encoding name - utf
204
199
  content
205
200
  end
206
- when nil
207
- "".b
208
201
  else
209
- @buffer
202
+ "".b
210
203
  end
211
204
  end
212
205
  alias_method :to_str, :to_s
@@ -334,12 +327,13 @@ module HTTPX
334
327
  include Loggable
335
328
  extend Forwardable
336
329
 
337
- attr_reader :request, :error
330
+ attr_reader :request, :response, :error
338
331
 
339
332
  def_delegator :@request, :uri
340
333
 
341
334
  def initialize(request, error, options)
342
335
  @request = request
336
+ @response = request.response if request.response.is_a?(Response)
343
337
  @error = error
344
338
  @options = Options.new(options)
345
339
  log_exception(@error)
@@ -361,6 +355,10 @@ module HTTPX
361
355
  end
362
356
  end
363
357
 
358
+ def close
359
+ @response.close if @response.respond_to?(:close)
360
+ end
361
+
364
362
  def finished?
365
363
  true
366
364
  end
data/lib/httpx/session.rb CHANGED
@@ -203,6 +203,7 @@ module HTTPX
203
203
  end
204
204
 
205
205
  def receive_requests(requests, connections)
206
+ # @type var responses: Array[response]
206
207
  responses = []
207
208
 
208
209
  begin
@@ -55,5 +55,4 @@ module HTTPX::Transcoder
55
55
  Encoder.new(body)
56
56
  end
57
57
  end
58
- register "body", Body
59
58
  end
@@ -112,5 +112,4 @@ module HTTPX::Transcoder
112
112
  Encoder.new(chunks)
113
113
  end
114
114
  end
115
- register "chunker", Chunker
116
115
  end
@@ -55,5 +55,4 @@ module HTTPX::Transcoder
55
55
  Decoder
56
56
  end
57
57
  end
58
- register "form", Form
59
58
  end
@@ -56,5 +56,4 @@ module HTTPX::Transcoder
56
56
  end
57
57
  # rubocop:enable Style/SingleLineMethods
58
58
  end
59
- register "json", JSON
60
59
  end
@@ -51,5 +51,4 @@ module HTTPX::Transcoder
51
51
  end
52
52
  end
53
53
  end
54
- register "xml", Xml
55
54
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module HTTPX
4
4
  module Transcoder
5
- extend Registry
6
-
7
5
  using RegexpExtensions unless Regexp.method_defined?(:match?)
8
6
 
9
7
  module_function
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.22.4"
4
+ VERSION = "0.23.0"
5
5
  end
data/lib/httpx.rb CHANGED
@@ -11,7 +11,6 @@ require "httpx/domain_name"
11
11
  require "httpx/altsvc"
12
12
  require "httpx/callbacks"
13
13
  require "httpx/loggable"
14
- require "httpx/registry"
15
14
  require "httpx/transcoder"
16
15
  require "httpx/timers"
17
16
  require "httpx/pool"
data/sig/buffer.rbs CHANGED
@@ -11,6 +11,7 @@ module HTTPX
11
11
 
12
12
  def full?: () -> bool
13
13
  def shift!: (Integer) -> void
14
+ def capacity: () -> Integer
14
15
 
15
16
  # delegated
16
17
  def <<: (string data) -> String
data/sig/chainable.rbs CHANGED
@@ -2,9 +2,9 @@ module HTTPX
2
2
  module Chainable
3
3
  def request: (*Request, **untyped) -> Array[response]
4
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]
5
+ | (verb, uri | [uri], **untyped) -> response
6
+ | (Array[[verb, uri] | [verb, uri, options]], **untyped) -> Array[response]
7
+ | (verb, _Each[uri | [uri, options]], **untyped) -> Array[response]
8
8
 
9
9
  def accept: (String) -> Session
10
10
  def wrap: () { (Session) -> void } -> void