httpx 0.22.4 → 0.23.0

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