httpx 1.8.1 → 1.8.4

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/1_8_2.md +30 -0
  3. data/doc/release_notes/1_8_3.md +9 -0
  4. data/doc/release_notes/1_8_4.md +13 -0
  5. data/lib/httpx/adapters/webmock.rb +2 -1
  6. data/lib/httpx/buffer.rb +5 -0
  7. data/lib/httpx/callbacks.rb +2 -2
  8. data/lib/httpx/chainable.rb +2 -2
  9. data/lib/httpx/connection/http1.rb +1 -1
  10. data/lib/httpx/connection/http2.rb +69 -17
  11. data/lib/httpx/connection.rb +58 -14
  12. data/lib/httpx/errors.rb +3 -0
  13. data/lib/httpx/io/ssl.rb +18 -8
  14. data/lib/httpx/options.rb +21 -12
  15. data/lib/httpx/plugins/auth.rb +6 -5
  16. data/lib/httpx/plugins/cookies/cookie.rb +1 -1
  17. data/lib/httpx/plugins/cookies.rb +2 -2
  18. data/lib/httpx/plugins/follow_redirects.rb +28 -14
  19. data/lib/httpx/plugins/grpc/call.rb +4 -4
  20. data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -2
  21. data/lib/httpx/plugins/oauth.rb +4 -2
  22. data/lib/httpx/plugins/persistent.rb +15 -0
  23. data/lib/httpx/plugins/proxy/http.rb +27 -6
  24. data/lib/httpx/plugins/proxy.rb +13 -7
  25. data/lib/httpx/plugins/retries.rb +26 -9
  26. data/lib/httpx/plugins/server_sent_events.rb +2 -2
  27. data/lib/httpx/plugins/stream.rb +2 -2
  28. data/lib/httpx/plugins/tracing.rb +20 -7
  29. data/lib/httpx/request/body.rb +8 -5
  30. data/lib/httpx/request.rb +8 -5
  31. data/lib/httpx/resolver/multi.rb +2 -2
  32. data/lib/httpx/resolver/native.rb +4 -0
  33. data/lib/httpx/resolver/resolver.rb +2 -2
  34. data/lib/httpx/response/body.rb +2 -2
  35. data/lib/httpx/response.rb +7 -7
  36. data/lib/httpx/selector.rb +1 -1
  37. data/lib/httpx/session.rb +7 -3
  38. data/lib/httpx/transcoder/chunker.rb +2 -2
  39. data/lib/httpx/transcoder/json.rb +8 -9
  40. data/lib/httpx/transcoder/utils/deflater.rb +1 -2
  41. data/lib/httpx/version.rb +1 -1
  42. data/sig/connection/http2.rbs +15 -1
  43. data/sig/connection.rbs +4 -2
  44. data/sig/errors.rbs +2 -2
  45. data/sig/io/ssl.rbs +5 -0
  46. data/sig/plugins/auth.rbs +3 -3
  47. data/sig/plugins/persistent.rbs +4 -0
  48. data/sig/plugins/proxy/http.rbs +2 -0
  49. data/sig/plugins/retries.rbs +4 -2
  50. data/sig/plugins/tracing.rbs +1 -1
  51. data/sig/request.rbs +2 -1
  52. metadata +8 -2
@@ -75,9 +75,12 @@ module HTTPX
75
75
 
76
76
  max_redirects = redirect_request.max_redirects
77
77
 
78
- return response unless response.is_a?(Response)
79
- return response unless REDIRECT_STATUS.include?(response.status) && response.headers.key?("location")
80
- return response unless max_redirects.positive?
78
+ return response unless response.is_a?(Response) &&
79
+ # it's a redirect response with location
80
+ REDIRECT_STATUS.include?(response.status) &&
81
+ response.headers.key?("location") &&
82
+ # maximum number of redirects hasn't been reached yet
83
+ max_redirects.positive?
81
84
 
82
85
  redirect_uri = __get_location_from_response(response)
83
86
 
@@ -135,9 +138,9 @@ module HTTPX
135
138
  return ErrorResponse.new(request, error)
