httpx 1.4.3 → 1.5.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/1_4_4.md +14 -0
  3. data/doc/release_notes/1_5_0.md +126 -0
  4. data/lib/httpx/adapters/datadog.rb +24 -3
  5. data/lib/httpx/adapters/webmock.rb +3 -0
  6. data/lib/httpx/buffer.rb +16 -5
  7. data/lib/httpx/connection/http1.rb +8 -9
  8. data/lib/httpx/connection/http2.rb +48 -24
  9. data/lib/httpx/connection.rb +40 -20
  10. data/lib/httpx/errors.rb +2 -11
  11. data/lib/httpx/headers.rb +24 -23
  12. data/lib/httpx/io/ssl.rb +8 -4
  13. data/lib/httpx/io/tcp.rb +9 -7
  14. data/lib/httpx/io/unix.rb +1 -1
  15. data/lib/httpx/loggable.rb +13 -1
  16. data/lib/httpx/options.rb +63 -48
  17. data/lib/httpx/parser/http1.rb +1 -1
  18. data/lib/httpx/plugins/aws_sigv4.rb +1 -0
  19. data/lib/httpx/plugins/callbacks.rb +19 -6
  20. data/lib/httpx/plugins/circuit_breaker.rb +4 -3
  21. data/lib/httpx/plugins/cookies/jar.rb +0 -2
  22. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +7 -4
  23. data/lib/httpx/plugins/cookies.rb +4 -4
  24. data/lib/httpx/plugins/follow_redirects.rb +4 -2
  25. data/lib/httpx/plugins/grpc/call.rb +1 -1
  26. data/lib/httpx/plugins/h2c.rb +7 -1
  27. data/lib/httpx/plugins/persistent.rb +22 -1
  28. data/lib/httpx/plugins/proxy/http.rb +3 -1
  29. data/lib/httpx/plugins/query.rb +35 -0
  30. data/lib/httpx/plugins/response_cache/file_store.rb +115 -15
  31. data/lib/httpx/plugins/response_cache/store.rb +7 -67
  32. data/lib/httpx/plugins/response_cache.rb +179 -29
  33. data/lib/httpx/plugins/retries.rb +27 -15
  34. data/lib/httpx/plugins/stream.rb +46 -20
  35. data/lib/httpx/plugins/stream_bidi.rb +315 -0
  36. data/lib/httpx/pool.rb +58 -5
  37. data/lib/httpx/request/body.rb +1 -1
  38. data/lib/httpx/request.rb +21 -5
  39. data/lib/httpx/resolver/https.rb +10 -4
  40. data/lib/httpx/resolver/native.rb +13 -13
  41. data/lib/httpx/resolver/resolver.rb +4 -0
  42. data/lib/httpx/resolver/system.rb +37 -14
  43. data/lib/httpx/resolver.rb +2 -2
  44. data/lib/httpx/response/body.rb +10 -21
  45. data/lib/httpx/response/buffer.rb +36 -12
  46. data/lib/httpx/response.rb +11 -1
  47. data/lib/httpx/selector.rb +16 -12
  48. data/lib/httpx/session.rb +80 -23
  49. data/lib/httpx/timers.rb +24 -16
  50. data/lib/httpx/transcoder/multipart/decoder.rb +4 -2
  51. data/lib/httpx/transcoder/multipart/encoder.rb +2 -1
  52. data/lib/httpx/version.rb +1 -1
  53. data/sig/buffer.rbs +1 -1
  54. data/sig/chainable.rbs +5 -2
  55. data/sig/connection/http2.rbs +11 -2
  56. data/sig/connection.rbs +4 -4
  57. data/sig/errors.rbs +0 -3
  58. data/sig/headers.rbs +15 -10
  59. data/sig/httpx.rbs +5 -1
  60. data/sig/io/tcp.rbs +6 -0
  61. data/sig/loggable.rbs +2 -0
  62. data/sig/options.rbs +7 -1
  63. data/sig/plugins/cookies/cookie.rbs +1 -3
  64. data/sig/plugins/cookies/jar.rbs +4 -4
  65. data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
  66. data/sig/plugins/cookies.rbs +2 -0
  67. data/sig/plugins/h2c.rbs +4 -0
  68. data/sig/plugins/proxy/http.rbs +3 -0
  69. data/sig/plugins/proxy.rbs +4 -0
  70. data/sig/plugins/response_cache/file_store.rbs +19 -0
  71. data/sig/plugins/response_cache/store.rbs +13 -0
  72. data/sig/plugins/response_cache.rbs +41 -19
  73. data/sig/plugins/retries.rbs +4 -3
  74. data/sig/plugins/stream.rbs +8 -1
  75. data/sig/plugins/stream_bidi.rbs +68 -0
  76. data/sig/plugins/upgrade/h2.rbs +9 -0
  77. data/sig/plugins/upgrade.rbs +5 -0
  78. data/sig/pool.rbs +5 -0
  79. data/sig/punycode.rbs +5 -0
  80. data/sig/request.rbs +7 -0
  81. data/sig/resolver/https.rbs +3 -2
  82. data/sig/resolver/native.rbs +1 -2
  83. data/sig/resolver/resolver.rbs +11 -3
  84. data/sig/resolver/system.rbs +19 -2
  85. data/sig/resolver.rbs +11 -7
  86. data/sig/response/body.rbs +3 -4
  87. data/sig/response/buffer.rbs +2 -3
  88. data/sig/response.rbs +2 -2
  89. data/sig/selector.rbs +20 -10
  90. data/sig/session.rbs +14 -6
  91. data/sig/timers.rbs +5 -7
  92. data/sig/transcoder/multipart.rbs +4 -3
  93. metadata +14 -5
  94. data/lib/httpx/session2.rb +0 -23
  95. data/lib/httpx/transcoder/utils/inflater.rb +0 -21
  96. data/sig/transcoder/utils/inflater.rbs +0 -12
