httpx 0.6.7 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx/adapters/faraday.rb +2 -2
- data/lib/httpx/altsvc.rb +18 -2
- data/lib/httpx/chainable.rb +27 -9
- data/lib/httpx/connection.rb +215 -65
- data/lib/httpx/connection/http1.rb +54 -18
- data/lib/httpx/connection/http2.rb +100 -37
- data/lib/httpx/extensions.rb +2 -2
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +11 -3
- data/lib/httpx/io/tcp.rb +12 -2
- data/lib/httpx/loggable.rb +6 -6
- data/lib/httpx/options.rb +43 -28
- data/lib/httpx/plugins/authentication.rb +1 -1
- data/lib/httpx/plugins/compression.rb +28 -8
- data/lib/httpx/plugins/compression/gzip.rb +22 -12
- data/lib/httpx/plugins/cookies.rb +12 -8
- data/lib/httpx/plugins/digest_authentication.rb +2 -0
- data/lib/httpx/plugins/expect.rb +79 -0
- data/lib/httpx/plugins/follow_redirects.rb +1 -2
- data/lib/httpx/plugins/h2c.rb +0 -1
- data/lib/httpx/plugins/proxy.rb +23 -20
- data/lib/httpx/plugins/proxy/http.rb +9 -6
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +5 -1
- data/lib/httpx/plugins/proxy/ssh.rb +0 -4
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/retries.rb +32 -29
- data/lib/httpx/pool.rb +15 -10
- data/lib/httpx/registry.rb +2 -1
- data/lib/httpx/request.rb +8 -6
- data/lib/httpx/resolver.rb +7 -8
- data/lib/httpx/resolver/https.rb +15 -3
- data/lib/httpx/resolver/native.rb +22 -32
- data/lib/httpx/resolver/options.rb +2 -2
- data/lib/httpx/resolver/resolver_mixin.rb +1 -1
- data/lib/httpx/response.rb +17 -3
- data/lib/httpx/selector.rb +96 -95
- data/lib/httpx/session.rb +33 -34
- data/lib/httpx/timeout.rb +7 -1
- data/lib/httpx/version.rb +1 -1
- metadata +77 -20
@@ -0,0 +1,38 @@
|
|
1
|
+
# 0.9.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Multiple requests with specific options
|
6
|
+
|
7
|
+
You can now pass a third element to the "request element" of an array to `.request`.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
requests = [
|
11
|
+
[:post, "https://url/post", { form: { foo: "bar" } }],
|
12
|
+
[:post, "https://url/post", { form: { foo: "bar2" } }]
|
13
|
+
]
|
14
|
+
HTTPX.request(requests)
|
15
|
+
# or, if you want to pass options common to all requests
|
16
|
+
HTTPX.request(requests, max_concurrent_requests: 1)
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
### HTTPX::Session#build_request
|
21
|
+
|
22
|
+
`HTTPX::Session::build_request` is now public API from a session. You can now build requests before you send them. These request objects are still considered somewhat "internal", so consider them immutable and **do not rely on its API**. Just pass them forward.
|
23
|
+
|
24
|
+
Note: this API is only available for instantiated session, so there is no `HTTPX.build_request`.
|
25
|
+
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
|
29
|
+
HTTPX.wrap do |http|
|
30
|
+
requests = [
|
31
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar" } }),
|
32
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar2" } })
|
33
|
+
]
|
34
|
+
http.request(requests)
|
35
|
+
# or, if you want to pass options common to all requests
|
36
|
+
http.request(requests, max_concurrent_requests: 1)
|
37
|
+
end
|
38
|
+
```
|
@@ -153,7 +153,7 @@ module Faraday
|
|
153
153
|
proxy_options = { uri: env.request.proxy }
|
154
154
|
|
155
155
|
session = @session.with(options_from_env(env))
|
156
|
-
session = session.plugin(:proxy).
|
156
|
+
session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
|
157
157
|
|
158
158
|
responses = session.request(requests)
|
159
159
|
Array(responses).each_with_index do |response, index|
|
@@ -192,7 +192,7 @@ module Faraday
|
|
192
192
|
meth, uri, request_options = build_request(env)
|
193
193
|
|
194
194
|
session = @session.with(options_from_env(env))
|
195
|
-
session = session.plugin(:proxy).
|
195
|
+
session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
|
196
196
|
response = session.__send__(meth, uri, **request_options)
|
197
197
|
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
198
198
|
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -42,7 +42,23 @@ module HTTPX
|
|
42
42
|
|
43
43
|
origin = request.origin
|
44
44
|
host = request.uri.host
|
45
|
-
|
45
|
+
|
46
|
+
altsvc = response.headers["alt-svc"]
|
47
|
+
|
48
|
+
# https://tools.ietf.org/html/rfc7838#section-3
|
49
|
+
# A field value containing the special value "clear" indicates that the
|
50
|
+
# origin requests all alternatives for that origin to be invalidated
|
51
|
+
# (including those specified in the same response, in case of an
|
52
|
+
# invalid reply containing both "clear" and alternative services).
|
53
|
+
if altsvc == "clear"
|
54
|
+
@altsvc_mutex.synchronize do
|
55
|
+
@altsvcs[origin].clear
|
56
|
+
end
|
57
|
+
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
parse(altsvc) do |alt_origin, alt_params|
|
46
62
|
alt_origin.host ||= host
|
47
63
|
yield(alt_origin, origin, alt_params)
|
48
64
|
end
|
@@ -73,7 +89,7 @@ module HTTPX
|
|
73
89
|
alt_proto, alt_origin = alt_origin.split("=")
|
74
90
|
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
75
91
|
if alt_origin.start_with?(":")
|
76
|
-
alt_origin = "dummy#{alt_origin}"
|
92
|
+
alt_origin = "#{alt_proto}://dummy#{alt_origin}"
|
77
93
|
uri = URI.parse(alt_origin)
|
78
94
|
uri.host = nil
|
79
95
|
uri
|
data/lib/httpx/chainable.rb
CHANGED
@@ -3,25 +3,31 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Chainable
|
5
5
|
%i[head get post put delete trace options connect patch].each do |meth|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
7
|
+
def #{meth}(*uri, **options)
|
8
|
+
request(:#{meth}, uri, **options)
|
9
|
+
end
|
10
|
+
MOD
|
9
11
|
end
|
10
12
|
|
11
|
-
def request(
|
12
|
-
branch(default_options).request(
|
13
|
+
def request(*args, **options)
|
14
|
+
branch(default_options).request(*args, **options)
|
13
15
|
end
|
14
16
|
|
17
|
+
# :nocov:
|
15
18
|
def timeout(**args)
|
16
|
-
|
19
|
+
warn ":#{__method__} is deprecated, use :with_timeout instead"
|
20
|
+
branch(default_options.with(timeout: args))
|
17
21
|
end
|
18
22
|
|
19
23
|
def headers(headers)
|
20
|
-
|
24
|
+
warn ":#{__method__} is deprecated, use :with_headers instead"
|
25
|
+
branch(default_options.with(headers: headers))
|
21
26
|
end
|
27
|
+
# :nocov:
|
22
28
|
|
23
29
|
def accept(type)
|
24
|
-
headers
|
30
|
+
with(headers: { "accept" => String(type) })
|
25
31
|
end
|
26
32
|
|
27
33
|
def wrap(&blk)
|
@@ -53,11 +59,23 @@ module HTTPX
|
|
53
59
|
@options || Options.new
|
54
60
|
end
|
55
61
|
|
56
|
-
# :nodoc:
|
57
62
|
def branch(options, &blk)
|
58
63
|
return self.class.new(options, &blk) if is_a?(Session)
|
59
64
|
|
60
65
|
Session.new(options, &blk)
|
61
66
|
end
|
67
|
+
|
68
|
+
def method_missing(meth, *args, **options)
|
69
|
+
if meth =~ /\Awith_(.+)/
|
70
|
+
option = Regexp.last_match(1).to_sym
|
71
|
+
with(option => (args.first || options))
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def respond_to_missing?(meth, *args)
|
78
|
+
default_options.respond_to?(meth, *args) || super
|
79
|
+
end
|
62
80
|
end
|
63
81
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -46,6 +46,8 @@ module HTTPX
|
|
46
46
|
|
47
47
|
attr_reader :origin, :state, :pending, :options
|
48
48
|
|
49
|
+
attr_writer :timers
|
50
|
+
|
49
51
|
def initialize(type, uri, options)
|
50
52
|
@type = type
|
51
53
|
@origins = [uri.origin]
|
@@ -65,6 +67,10 @@ module HTTPX
|
|
65
67
|
else
|
66
68
|
transition(:idle)
|
67
69
|
end
|
70
|
+
|
71
|
+
@inflight = 0
|
72
|
+
@keep_alive_timeout = options.timeout.keep_alive_timeout
|
73
|
+
@keep_alive_timer = nil
|
68
74
|
end
|
69
75
|
|
70
76
|
# this is a semi-private method, to be used by the resolver
|
@@ -80,6 +86,8 @@ module HTTPX
|
|
80
86
|
def match?(uri, options)
|
81
87
|
return false if @state == :closing || @state == :closed
|
82
88
|
|
89
|
+
return false if exhausted?
|
90
|
+
|
83
91
|
(
|
84
92
|
(
|
85
93
|
@origins.include?(uri.origin) &&
|
@@ -95,6 +103,8 @@ module HTTPX
|
|
95
103
|
def mergeable?(connection)
|
96
104
|
return false if @state == :closing || @state == :closed || !@io
|
97
105
|
|
106
|
+
return false if exhausted?
|
107
|
+
|
98
108
|
!(@io.addresses & connection.addresses).empty? && @options == connection.options
|
99
109
|
end
|
100
110
|
|
@@ -110,10 +120,13 @@ module HTTPX
|
|
110
120
|
end
|
111
121
|
end
|
112
122
|
|
123
|
+
def create_idle
|
124
|
+
self.class.new(@type, @origin, @options)
|
125
|
+
end
|
126
|
+
|
113
127
|
def merge(connection)
|
114
128
|
@origins += connection.instance_variable_get(:@origins)
|
115
|
-
|
116
|
-
pending.each do |req|
|
129
|
+
connection.purge_pending do |req|
|
117
130
|
send(req)
|
118
131
|
end
|
119
132
|
end
|
@@ -130,7 +143,13 @@ module HTTPX
|
|
130
143
|
end
|
131
144
|
|
132
145
|
def purge_pending
|
133
|
-
[
|
146
|
+
pendings = []
|
147
|
+
if @parser
|
148
|
+
@inflight -= @parser.pending.size
|
149
|
+
pendings << @parser.pending
|
150
|
+
end
|
151
|
+
pendings << @pending
|
152
|
+
pendings.each do |pending|
|
134
153
|
pending.reject! do |request|
|
135
154
|
yield request
|
136
155
|
end
|
@@ -156,22 +175,45 @@ module HTTPX
|
|
156
175
|
end
|
157
176
|
|
158
177
|
def interests
|
159
|
-
|
178
|
+
# connecting
|
179
|
+
if connecting?
|
180
|
+
connect
|
181
|
+
|
182
|
+
return @io.interests if connecting?
|
183
|
+
end
|
184
|
+
|
185
|
+
# if the write buffer is full, we drain it
|
186
|
+
return :w if @write_buffer.full?
|
187
|
+
|
188
|
+
return @parser.interests if @parser
|
160
189
|
|
161
|
-
|
190
|
+
nil
|
162
191
|
end
|
163
192
|
|
164
193
|
def to_io
|
194
|
+
@io.to_io
|
195
|
+
end
|
196
|
+
|
197
|
+
def call
|
165
198
|
case @state
|
166
|
-
when :
|
167
|
-
|
199
|
+
when :closed
|
200
|
+
return
|
201
|
+
when :closing
|
202
|
+
consume
|
203
|
+
transition(:closed)
|
204
|
+
emit(:close)
|
205
|
+
when :open
|
206
|
+
consume
|
168
207
|
end
|
169
|
-
|
208
|
+
nil
|
170
209
|
end
|
171
210
|
|
172
211
|
def close
|
173
212
|
@parser.close if @parser
|
174
|
-
|
213
|
+
return unless @keep_alive_timer
|
214
|
+
|
215
|
+
@keep_alive_timer.cancel
|
216
|
+
remove_instance_variable(:@keep_alive_timer)
|
175
217
|
end
|
176
218
|
|
177
219
|
def reset
|
@@ -183,26 +225,25 @@ module HTTPX
|
|
183
225
|
def send(request)
|
184
226
|
if @parser && !@write_buffer.full?
|
185
227
|
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
228
|
+
if @keep_alive_timer
|
229
|
+
# when pushing a request into an existing connection, we have to check whether there
|
230
|
+
# is the possibility that the connection might have extended the keep alive timeout.
|
231
|
+
# for such cases, we want to ping for availability before deciding to shovel requests.
|
232
|
+
if @keep_alive_timer.fires_in.negative?
|
233
|
+
@pending << request
|
234
|
+
parser.ping
|
235
|
+
return
|
236
|
+
end
|
237
|
+
|
238
|
+
@keep_alive_timer.pause
|
239
|
+
end
|
240
|
+
@inflight += 1
|
186
241
|
parser.send(request)
|
187
242
|
else
|
188
243
|
@pending << request
|
189
244
|
end
|
190
245
|
end
|
191
246
|
|
192
|
-
def call
|
193
|
-
case @state
|
194
|
-
when :closed
|
195
|
-
return
|
196
|
-
when :closing
|
197
|
-
dwrite
|
198
|
-
transition(:closed)
|
199
|
-
emit(:close)
|
200
|
-
when :open
|
201
|
-
consume
|
202
|
-
end
|
203
|
-
nil
|
204
|
-
end
|
205
|
-
|
206
247
|
def timeout
|
207
248
|
return @timeout if defined?(@timeout)
|
208
249
|
|
@@ -213,50 +254,94 @@ module HTTPX
|
|
213
254
|
|
214
255
|
private
|
215
256
|
|
216
|
-
def
|
217
|
-
|
218
|
-
dread
|
219
|
-
dwrite
|
220
|
-
parser.consume
|
221
|
-
end
|
257
|
+
def connect
|
258
|
+
transition(:open)
|
222
259
|
end
|
223
260
|
|
224
|
-
def
|
225
|
-
|
226
|
-
siz = @io.read(wsize, @read_buffer)
|
227
|
-
unless siz
|
228
|
-
ex = EOFError.new("descriptor closed")
|
229
|
-
ex.set_backtrace(caller)
|
230
|
-
on_error(ex)
|
231
|
-
return
|
232
|
-
end
|
233
|
-
return if siz.zero?
|
234
|
-
|
235
|
-
log { "READ: #{siz} bytes..." }
|
236
|
-
parser << @read_buffer.to_s
|
237
|
-
return if @state == :closing || @state == :closed
|
238
|
-
end
|
261
|
+
def exhausted?
|
262
|
+
@parser && parser.exhausted?
|
239
263
|
end
|
240
264
|
|
241
|
-
def
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
265
|
+
def consume
|
266
|
+
catch(:called) do
|
267
|
+
loop do
|
268
|
+
parser.consume
|
269
|
+
|
270
|
+
# we exit if there's no more data to process
|
271
|
+
if @pending.size.zero? && @inflight.zero?
|
272
|
+
log(level: 3) { "NO MORE REQUESTS..." }
|
273
|
+
return
|
274
|
+
end
|
275
|
+
|
276
|
+
@timeout = @current_timeout
|
277
|
+
|
278
|
+
read_drained = false
|
279
|
+
write_drained = nil
|
280
|
+
|
281
|
+
# dread
|
282
|
+
loop do
|
283
|
+
siz = @io.read(@window_size, @read_buffer)
|
284
|
+
unless siz
|
285
|
+
ex = EOFError.new("descriptor closed")
|
286
|
+
ex.set_backtrace(caller)
|
287
|
+
on_error(ex)
|
288
|
+
return
|
289
|
+
end
|
290
|
+
|
291
|
+
if siz.zero?
|
292
|
+
read_drained = @read_buffer.empty?
|
293
|
+
break
|
294
|
+
end
|
295
|
+
|
296
|
+
log { "READ: #{siz} bytes..." }
|
297
|
+
|
298
|
+
parser << @read_buffer.to_s
|
299
|
+
|
300
|
+
break if @state == :closing || @state == :closed
|
301
|
+
|
302
|
+
# for HTTP/2, we just want to write goaway frame
|
303
|
+
end unless @state == :closing
|
304
|
+
|
305
|
+
# dwrite
|
306
|
+
loop do
|
307
|
+
if @write_buffer.empty?
|
308
|
+
# we only mark as drained on the first loop
|
309
|
+
write_drained = write_drained.nil? && @inflight.positive?
|
310
|
+
break
|
311
|
+
end
|
312
|
+
|
313
|
+
siz = @io.write(@write_buffer)
|
314
|
+
unless siz
|
315
|
+
ex = EOFError.new("descriptor closed")
|
316
|
+
ex.set_backtrace(caller)
|
317
|
+
on_error(ex)
|
318
|
+
return
|
319
|
+
end
|
320
|
+
log { "WRITE: #{siz} bytes..." }
|
321
|
+
|
322
|
+
if siz.zero?
|
323
|
+
write_drained = !@write_buffer.empty?
|
324
|
+
break
|
325
|
+
end
|
326
|
+
|
327
|
+
break if @state == :closing || @state == :closed
|
328
|
+
|
329
|
+
write_drained = false
|
330
|
+
end
|
331
|
+
|
332
|
+
# return if socket is drained
|
333
|
+
if read_drained && write_drained
|
334
|
+
log(level: 3) { "WAITING FOR EVENTS..." }
|
335
|
+
return
|
336
|
+
end
|
251
337
|
end
|
252
|
-
log { "WRITE: #{siz} bytes..." }
|
253
|
-
return if siz.zero?
|
254
|
-
return if @state == :closing || @state == :closed
|
255
338
|
end
|
256
339
|
end
|
257
340
|
|
258
341
|
def send_pending
|
259
342
|
while !@write_buffer.full? && (request = @pending.shift)
|
343
|
+
@inflight += 1
|
344
|
+
@keep_alive_timer.pause if @keep_alive_timer
|
260
345
|
parser.send(request)
|
261
346
|
end
|
262
347
|
end
|
@@ -276,26 +361,39 @@ module HTTPX
|
|
276
361
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
277
362
|
emit(:altsvc, alt_origin, origin, alt_params)
|
278
363
|
end
|
364
|
+
handle_response
|
279
365
|
request.emit(:response, response)
|
280
366
|
end
|
281
367
|
parser.on(:altsvc) do |alt_origin, origin, alt_params|
|
282
368
|
emit(:altsvc, alt_origin, origin, alt_params)
|
283
369
|
end
|
284
370
|
|
371
|
+
parser.on(:pong, &method(:send_pending))
|
372
|
+
|
285
373
|
parser.on(:promise) do |request, stream|
|
286
374
|
request.emit(:promise, parser, stream)
|
287
375
|
end
|
376
|
+
parser.on(:exhausted) do
|
377
|
+
emit(:exhausted)
|
378
|
+
end
|
288
379
|
parser.on(:origin) do |origin|
|
289
380
|
@origins << origin
|
290
381
|
end
|
291
|
-
parser.on(:close) do
|
382
|
+
parser.on(:close) do |force|
|
292
383
|
transition(:closing)
|
384
|
+
if force
|
385
|
+
transition(:closed)
|
386
|
+
emit(:close)
|
387
|
+
end
|
293
388
|
end
|
294
389
|
parser.on(:reset) do
|
295
|
-
|
296
|
-
|
390
|
+
if parser.empty?
|
391
|
+
reset
|
392
|
+
else
|
393
|
+
transition(:closing)
|
297
394
|
transition(:closed)
|
298
395
|
emit(:reset)
|
396
|
+
@parser.reset if @parser
|
299
397
|
transition(:idle)
|
300
398
|
transition(:open)
|
301
399
|
end
|
@@ -316,13 +414,20 @@ module HTTPX
|
|
316
414
|
|
317
415
|
def transition(nextstate)
|
318
416
|
case nextstate
|
417
|
+
when :idle
|
418
|
+
@timeout = @current_timeout = @options.timeout.connect_timeout
|
419
|
+
|
319
420
|
when :open
|
320
421
|
return if @state == :closed
|
321
422
|
|
423
|
+
total_timeout
|
424
|
+
|
322
425
|
@io.connect
|
323
426
|
return unless @io.connected?
|
324
427
|
|
325
428
|
send_pending
|
429
|
+
|
430
|
+
@timeout = @current_timeout = @options.timeout.operation_timeout
|
326
431
|
emit(:open)
|
327
432
|
when :closing
|
328
433
|
return unless @state == :open
|
@@ -330,8 +435,18 @@ module HTTPX
|
|
330
435
|
return unless @state == :closing
|
331
436
|
return unless @write_buffer.empty?
|
332
437
|
|
438
|
+
if @total_timeout
|
439
|
+
@total_timeout.cancel
|
440
|
+
remove_instance_variable(:@total_timeout)
|
441
|
+
end
|
442
|
+
|
333
443
|
@io.close
|
334
444
|
@read_buffer.clear
|
445
|
+
if @keep_alive_timer
|
446
|
+
@keep_alive_timer.cancel
|
447
|
+
remove_instance_variable(:@keep_alive_timer)
|
448
|
+
end
|
449
|
+
|
335
450
|
remove_instance_variable(:@timeout) if defined?(@timeout)
|
336
451
|
when :already_open
|
337
452
|
nextstate = :open
|
@@ -353,25 +468,60 @@ module HTTPX
|
|
353
468
|
emit(:close)
|
354
469
|
end
|
355
470
|
|
356
|
-
def
|
357
|
-
|
358
|
-
|
471
|
+
def handle_response
|
472
|
+
@inflight -= 1
|
473
|
+
return unless @inflight.zero?
|
474
|
+
|
475
|
+
if @keep_alive_timer
|
476
|
+
@keep_alive_timer.resume
|
477
|
+
@keep_alive_timer.reset
|
478
|
+
else
|
479
|
+
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
|
480
|
+
unless @inflight.zero?
|
481
|
+
log { "(#{@origin}): keep alive timeout expired" }
|
482
|
+
parser.ping
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
359
486
|
end
|
360
487
|
|
361
|
-
def
|
488
|
+
def on_error(error)
|
362
489
|
if error.instance_of?(TimeoutError)
|
363
490
|
if @timeout
|
364
491
|
@timeout -= error.timeout
|
365
492
|
return unless @timeout <= 0
|
366
493
|
end
|
367
494
|
|
368
|
-
|
495
|
+
if @total_timeout && @total_timeout.fires_in.negative?
|
496
|
+
ex = TotalTimeoutError.new(@total_timeout.interval, "Timed out after #{@total_timeout.interval} seconds")
|
497
|
+
ex.set_backtrace(error.backtrace)
|
498
|
+
error = ex
|
499
|
+
elsif connecting?
|
500
|
+
error = error.to_connection_error
|
501
|
+
end
|
369
502
|
end
|
503
|
+
handle_error(error)
|
504
|
+
reset
|
505
|
+
end
|
370
506
|
|
507
|
+
def handle_error(error)
|
371
508
|
parser.handle_error(error) if @parser && parser.respond_to?(:handle_error)
|
372
509
|
while (request = @pending.shift)
|
373
510
|
request.emit(:response, ErrorResponse.new(request, error, @options))
|
374
511
|
end
|
375
512
|
end
|
513
|
+
|
514
|
+
def total_timeout
|
515
|
+
total = @options.timeout.total_timeout
|
516
|
+
|
517
|
+
return unless total
|
518
|
+
|
519
|
+
@total_timeout ||= @timers.after(total) do
|
520
|
+
ex = TotalTimeoutError.new(total, "Timed out after #{total} seconds")
|
521
|
+
ex.set_backtrace(caller)
|
522
|
+
on_error(ex)
|
523
|
+
@parser.close if @parser
|
524
|
+
end
|
525
|
+
end
|
376
526
|
end
|
377
527
|
end
|