136
139
  end
137
140
 
138
- retry_request = build_request(redirect_method, redirect_uri, redirect_params, options)
141
+ redirect_request = build_request(redirect_method, redirect_uri, redirect_params, options)
139
142
 
140
- request.redirect_request = retry_request
143
+ request.redirect_request = redirect_request
141
144
 
142
145
  redirect_after = response.headers["retry-after"]
143
146
 
@@ -151,24 +154,24 @@ module HTTPX
151
154
  redirect_after = Utils.parse_retry_after(redirect_after)
152
155
 
153
156
  retry_start = Utils.now
154
- log { "redirecting after #{redirect_after} secs..." }
157
+ redirect_request.log { "redirecting after #{redirect_after} secs..." }
155
158
  selector.after(redirect_after) do
156
159
  if (response = request.response)
157
160
  response.finish!
158
- retry_request.response = response
161
+ redirect_request.response = response
159
162
  # request has terminated abruptly meanwhile
160
- retry_request.emit_response(response)
163
+ redirect_request.emit_response(response)
161
164
  else
162
- log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
163
- send_request(retry_request, selector, options)
165
+ redirect_request.log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
166
+ send_request(redirect_request, selector, options)
164
167
  end
165
168
  end
166
169
  else
167
- send_request(retry_request, selector, options)
170
+ send_request(redirect_request, selector, options)
168
171
 
169
172
  # recalling itself, in case an error was triggered by the above, and we can
170
173
  # verify retriability again.
171
- return fetch_response(request, selector, options)
174
+ return fetch_response(redirect_request, selector, options)
172
175
  end
173
176
  nil
174
177
  end
@@ -190,8 +193,7 @@ module HTTPX
190
193
 
191
194
  # :nodoc:
192
195
  def __get_location_from_response(response)
193
- # @type var location_uri: http_uri
194
- location_uri = URI(response.headers["location"])
196
+ location_uri = URI(response.headers["location"]) #: http_uri
195
197
  location_uri = response.uri.merge(location_uri) if location_uri.relative?
196
198
  location_uri
197
199
  end
@@ -231,6 +233,18 @@ module HTTPX
231
233
  @redirect_request.response
232
234
  end
233
235
 
236
+ def response=(response)
237
+ return super unless @redirect_request && @response.nil? # rubocop:disable Lint/ReturnInVoidContext
238
+
239
+ @redirect_request.response = response
240
+ end
241
+
242
+ def emit_response(response)
243
+ return super unless @redirect_request && @response.nil?
244
+
245
+ @redirect_request.emit_response(response)
246
+ end
247
+
234
248
  def max_redirects
235
249
  @options.max_redirects || MAX_REDIRECTS
236
250
  end
@@ -48,12 +48,12 @@ module HTTPX
48
48
  end
49
49
  end
50
50
 
51
- def respond_to_missing?(meth, *args, &blk)
52
- grpc_response.respond_to?(meth, *args) || super
51
+ def respond_to_missing?(meth, ...)
52
+ grpc_response.respond_to?(meth, ...) || super
53
53
  end
54
54
 
55
- def method_missing(meth, *args, &blk)
56
- return grpc_response.__send__(meth, *args, &blk) if grpc_response.respond_to?(meth)
55
+ def method_missing(meth, ...)
56
+ return grpc_response.__send__(meth, ...) if grpc_response.respond_to?(meth)
57
57
 
58
58
  super
59
59
  end
@@ -78,8 +78,8 @@ module HTTPX
78
78
  end
79
79
  end
80
80
 
81
- def self.encode(*args, **kwargs)
82
- Deflater.new(*args, **kwargs)
81
+ def self.encode(...)
82
+ Deflater.new(...)
83
83
  end
84
84
 
85
85
  def self.decode(response)
@@ -296,9 +296,11 @@ module HTTPX
296
296
 
297
297
  private
298
298
 
299
- def generate_auth_token
300
- return unless @oauth_session
299
+ def generate_auth_token(*)
300
+ return super unless @oauth_session
301
301
 