data/lib/httpx/request.rb CHANGED
@@ -104,21 +104,32 @@ module HTTPX
104
104
  @state = :idle
105
105
  @response = nil
106
106
  @peer_address = nil
107
+ @ping = false
107
108
  @persistent = @options.persistent
108
109
  @active_timeouts = []
109
110
  end
110
111
 
111
- # the read timeout defined for this requet.
112
+ # whether request has been buffered with a ping
113
+ def ping?
114
+ @ping
115
+ end
116
+
117
+ # marks the request as having been buffered with a ping
118
+ def ping!
119
+ @ping = true
120
+ end
121
+
122
+ # the read timeout defined for this request.
112
123
  def read_timeout
113
124
  @options.timeout[:read_timeout]
114
125
  end
115
126
 
116
- # the write timeout defined for this requet.
127
+ # the write timeout defined for this request.
117
128
  def write_timeout
118
129
  @options.timeout[:write_timeout]
119
130
  end
120
131
 
121
- # the request timeout defined for this requet.
132
+ # the request timeout defined for this request.
122
133
  def request_timeout
123
134
  @options.timeout[:request_timeout]
124
135
  end
@@ -144,6 +155,10 @@ module HTTPX
144
155
  :w
145
156
  end
146
157
 
158
+ def can_buffer?
159
+ @state != :done
160
+ end
161
+
147
162
  # merges +h+ into the instance of HTTPX::Headers of the request.
148
163
  def merge_headers(h)
149
164
  @headers = @headers.merge(h)
@@ -211,7 +226,7 @@ module HTTPX
211
226
  return @query if defined?(@query)
212
227
 
213
228
  query = []
214
- if (q = @query_params)
229
+ if (q = @query_params) && !q.empty?
215
230
  query << Transcoder::Form.encode(q)
216
231
  end
217
232
  query << @uri.query if @uri.query
@@ -236,7 +251,7 @@ module HTTPX
236
251
 
237
252
  # :nocov:
238
253
  def inspect
239
- "#<HTTPX::Request:#{object_id} " \
254
+ "#<#{self.class}:#{object_id} " \
240
255
  "#{@verb} " \
241
256
  "#{uri} " \
242
257
  "@headers=#{@headers} " \
@@ -249,6 +264,7 @@ module HTTPX
249
264
  case nextstate
250
265
  when :idle
251
266
  @body.rewind
267
+ @ping = false
252
268
  @response = nil
253
269
  @drainer = nil
254
270
  @active_timeouts.clear
@@ -2,11 +2,14 @@
2
2
 
3
3
  require "resolv"
4
4
  require "uri"
5
- require "cgi"
6
5
  require "forwardable"
7
6
  require "httpx/base64"
8
7
 
9
8
  module HTTPX
9
+ # Implementation of a DoH name resolver (https://www.youtube.com/watch?v=unMXvnY2FNM).
10
+ # It wraps an HTTPX::Connection object which integrates with the main session in the
11
+ # same manner as other performed HTTP requests.
12
+ #
10
13
  class Resolver::HTTPS < Resolver::Resolver
11
14
  extend Forwardable
12
15
  using URIExtensions
@@ -27,14 +30,13 @@ module HTTPX
27
30
  use_get: false,
28
31
  }.freeze
29
32
 
30
- def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate, :inflight?
33
+ def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate, :inflight?, :handle_socket_timeout
31
34
 
32
35
  def initialize(_, options)
33
36
  super
34
37
  @resolver_options = DEFAULTS.merge(@options.resolver_options)
35
38
  @queries = {}
36
39
  @requests = {}
37
- @connections = []
38
40
  @uri = URI(@resolver_options[:uri])
39
41
  @uri_addresses = nil
40
42
  @resolver = Resolv::DNS.new
@@ -75,7 +77,11 @@ module HTTPX
75
77
 
76
78
  private
77
79
 
78
- def resolve(connection = @connections.first, hostname = nil)
80
+ def resolve(connection = nil, hostname = nil)
81
+ @connections.shift until @connections.empty? || @connections.first.state != :closed
82
+
83
+ connection ||= @connections.first
84
+
79
85
  return unless connection
80
86
 
81
87
  hostname ||= @queries.key(connection)
@@ -4,6 +4,9 @@ require "forwardable"
4
4
  require "resolv"
5
5
 
6
6
  module HTTPX
7
+ # Implements a pure ruby name resolver, which abides by the Selectable API.
8
+ # It delegates DNS payload encoding/decoding to the +resolv+ stlid gem.
9
+ #
7
10
  class Resolver::Native < Resolver::Resolver
8
11
  extend Forwardable
9
12
  using URIExtensions
@@ -34,7 +37,6 @@ module HTTPX
34
37
  @search = Array(@resolver_options[:search]).map { |srch| srch.scan(/[^.]+/) }
35
38
  @_timeouts = Array(@resolver_options[:timeouts])
36
39
  @timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup }
37
- @connections = []
38
40
  @name = nil
39
41
  @queries = {}
40
42
  @read_buffer = "".b
@@ -46,6 +48,10 @@ module HTTPX
46
48
  transition(:closed)
47
49
  end
48
50
 
51
+ def terminate
52
+ emit(:close, self)
53
+ end
54
+
49
55
  def closed?
50
56
  @state == :closed
51
57
  end
@@ -120,10 +126,7 @@ module HTTPX
120
126
  @ns_index += 1
121
127
  nameserver = @nameserver
122
128
  if nameserver && @ns_index < nameserver.size
123
- log do
124
- "resolver #{FAMILY_TYPES[@record_type]}: " \
125
- "failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})"
126
- end
129
+ log { "resolver #{FAMILY_TYPES[@record_type]}: failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})" }
127
130
  transition(:idle)
128
131
  @timeouts.clear
129
132
  retry
@@ -158,9 +161,7 @@ module HTTPX
158
161
  timeouts = @timeouts[h]
159
162
 
160
163
  if !timeouts.empty?
161
- log do
162
- "resolver #{FAMILY_TYPES[@record_type]}: timeout after #{interval}s, retry (with #{timeouts.first}s) #{h}..."
163
- end
164
+ log { "resolver #{FAMILY_TYPES[@record_type]}: timeout after #{interval}s, retry (with #{timeouts.first}s) #{h}..." }
164
165
  # must downgrade to tcp AND retry on same host as last
165
166
  downgrade_socket
166
167
  resolve(connection, h)
@@ -388,10 +389,9 @@ module HTTPX
388
389
 
389
390
  if hostname.nil?
390
391
  hostname = connection.peer.host
391
- log do
392
- "resolver #{FAMILY_TYPES[@record_type]}: " \
393
- "resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
394
- end if connection.peer.non_ascii_hostname
392
+ if connection.peer.non_ascii_hostname
393
+ log { "resolver #{FAMILY_TYPES[@record_type]}: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}" }
394
+ end
395
395
 
396
396
  hostname = generate_candidates(hostname).each do |name|
397
397
  @queries[name] = connection
@@ -507,7 +507,7 @@ module HTTPX
507
507
  end
508
508
 
509
509
  while (connection = @connections.shift)
510
- emit_resolve_error(connection, host, error)
510
+ emit_resolve_error(connection, connection.peer.host, error)
511
511
  end
512
512
  end
513
513
  end