302
+ # should_regenerate arg ignored, as there's no way to check
303
+ # for token expiration yet.
302
304
  @oauth_session.fetch_access_token(self)
303
305
  end
304
306
 
@@ -18,6 +18,12 @@ module HTTPX
18
18
  # https://gitlab.com/os85/httpx/wikis/Persistent
19
19
  #
20
20
  module Persistent
21
+ SAFE_REONNECTABLE_ERRORS = [
22
+ PingTimeoutError,
23
+ Connection::HTTP2::GoawayError,
24
+ Connection::HTTP2::PingError,
25
+ ].freeze
26
+
21
27
  class << self
22
28
  def load_dependencies(klass)
23
29
  klass.plugin(:fiber_concurrency)
@@ -59,6 +65,15 @@ module HTTPX
59
65
  Retries::RECONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
60
66
  end
61
67
 
68
+ # whether the error can be safely retried without booking threshold attempts.
69
+ def safe_reconnectable_error?(error)
70
+ SAFE_REONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
71
+ end
72
+
73
+ def can_reconnect?(_, response)
74
+ super || (response.is_a?(ErrorResponse) && safe_reconnectable_error?(response.error))
75
+ end
76
+
62
77
  def when_to_retry(request, response, *)
63
78
  return super unless response.is_a?(ErrorResponse)
64
79
 
@@ -46,6 +46,11 @@ module HTTPX
46
46
  end
47
47
 
48
48
  module ConnectionMethods
49
+ def initialize(*)
50
+ @tunnel_parser = nil
51
+ super
52
+ end
53
+
49
54
  def force_close(*)
50
55
  if @state == :connecting
51
56
  # proxy connect related requests should not be reenqueed
@@ -59,6 +64,12 @@ module HTTPX
59
64
 
60
65
  private
61
66
 
67
+ def purge_after_closed
68
+ super
69
+
70
+ @tunnel_parser = nil
71
+ end
72
+
62
73
  def handle_transition(nextstate)
63
74
  return super unless @options.proxy && @options.proxy.uri.scheme == "http"
64
75
 
@@ -69,8 +80,15 @@ module HTTPX
69
80
  @io.connect
70
81
  return unless @io.connected?
71
82
 
72
- @parser || begin
73
- @parser = parser = parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
83
+ @tunnel_parser || begin
84
+ if @parser
85
+ # in case this happens, requests from @parser need to be reenqueued for delivery.
86
+ enqueue_pending_requests_from_parser(@parser)
87
+ @parser = nil
88
+ end
89
+
90
+ @tunnel_parser = @parser = parser =
91
+ parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
74
92
  parser.extend(ProxyParser)
75
93
  parser.on(:response, &method(:__http_on_connect))
76
94
  parser.on(:close) do
@@ -86,6 +104,9 @@ module HTTPX
86
104
 
87
105
  initial_state = @state
88
106
 
107
+ # because #reset will nillify @parser...
108
+ tunnel_parser = @parser
109
+
89
110
  reset
90
111
 
91
112
  if @pending.empty?
@@ -98,8 +119,8 @@ module HTTPX
98
119
  connect_request = parser = nil
99
120
 
100
121
  if initial_state == :connecting
101
- parser = @parser
102
- @parser.reset
122
+ parser = tunnel_parser
123
+ parser.reset
103
124
  if @pending.first.is_a?(ConnectRequest)
104
125
  connect_request = @pending.shift # this happened when reenqueing
105
126
  end
@@ -107,7 +128,7 @@ module HTTPX
107
128
 
108
129
  idling
109
130
 
110
- @parser = parser
131
+ @tunnel_parser = @parser = parser
111
132
  if connect_request
112
133
  @inflight += 1
113
134
  parser.send(connect_request)
@@ -124,7 +145,7 @@ module HTTPX
124
145
  case @state
125
146
  when :connecting
126
147
  parser = @parser
127
- @parser = nil
148
+ @tunnel_parser = @parser = nil
128
149
  parser.close
129
150
  when :idle
130
151
  @parser.callbacks.clear
@@ -92,16 +92,16 @@ module HTTPX
92
92
  @authenticator = load_authenticator(scheme, @username, @password)