@@ -4,6 +4,9 @@ require "resolv"
4
4
  require "ipaddr"
5
5
 
6
6
  module HTTPX
7
+ # Base class for all internal internet name resolvers. It handles basic blocks
8
+ # from the Selectable API.
9
+ #
7
10
  class Resolver::Resolver
8
11
  include Callbacks
9
12
  include Loggable
@@ -36,6 +39,7 @@ module HTTPX
36
39
  @family = family
37
40
  @record_type = RECORD_TYPES[family]
38
41
  @options = options
42
+ @connections = []
39
43
 
40
44
  set_resolver_callbacks
41
45
  end
@@ -3,6 +3,15 @@
3
3
  require "resolv"
4
4
 
5
5
  module HTTPX
6
+ # Implementation of a synchronous name resolver which relies on the system resolver,
7
+ # which is lib'c getaddrinfo function (abstracted in ruby via Addrinfo.getaddrinfo).
8
+ #
9
+ # Its main advantage is relying on the reference implementation for name resolution
10
+ # across most/all OSs which deploy ruby (it's what TCPSocket also uses), its main
11
+ # disadvantage is the inability to set timeouts / check socket for readiness events,
12
+ # hence why it relies on using the Timeout module, which poses a lot of problems for
13
+ # the selector loop, specially when network is unstable.
14
+ #
6
15
  class Resolver::System < Resolver::Resolver
7
16
  using URIExtensions
8
17
 
@@ -23,14 +32,13 @@ module HTTPX
23
32
  attr_reader :state
24
33
 
25
34
  def initialize(options)
26
- super(nil, options)
35
+ super(0, options)
27
36
  @resolver_options = @options.resolver_options
28
37
  resolv_options = @resolver_options.dup
29
38
  timeouts = resolv_options.delete(:timeouts) || Resolver::RESOLVE_TIMEOUT
30
39
  @_timeouts = Array(timeouts)
31
40
  @timeouts = Hash.new { |tims, host| tims[host] = @_timeouts.dup }
32
41
  resolv_options.delete(:cache)
33
- @connections = []
34
42
  @queries = []
35
43
  @ips = []
36
44
  @pipe_mutex = Thread::Mutex.new
@@ -100,7 +108,14 @@ module HTTPX
100
108
  def handle_socket_timeout(interval)
101
109
  error = HTTPX::ResolveTimeoutError.new(interval, "timed out while waiting on select")
102
110
  error.set_backtrace(caller)
103
- on_error(error)
111
+ @queries.each do |host, connection|
112
+ @connections.delete(connection)
113
+ emit_resolve_error(connection, host, error)
114
+ end
115
+
116
+ while (connection = @connections.shift)
117
+ emit_resolve_error(connection, connection.peer.host, error)
118
+ end
104
119
  end
105
120
 
106
121
  private
@@ -131,19 +146,22 @@ module HTTPX
131
146
  case event
132
147
  when DONE
133
148
  *pair, addrs = @pipe_mutex.synchronize { @ips.pop }
134
- @queries.delete(pair)
135
- _, connection = pair
136
- @connections.delete(connection)
149
+ if pair
150
+ @queries.delete(pair)
151
+ family, connection = pair
152
+ @connections.delete(connection)
137
153
 
138
- family, connection = pair
139
- catch(:coalesced) { emit_addresses(connection, family, addrs) }
154
+ catch(:coalesced) { emit_addresses(connection, family, addrs) }
155
+ end
140
156
  when ERROR
141
157
  *pair, error = @pipe_mutex.synchronize { @ips.pop }
142
- @queries.delete(pair)
143
- @connections.delete(connection)
158
+ if pair && error
159
+ @queries.delete(pair)
160
+ @connections.delete(connection)
144
161
 
145
- _, connection = pair
146
- emit_resolve_error(connection, connection.peer.host, error)
162
+ _, connection = pair
163
+ emit_resolve_error(connection, connection.peer.host, error)
164
+ end
147
165
  end
148
166
  end
149
167
 
@@ -152,11 +170,16 @@ module HTTPX
152
170
  resolve
153
171
  end
154
172
 
155
- def resolve(connection = @connections.first)
173
+ def resolve(connection = nil, hostname = nil)
174
+ @connections.shift until @connections.empty? || @connections.first.state != :closed
175
+
176
+ connection ||= @connections.first
177
+
156
178
  raise Error, "no URI to resolve" unless connection
179
+
157
180
  return unless @queries.empty?