93
93
  end
94
94
 
95
- def can_authenticate?(*args)
95
+ def can_authenticate?(...)
96
96
  return false unless @authenticator && @authenticator.respond_to?(:can_authenticate?)
97
97
 
98
- @authenticator.can_authenticate?(*args)
98
+ @authenticator.can_authenticate?(...)
99
99
  end
100
100
 
101
- def authenticate(*args)
101
+ def authenticate(...)
102
102
  return unless @authenticator
103
103
 
104
- @authenticator.authenticate(*args)
104
+ @authenticator.authenticate(...)
105
105
  end
106
106
 
107
107
  def ==(other)
@@ -182,9 +182,11 @@ module HTTPX
182
182
  if (no_proxy = proxy.no_proxy)
183
183
  no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
184
184
 
185
- # TODO: setting proxy to nil leaks the connection object in the pool
186
- return super(request_uri, selector, options.merge(proxy: nil)) unless URI::Generic.use_proxy?(request_uri.host, next_proxy.host,
187
- next_proxy.port, no_proxy)
185
+ unless no_proxy != "*" && # NO_PROXY=* bypasses proxy use
186
+ URI::Generic.use_proxy?(request_uri.host, next_proxy.host, next_proxy.port, no_proxy)
187
+ # TODO: setting proxy to nil leaks the connection object in the pool
188
+ return super(request_uri, selector, options.merge(proxy: nil))
189
+ end
188
190
  end
189
191
 
190
192
  super(request_uri, selector, options.merge(proxy: proxy))
@@ -341,6 +343,10 @@ module HTTPX
341
343
  module InstanceMethods
342
344
  private
343
345
 
346
+ def proxy_error?(request, response, _)
347
+ super && !request.retries.positive?
348
+ end
349
+
344
350
  def retryable_error?(ex, *)
345
351
  super || ex.is_a?(ProxyConnectionError)
346
352
  end
@@ -29,10 +29,23 @@ module HTTPX
29
29
  Errno::ETIMEDOUT,
30
30
  ConnectionError,
31
31
  TLSError,
32
- Connection::HTTP2::Error,
32
+ Zlib::BufError,
33
+ PingTimeoutError,
34
+ # HTTP/2 GOAWAY or RST_STREAM errors guaranteed to be retriable for all
35
+ # types of requests.
36
+ Connection::HTTP2::RefusedStreamError,
37
+ Connection::HTTP2::GoawayError,
38
+ Connection::HTTP2::PingError,
39
+
40
+ # happens if connections were somehow sent to a closed or closing
41
+ # HTTP2::Connection, request guaranteed to not have been processed.
42
+ ::HTTP2::Error::ConnectionClosed,
33
43
  ].freeze
34
44
 
35
45
  RETRYABLE_ERRORS = (RECONNECTABLE_ERRORS + [
46
+ # most RST_STREAM errors aren't safely retriable, as request may have
47
+ # been partially processed.
48
+ Connection::HTTP2::RstStreamError,
36
49
  Parser::Error,
37
50
  TimeoutError,
38
51
  ]).freeze
@@ -148,10 +161,13 @@ module HTTPX
148
161
  retryable_response?(response, options)
149
162
  try_partial_retry(request, response)
150
163
  log { "failed to get response, #{request.retries} tries to go..." }
151
- prepare_to_retry(request, response)
152
164
 
153
- if (retry_after = when_to_retry(request, response, options)) && retry_after.positive?
165
+ # retry-after must be calculated before prepare_to_retry, as it relies on
166
+ # state changed byit.
167
+ retry_after = when_to_retry(request, response, options)
168
+ prepare_to_retry(request, response)
154
169
 
170
+ if retry_after&.positive?
155
171
  retry_start = Utils.now
156
172
  log { "retrying after #{retry_after} secs..." }
157
173
  selector.after(retry_after) do
@@ -191,13 +207,14 @@ module HTTPX
191
207
  RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) } && !ex.is_a?(TotalRequestTimeoutError)
192
208
  end
193
209
 