158
181
 
159
- hostname = connection.peer.host
182
+ hostname ||= connection.peer.host
160
183
  scheme = connection.origin.scheme
161
184
  log do
162
185
  "resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
@@ -92,8 +92,8 @@ module HTTPX
92
92
  end
93
93
 
94
94
  ips = entries.flat_map do |address|
95
- if address.key?("alias")
96
- lookup(address["alias"], lookups, ttl)
95
+ if (als = address["alias"])
96
+ lookup(als, lookups, ttl)
97
97
  else
98
98
  IPAddr.new(address["data"])
99
99
  end
@@ -11,6 +11,9 @@ module HTTPX
11
11
  # Array of encodings contained in the response "content-encoding" header.
12
12
  attr_reader :encodings
13
13
 
14
+ attr_reader :buffer
15
+ protected :buffer
16
+
14
17
  # initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+.
15
18
  def initialize(response, options)
16
19
  @response = response
@@ -148,18 +151,17 @@ module HTTPX
148
151
  end
149
152
 
150
153
  def ==(other)
151
- object_id == other.object_id || begin
152
- if other.respond_to?(:read)
153
- _with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
154
- else
155
- to_s == other.to_s
156
- end
157
- end
154
+ super || case other
155
+ when Response::Body
156
+ @buffer == other.buffer
157
+ else
158
+ @buffer = other
159
+ end
158
160
  end
159
161
 
160
162
  # :nocov:
161
163
  def inspect
162
- "#<HTTPX::Response::Body:#{object_id} " \
164
+ "#<#{self.class}:#{object_id} " \
163
165
  "@state=#{@state} " \
164
166
  "@length=#{@length}>"
165
167
  end
@@ -226,19 +228,6 @@ module HTTPX
226
228
  @state = nextstate
227
229
  end
228
230
 
229
- def _with_same_buffer_pos # :nodoc:
230
- return yield unless @buffer && @buffer.respond_to?(:pos)
231
-
232
- # @type ivar @buffer: StringIO | Tempfile
233
- current_pos = @buffer.pos
234
- @buffer.rewind
235
- begin
236
- yield
237
- ensure
238
- @buffer.pos = current_pos
239
- end
240
- end
241
-
242
231
  class << self
243
232
  def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc:
244
233
  case encoding
@@ -7,6 +7,9 @@ require "tempfile"
7
7
  module HTTPX
8
8
  # wraps and delegates to an internal buffer, which can be a StringIO or a Tempfile.
9
9
  class Response::Buffer < SimpleDelegator
10
+ attr_reader :buffer
11
+ protected :buffer
12
+
10
13
  # initializes buffer with the +threshold_size+ over which the payload gets buffer to a tempfile,
11
14
  # the initial +bytesize+, and the +encoding+.
12
15
  def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY)
@@ -20,7 +23,14 @@ module HTTPX
20
23
  def initialize_dup(other)
21
24
  super
22
25
 
23
- @buffer = other.instance_variable_get(:@buffer).dup
26
+ # create new descriptor in READ-ONLY mode
27
+ @buffer =
28
+ case other.buffer
29
+ when StringIO
30
+ StringIO.new(other.buffer.string, mode: File::RDONLY)
31
+ else
32
+ other.buffer.class.new(other.buffer.path, encoding: Encoding::BINARY, mode: File::RDONLY)
33
+ end
24
34
  end
25
35
 
26
36
  # size in bytes of the buffered content.
@@ -46,7 +56,7 @@ module HTTPX
46
56
  end
47
57
  when Tempfile
48
58
  rewind
49
- content = _with_same_buffer_pos { @buffer.read }
59
+ content = @buffer.read
50
60
  begin
51
61
  content.force_encoding(@encoding)
52
62
  rescue ArgumentError # ex: unknown encoding name - utf
@@ -61,6 +71,30 @@ module HTTPX
61
71
  @buffer.unlink if @buffer.respond_to?(:unlink)
62
72
  end
63
73
 
74
+ def ==(other)
75
+ super || begin
76
+ return false unless other.is_a?(Response::Buffer)
77
+
78
+ if @buffer.nil?
79
+ other.buffer.nil?
80
+ elsif @buffer.respond_to?(:read) &&
81
+ other.respond_to?(:read)
82
+ buffer_pos = @buffer.pos
83
+ other_pos = other.buffer.pos
84
+ @buffer.rewind
85
+ other.buffer.rewind
86
+ begin
87
+ FileUtils.compare_stream(@buffer, other.buffer)
88
+ ensure
89
+ @buffer.pos = buffer_pos
90
+ other.buffer.pos = other_pos
91
+ end
92
+ else
93
+ to_s == other.to_s
94
+ end
95
+ end
96
+ end
97
+
64
98
  private
65
99
 
66
100
  # initializes the buffer into a StringIO, or turns it into a Tempfile when the threshold
@@ -82,15 +116,5 @@ module HTTPX
82
116
 
83
117
  __setobj__(@buffer)
84
118
  end
85
-
86
- def _with_same_buffer_pos # :nodoc:
87
- current_pos = @buffer.pos
88
- @buffer.rewind
89
- begin
90
- yield
91
- ensure
92
- @buffer.pos = current_pos
93
- end
94
- end
95
119
  end
96
120
  end
@@ -71,6 +71,14 @@ module HTTPX
71
71
  @content_type = nil
72
72
  end
73
73
 
74
+ # dupped initialization
75
+ def initialize_dup(orig)
76
+ super
77
+ # if a response gets dupped, the body handle must also get dupped to prevent
78
+ # two responses from using the same file handle to read.
79
+ @body = orig.body.dup
80
+ end
81
+
74
82
  # closes the respective +@request+ and +@body+.
75
83
  def close
76
84
  @request.close
@@ -126,7 +134,7 @@ module HTTPX
126
134
 
127
135
  # :nocov:
128
136
  def inspect
129
- "#<Response:#{object_id} " \
137
+ "#<#{self.class}:#{object_id} " \
130
138
  "HTTP/#{version} " \
131
139
  "@status=#{@status} " \
132
140
  "@headers=#{@headers} " \
@@ -275,6 +283,8 @@ module HTTPX
275
283
  true
276
284
  end
277
285
 
286
+ def finish!; end
287
+
278
288
  # raises the wrapped exception.
279
289
  def raise_for_status
280
290
  raise @error
@@ -35,14 +35,21 @@ module HTTPX
35
35
  end
36
36
 
37
37
  begin
38
- select(timeout, &:call)
38
+ select(timeout) do |c|
39
+ c.log(level: 2) { "[#{c.state}] selected#{" after #{timeout} secs" unless timeout.nil?}..." }
40
+
41
+ c.call
42
+ end
43
+
39
44
  @timers.fire
40
45
  rescue TimeoutError => e
41
46
  @timers.fire(e)
42
47
  end
43
48
  end
44
49
  rescue StandardError => e
45
- emit_error(e)
50
+ each_connection do |c|
51
+ c.emit(:error, e)
52
+ end
46
53
  rescue Exception # rubocop:disable Lint/RescueException
47
54
  each_connection do |conn|
48
55
  conn.force_reset
@@ -77,9 +84,10 @@ module HTTPX
77
84
  return enum_for(__method__) unless block
78
85
 
79
86
  @selectables.each do |c|
80
- if c.is_a?(Resolver::Resolver)
87
+ case c
88
+ when Resolver::Resolver
81
89
  c.each_connection(&block)
82
- else
90
+ when Connection
83
91
  yield c
84
92
  end
85
93
  end
@@ -133,6 +141,8 @@ module HTTPX
133
141
  @selectables.delete_if do |io|
134
142
  interests = io.interests
135
143
 
144
+ io.log(level: 2) { "[#{io.state}] registering for select (#{interests})#{" for #{interval} seconds" unless interval.nil?}" }
145
+
136
146
  (r ||= []) << io if READABLE.include?(interests)
137
147
  (w ||= []) << io if WRITABLE.include?(interests)
138
148
 
@@ -169,6 +179,8 @@ module HTTPX
169
179
 
170
180
  interests = io.interests
171
181
 
182
+ io.log(level: 2) { "[#{io.state}] registering for select (#{interests})#{" for #{interval} seconds" unless interval.nil?}" }
183
+
172
184
  result = case interests
173
185
  when :r then io.to_io.wait_readable(interval)
174
186
  when :w then io.to_io.wait_writable(interval)
@@ -205,13 +217,5 @@ module HTTPX
205
217
 
206
218
  connection_interval
207
219
  end
208
-
209
- def emit_error(e)
210
- @selectables.each do |c|
211
- next if c.is_a?(Resolver::Resolver)
212
-
213
- c.emit(:error, e)
214
- end
215
- end
216
220
  end
217
221
  end