194
- def proxy_error?(request, response, _)
195
- super && !request.retries.positive?
210
+ def prepare_to_retry(request, response)
211
+ request.retries -= 1 unless can_reconnect?(request, response)
212
+ request.transition(:idle)
196
213
  end
197
214
 
198
- def prepare_to_retry(request, _response)
199
- request.retries -= 1 unless request.ping? # do not exhaust retries on connection liveness probes
200
- request.transition(:idle)
215
+ # do not exhaust retries on connection liveness probes
216
+ def can_reconnect?(request, _)
217
+ request.ping?
201
218
  end
202
219
 
203
220
  def when_to_retry(request, response, options)
@@ -249,7 +266,7 @@ module HTTPX
249
266
  attr_writer :partial_response
250
267
 
251
268
  # initializes the request instance, sets the number of retries for the request.
252
- def initialize(*args)
269
+ def initialize(*)
253
270
  super
254
271
  @retries = @options.max_retries
255
272
  @partial_response = nil
@@ -40,7 +40,7 @@ module HTTPX
40
40
  end
41
41
 
42
42
  module InstanceMethods
43
- def request(*args, **options)
43
+ def request(*, **options)
44
44
  options[:stream] = true if options[:event_stream]
45
45
 
46
46
  super
@@ -146,7 +146,7 @@ module HTTPX
146
146
  def when_to_retry(request, *)
147
147
  retry_after = request.last_server_sent_message&.retry_after
148
148
 
149
- retry_after / 1_000.0 if retry_after # original in milliseconds
149
+ retry_after.to_i / 1_000.0 if retry_after # original in milliseconds
150
150
 
151
151
  request.last_server_sent_message&.retry_after && super
152
152
  end
@@ -111,10 +111,10 @@ module HTTPX
111
111
  end || super
112
112
  end
113
113
 
114
- def method_missing(meth, *args, **kwargs, &block)
114
+ def method_missing(meth, ...)
115
115
  return super unless response.respond_to?(meth)
116
116
 
117
- response.__send__(meth, *args, **kwargs, &block)
117
+ response.__send__(meth, ...)
118
118
  end
119
119
  end
120
120
 
@@ -36,9 +36,9 @@ module HTTPX::Plugins
36
36
  %i[start finish reset enabled?].each do |callback|
37
37
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
38
38
  # proxies ##{callback} calls to wrapper tracers.
39
- def #{callback}(*args) # def start(*args)
40
- @tracers.each { |t| t.#{callback}(*args) } # @tracers.each { |t| t.start(*args) }
41
- end # end
39
+ def #{callback}(...) # def start(...)
40
+ @tracers.each { |t| t.#{callback}(...) } # @tracers.each { |t| t.start(....) }
41
+ end # end
42
42
  OUT
43
43
  end
44
44
  end
@@ -108,11 +108,13 @@ module HTTPX::Plugins
108
108
  def initialize(*)
109
109
  super
110
110
 
111
- @init_time = ::Time.now.utc
111
+ @init_time = nil
112
112
  end
113
113
 
114
114
  def send_request_to_parser(request)
115
115
  if connecting?
116
+ @init_time ||= ::Time.now.utc
117
+
116
118
  # request span timeframe should include the time it took to connect.
117
119
  request.init_time ||= @init_time
118
120
  end
@@ -123,13 +125,24 @@ module HTTPX::Plugins
123
125
  def idling
124
126
  super
125
127
 
126
- # time of initial request(s) is accounted from the moment
127
- # the connection is back to :idle, and ready to connect again.
128
- @init_time = ::Time.now.utc
128
+ @init_time = nil
129
+ end
130
+
131
+ def terminate
132
+ super
133
+
134
+ # ensure that connections which go back to the pool reset their init time.
135
+ @init_time = nil
129
136
  end
130
137
 
131
138
  private
132
139
 
140
+ def connect
141
+ @init_time ||= ::Time.now.utc
142
+
143
+ super
144
+ end
145
+
133
146
  def ping(request)
134
147
  # if a connection is probed for liveness, the request timeframe should include
135
148
  # it too.
@@ -4,14 +4,17 @@ module HTTPX
4
4
  # Implementation of the HTTP Request body as a delegator which iterates (responds to +each+) payload chunks.
5
5
  class Request::Body < SimpleDelegator
6
6
  class << self
7
- def new(_, options, body: nil, **params)
8
- if body.is_a?(self)
7
+ def new(h, options, body: nil, **params)
8
+ case body
9
+ when self
9
10
  # request derives its options from body
10
11
  body.options = options.merge(params)
11
- return body
12
+ body
13
+ when nil
14
+ super(h, options, **params)
15
+ else
16
+ super
12
17
  end
13
-
14
- super
15
18
  end
16
19
  end
17
20
 
data/lib/httpx/request.rb CHANGED
@@ -105,9 +105,9 @@ module HTTPX
105
105
 
106
106
  @state = :idle
107
107
  @connection = @response =
108
- @drainer = @peer_address =
109
- @informational_status = @on_response_arrived = nil
110
- @ping = @started = false
108
+ @drainer = @peer_address = @callbacks =
109
+ @informational_status = @on_response_arrived = nil
110
+ @ping = @started = @complete = false
111
111
  @persistent = @options.persistent
112
112
  @active_timeouts = []
113
113
  end
@@ -115,14 +115,17 @@ module HTTPX
115
115
  # dupped initialization
116
116
  def initialize_dup(orig)
117
117
  super
118
- @uri = orig.instance_variable_get(:@uri).dup
119
118
  @headers = orig.instance_variable_get(:@headers).dup
120
119
  @body = orig.instance_variable_get(:@body).dup
120
+ @active_timeouts = orig.instance_variable_get(:@active_timeouts).dup
121
121
  end
122
122
 
123
123
  def complete!(response = @response)
124
+ return false if @complete
125
+
124
126
  emit(:complete, response)
125
127
  reset_timers(true)
128
+ @complete = true
126
129
  end
127
130
 
128
131
  # whether request has been buffered with a ping
@@ -296,7 +299,7 @@ module HTTPX
296
299
  case nextstate
297
300
  when :idle
298
301
  @body.rewind
299
- @ping = false
302
+ @ping = @complete = false
300
303
  @response = @drainer = nil
301
304
 
302
305
  # request may be sent to a different connection and will be
@@ -35,8 +35,8 @@ module HTTPX
35
35
  @resolvers.each { |r| r.current_session = s }
36
36
  end
37
37
 
38
- def log(*args, **kwargs, &blk)
39
- @resolvers.each { |r| r.log(*args, **kwargs, &blk) }
38
+ def log(...)
39
+ @resolvers.each { |r| r.log(...) }
40
40
  end
41
41
 
42
42
  def closed?
@@ -539,6 +539,10 @@ module HTTPX
539
539
  return unless @io.connected?
540
540
 
541
541
  resolve if @queries.empty? && !@connections.empty?
542
+
543
+ # #resolve may have closed the resolver already as part of error handling.
544
+ # @fiber-switch-guard
545
+ return unless @io
542
546
  when :closed
543
547
  return if @state == :closed
544
548
 
@@ -56,9 +56,9 @@ module HTTPX
56
56
  call
57
57
  end
58
58
 
59
- def force_close(*args)
59
+ def force_close(...)
60
60
  while (connection = @connections.shift)
61
- connection.force_close(*args)
61
+ connection.force_close(...)
62
62
  end
63
63
  end
64
64
 
@@ -69,7 +69,7 @@ module HTTPX
69
69
  end
70
70
 
71
71
  # reads a chunk from the payload (implementation of the IO reader protocol).
72
- def read(*args)
72
+ def read(...)
73
73
  return unless @buffer
74
74
 
75
75
  unless @reader
@@ -77,7 +77,7 @@ module HTTPX
77
77
  @reader = @buffer
78
78
  end
79
79
 
80
- @reader.read(*args)
80
+ @reader.read(...)
81
81
  end
82
82
 
83
83
  # size of the decoded response payload. May differ from "content-length" header if
@@ -36,10 +36,10 @@ module HTTPX
36
36
  # * HTTPX::Response::Body#read
37
37
  # * HTTPX::Response::Body#copy_to
38
38
  # * HTTPX::Response::Body#close
39
- attr_reader :body
39
+ attr_reader :body
40
40
 
41
41
  # The HTTP protocol version used to fetch the response.
42
- attr_reader :version
42
+ attr_reader :version
43
43
 
44
44
  # returns the response body buffered in a string.
45
45
  def_delegator :@body, :to_s
@@ -68,7 +68,7 @@ module HTTPX
68
68
  @headers = @options.headers_class.new(headers)
69
69
  @body = @options.response_body_class.new(self, @options)
70
70
  @finished = complete?
71
- @content_type = @content_length = nil
71
+ @callbacks = @content_type = @content_length = nil
72
72
  end
73
73
 
74
74
  # dupped initialization
@@ -176,8 +176,8 @@ module HTTPX
176
176
  #
177
177
  # response.json #≈> { "foo" => "bar" } for "{\"foo\":\"bar\"}" payload
178
178
  # response.json(symbolize_names: true) #≈> { foo: "bar" } for "{\"foo\":\"bar\"}" payload
179
- def json(*args)
180
- decode(Transcoder::JSON, *args)
179
+ def json(...)
180
+ decode(Transcoder::JSON, ...)
181
181
  end
182
182
 
183
183
  # decodes the response payload into a ruby object **if** the payload is valid
@@ -200,7 +200,7 @@ module HTTPX
200
200
  #
201
201
  # +transcoder+ must implement the internal transcoder API, i.e. respond to <tt>decode(HTTPX::Response response)</tt>,
202
202
  # which returns a decoder which responds to <tt>call(HTTPX::Response response, **kwargs)</tt>
203
- def decode(transcoder, *args)
203
+ def decode(transcoder, ...)
204
204
  # TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
205
205
 
206
206
  decoder = transcoder.decode(self)
@@ -209,7 +209,7 @@ module HTTPX
209
209
 
210
210
  @body.rewind
211
211
 
212
- decoder.call(self, *args)
212
+ decoder.call(self, ...)
213
213
  end
214
214
  end
215
215
 
@@ -56,7 +56,7 @@ module HTTPX
56
56
 
57
57
  begin
58
58
  select(timeout) do |c|
59
- c.log(level: 2) { "[#{c.state}] selected from selector##{object_id} #{" after #{timeout} secs" unless timeout.nil?}..." }
59
+ c.log(level: 2) { "[#{c.state}] selected from selector##{object_id}#{" after #{timeout} secs" unless timeout.nil?}..." }
60
60
 
61
61
  c.call
62
62
  end
data/lib/httpx/session.rb CHANGED
@@ -237,7 +237,7 @@ module HTTPX
237
237
 
238
238
  return unless response && response.finished?
239
239
 
240
- log(level: 2) { "response##{response.object_id} fetched" }
240
+ request.log(level: 2) { "response##{response.object_id} fetched" }
241
241
 
242
242
  response
243
243
  end
@@ -337,6 +337,8 @@ module HTTPX
337
337
  end
338
338
  end
339
339
 
340
+ log(level: 2) { "waiting to receive #{pending} pending requests..." }
341
+
340
342
  until pending.zero? || selector.empty?
341
343
  # loop on selector until at least one response has been received.
342
344
  waiting = true
@@ -353,14 +355,16 @@ module HTTPX
353
355
  # making the next loop cheaper (because we're dropping).
354
356
  next unless response
355
357
 
356
- request.complete!(response)
358
+ next unless request.complete!(response)
359
+
357
360
  responses[idx] = response
358
361
  request.on_response_arrived = nil
359
362
  pending -= 1
360
363
  end
361
364
  end
362
365
 
363
- raise Error, "something went wrong, responses not found and requests not resent" unless pending.zero?
366
+ raise Error, "something went wrong, #{pending} responses not found " \
367
+ "and requests not resent" unless pending.zero?
364
368
 
365
369
  responses
366
370
  end