httpx 1.4.0 → 1.4.2
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.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/lib/httpx/adapters/datadog.rb +55 -83
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/webmock.rb +7 -1
- data/lib/httpx/callbacks.rb +2 -2
- data/lib/httpx/connection/http2.rb +2 -2
- data/lib/httpx/connection.rb +106 -51
- data/lib/httpx/errors.rb +3 -0
- data/lib/httpx/loggable.rb +8 -1
- data/lib/httpx/plugins/callbacks.rb +1 -0
- data/lib/httpx/plugins/circuit_breaker.rb +1 -0
- data/lib/httpx/plugins/expect.rb +1 -1
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
- data/lib/httpx/request/body.rb +9 -14
- data/lib/httpx/request.rb +20 -0
- data/lib/httpx/resolver/https.rb +4 -2
- data/lib/httpx/resolver/native.rb +111 -55
- data/lib/httpx/resolver/resolver.rb +18 -11
- data/lib/httpx/resolver/system.rb +3 -5
- data/lib/httpx/selector.rb +33 -23
- data/lib/httpx/session.rb +17 -43
- data/lib/httpx/timers.rb +16 -1
- data/lib/httpx/transcoder/body.rb +15 -31
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -1
- data/sig/callbacks.rbs +2 -2
- data/sig/connection.rbs +19 -5
- data/sig/errors.rbs +3 -0
- data/sig/request/body.rbs +0 -8
- data/sig/request.rbs +3 -0
- data/sig/resolver/native.rbs +6 -1
- data/sig/selector.rbs +1 -0
- data/sig/session.rbs +2 -0
- data/sig/timers.rbs +15 -4
- data/sig/transcoder/body.rbs +1 -3
- data/sig/transcoder/utils/body_reader.rbs +1 -1
- data/sig/transcoder/utils/deflater.rbs +1 -1
- metadata +7 -3
@@ -35,6 +35,7 @@ module HTTPX
|
|
35
35
|
@_timeouts = Array(@resolver_options[:timeouts])
|
36
36
|
@timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup }
|
37
37
|
@connections = []
|
38
|
+
@name = nil
|
38
39
|
@queries = {}
|
39
40
|
@read_buffer = "".b
|
40
41
|
@write_buffer = Buffer.new(@resolver_options[:packet_size])
|
@@ -58,19 +59,6 @@ module HTTPX
|
|
58
59
|
when :open
|
59
60
|
consume
|
60
61
|
end
|
61
|
-
nil
|
62
|
-
rescue Errno::EHOSTUNREACH => e
|
63
|
-
@ns_index += 1
|
64
|
-
nameserver = @nameserver
|
65
|
-
if nameserver && @ns_index < nameserver.size
|
66
|
-
log { "resolver: failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})" }
|
67
|
-
transition(:idle)
|
68
|
-
@timeouts.clear
|
69
|
-
else
|
70
|
-
handle_error(e)
|
71
|
-
end
|
72
|
-
rescue NativeResolveError => e
|
73
|
-
handle_error(e)
|
74
62
|
end
|
75
63
|
|
76
64
|
def interests
|
@@ -105,9 +93,7 @@ module HTTPX
|
|
105
93
|
@timeouts.values_at(*hosts).reject(&:empty?).map(&:first).min
|
106
94
|
end
|
107
95
|
|
108
|
-
def handle_socket_timeout(interval)
|
109
|
-
do_retry(interval)
|
110
|
-
end
|
96
|
+
def handle_socket_timeout(interval); end
|
111
97
|
|
112
98
|
private
|
113
99
|
|
@@ -120,54 +106,94 @@ module HTTPX
|
|
120
106
|
end
|
121
107
|
|
122
108
|
def consume
|
123
|
-
|
124
|
-
|
125
|
-
|
109
|
+
loop do
|
110
|
+
dread if calculate_interests == :r
|
111
|
+
|
112
|
+
break unless calculate_interests == :w
|
113
|
+
|
114
|
+
# do_retry
|
115
|
+
dwrite
|
116
|
+
|
117
|
+
break unless calculate_interests == :r
|
118
|
+
end
|
119
|
+
rescue Errno::EHOSTUNREACH => e
|
120
|
+
@ns_index += 1
|
121
|
+
nameserver = @nameserver
|
122
|
+
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
|
127
|
+
transition(:idle)
|
128
|
+
@timeouts.clear
|
129
|
+
retry
|
130
|
+
else
|
131
|
+
handle_error(e)
|
132
|
+
emit(:close, self)
|
133
|
+
end
|
134
|
+
rescue NativeResolveError => e
|
135
|
+
handle_error(e)
|
136
|
+
close_or_resolve
|
137
|
+
retry unless closed?
|
126
138
|
end
|
127
139
|
|
128
|
-
def
|
129
|
-
|
140
|
+
def schedule_retry
|
141
|
+
h = @name
|
130
142
|
|
131
|
-
|
143
|
+
return unless h
|
132
144
|
|
133
|
-
|
145
|
+
connection = @queries[h]
|
134
146
|
|
135
|
-
|
147
|
+
timeouts = @timeouts[h]
|
148
|
+
timeout = timeouts.shift
|
136
149
|
|
137
|
-
|
138
|
-
|
139
|
-
timeout = (@timeouts[host][0] -= loop_time)
|
150
|
+
@timer = @current_selector.after(timeout) do
|
151
|
+
next unless @connections.include?(connection)
|
140
152
|
|
141
|
-
|
153
|
+
do_retry(h, connection, timeout)
|
154
|
+
end
|
155
|
+
end
|
142
156
|
|
143
|
-
|
157
|
+
def do_retry(h, connection, interval)
|
158
|
+
timeouts = @timeouts[h]
|
144
159
|
|
145
|
-
if
|
146
|
-
log
|
160
|
+
if !timeouts.empty?
|
161
|
+
log do
|
162
|
+
"resolver #{FAMILY_TYPES[@record_type]}: timeout after #{interval}s, retry (with #{timeouts.first}s) #{h}..."
|
163
|
+
end
|
147
164
|
# must downgrade to tcp AND retry on same host as last
|
148
165
|
downgrade_socket
|
149
166
|
resolve(connection, h)
|
150
167
|
elsif @ns_index + 1 < @nameserver.size
|
151
168
|
# try on the next nameserver
|
152
169
|
@ns_index += 1
|
153
|
-
log
|
170
|
+
log do
|
171
|
+
"resolver #{FAMILY_TYPES[@record_type]}: failed resolving #{h} on nameserver #{@nameserver[@ns_index - 1]} (timeout error)"
|
172
|
+
end
|
154
173
|
transition(:idle)
|
155
174
|
@timeouts.clear
|
156
175
|
resolve(connection, h)
|
157
176
|
else
|
158
177
|
|
159
|
-
@timeouts.delete(
|
178
|
+
@timeouts.delete(h)
|
160
179
|
reset_hostname(h, reset_candidates: false)
|
161
180
|
|
162
|
-
|
181
|
+
unless @queries.empty?
|
182
|
+
resolve(connection)
|
183
|
+
return
|
184
|
+
end
|
163
185
|
|
164
186
|
@connections.delete(connection)
|
187
|
+
|
188
|
+
host = connection.peer.host
|
189
|
+
|
165
190
|
# This loop_time passed to the exception is bogus. Ideally we would pass the total
|
166
191
|
# resolve timeout, including from the previous retries.
|
167
|
-
ex = ResolveTimeoutError.new(
|
192
|
+
ex = ResolveTimeoutError.new(interval, "Timed out while resolving #{host}")
|
168
193
|
ex.set_backtrace(ex ? ex.backtrace : caller)
|
169
194
|
emit_resolve_error(connection, host, ex)
|
170
|
-
|
195
|
+
|
196
|
+
close_or_resolve
|
171
197
|
end
|
172
198
|
end
|
173
199
|
|
@@ -216,7 +242,7 @@ module HTTPX
|
|
216
242
|
parse(@read_buffer)
|
217
243
|
end
|
218
244
|
|
219
|
-
return if @state == :closed
|
245
|
+
return if @state == :closed || !@write_buffer.empty?
|
220
246
|
end
|
221
247
|
end
|
222
248
|
|
@@ -234,11 +260,15 @@ module HTTPX
|
|
234
260
|
|
235
261
|
return unless siz.positive?
|
236
262
|
|
263
|
+
schedule_retry if @write_buffer.empty?
|
264
|
+
|
237
265
|
return if @state == :closed
|
238
266
|
end
|
239
267
|
end
|
240
268
|
|
241
269
|
def parse(buffer)
|
270
|
+
@timer.cancel
|
271
|
+
|
242
272
|
code, result = Resolver.decode_dns_answer(buffer)
|
243
273
|
|
244
274
|
case code
|
@@ -249,15 +279,17 @@ module HTTPX
|
|
249
279
|
hostname, connection = @queries.first
|
250
280
|
reset_hostname(hostname, reset_candidates: false)
|
251
281
|
|
252
|
-
|
282
|
+
other_candidate, _ = @queries.find { |_, conn| conn == connection }
|
283
|
+
|
284
|
+
if other_candidate
|
285
|
+
resolve(connection, other_candidate)
|
286
|
+
else
|
253
287
|
@connections.delete(connection)
|
254
288
|
ex = NativeResolveError.new(connection, connection.peer.host, "name or service not known")
|
255
289
|
ex.set_backtrace(ex ? ex.backtrace : caller)
|
256
290
|
emit_resolve_error(connection, connection.peer.host, ex)
|
257
|
-
|
291
|
+
close_or_resolve
|
258
292
|
end
|
259
|
-
|
260
|
-
resolve
|
261
293
|
when :message_truncated
|
262
294
|
# TODO: what to do if it's already tcp??
|
263
295
|
return if @socket_type == :tcp
|
@@ -312,8 +344,10 @@ module HTTPX
|
|
312
344
|
connection = @queries.delete(name)
|
313
345
|
end
|
314
346
|
|
315
|
-
|
316
|
-
|
347
|
+
alias_addresses, addresses = addresses.partition { |addr| addr.key?("alias") }
|
348
|
+
|
349
|
+
if addresses.empty? && !alias_addresses.empty? # CNAME
|
350
|
+
hostname_alias = alias_addresses.first["alias"]
|
317
351
|
# clean up intermediate queries
|
318
352
|
@timeouts.delete(name) unless connection.peer.host == name
|
319
353
|
|
@@ -326,7 +360,7 @@ module HTTPX
|
|
326
360
|
transition(:idle)
|
327
361
|
transition(:open)
|
328
362
|
end
|
329
|
-
log { "resolver: ALIAS #{hostname_alias} for #{name}" }
|
363
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: ALIAS #{hostname_alias} for #{name}" }
|
330
364
|
resolve(connection, hostname_alias)
|
331
365
|
return
|
332
366
|
end
|
@@ -338,12 +372,14 @@ module HTTPX
|
|
338
372
|
catch(:coalesced) { emit_addresses(connection, @family, addresses.map { |addr| addr["data"] }) }
|
339
373
|
end
|
340
374
|
end
|
341
|
-
|
342
|
-
|
343
|
-
resolve
|
375
|
+
close_or_resolve
|
344
376
|
end
|
345
377
|
|
346
|
-
def resolve(connection =
|
378
|
+
def resolve(connection = nil, hostname = nil)
|
379
|
+
@connections.shift until @connections.empty? || @connections.first.state != :closed
|
380
|
+
|
381
|
+
connection ||= @connections.find { |c| !@queries.value?(c) }
|
382
|
+
|
347
383
|
raise Error, "no URI to resolve" unless connection
|
348
384
|
|
349
385
|
return unless @write_buffer.empty?
|
@@ -352,7 +388,10 @@ module HTTPX
|
|
352
388
|
|
353
389
|
if hostname.nil?
|
354
390
|
hostname = connection.peer.host
|
355
|
-
log
|
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
|
356
395
|
|
357
396
|
hostname = generate_candidates(hostname).each do |name|
|
358
397
|
@queries[name] = connection
|
@@ -360,14 +399,17 @@ module HTTPX
|
|
360
399
|
else
|
361
400
|
@queries[hostname] = connection
|
362
401
|
end
|
363
|
-
|
402
|
+
|
403
|
+
@name = hostname
|
404
|
+
|
405
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: query for #{hostname}" }
|
364
406
|
begin
|
365
407
|
@write_buffer << encode_dns_query(hostname)
|
366
408
|
rescue Resolv::DNS::EncodeError => e
|
367
409
|
reset_hostname(hostname, connection: connection)
|
368
410
|
@connections.delete(connection)
|
369
411
|
emit_resolve_error(connection, hostname, e)
|
370
|
-
|
412
|
+
close_or_resolve
|
371
413
|
end
|
372
414
|
end
|
373
415
|
|
@@ -397,10 +439,10 @@ module HTTPX
|
|
397
439
|
|
398
440
|
case @socket_type
|
399
441
|
when :udp
|
400
|
-
log { "resolver: server: udp://#{ip}:#{port}..." }
|
442
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: server: udp://#{ip}:#{port}..." }
|
401
443
|
UDP.new(ip, port, @options)
|
402
444
|
when :tcp
|
403
|
-
log { "resolver: server: tcp://#{ip}:#{port}..." }
|
445
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: server: tcp://#{ip}:#{port}..." }
|
404
446
|
origin = URI("tcp://#{ip}:#{port}")
|
405
447
|
TCP.new(origin, [ip], @options)
|
406
448
|
end
|
@@ -448,6 +490,7 @@ module HTTPX
|
|
448
490
|
# these errors may happen during TCP handshake
|
449
491
|
# treat them as resolve errors.
|
450
492
|
handle_error(e)
|
493
|
+
emit(:close, self)
|
451
494
|
end
|
452
495
|
|
453
496
|
def handle_error(error)
|
@@ -462,13 +505,15 @@ module HTTPX
|
|
462
505
|
@connections.delete(connection)
|
463
506
|
emit_resolve_error(connection, host, error)
|
464
507
|
end
|
508
|
+
|
509
|
+
while (connection = @connections.shift)
|
510
|
+
emit_resolve_error(connection, host, error)
|
511
|
+
end
|
465
512
|
end
|
466
|
-
emit(:close, self) if @connections.empty?
|
467
513
|
end
|
468
514
|
|
469
515
|
def reset_hostname(hostname, connection: @queries.delete(hostname), reset_candidates: true)
|
470
516
|
@timeouts.delete(hostname)
|
471
|
-
@timeouts.delete(hostname)
|
472
517
|
|
473
518
|
return unless connection && reset_candidates
|
474
519
|
|
@@ -478,5 +523,16 @@ module HTTPX
|
|
478
523
|
# reset timeouts
|
479
524
|
@timeouts.delete_if { |h, _| candidates.include?(h) }
|
480
525
|
end
|
526
|
+
|
527
|
+
def close_or_resolve
|
528
|
+
# drop already closed connections
|
529
|
+
@connections.shift until @connections.empty? || @connections.first.state != :closed
|
530
|
+
|
531
|
+
if (@connections - @queries.values).empty?
|
532
|
+
emit(:close, self)
|
533
|
+
else
|
534
|
+
resolve
|
535
|
+
end
|
536
|
+
end
|
481
537
|
end
|
482
538
|
end
|
@@ -72,17 +72,22 @@ module HTTPX
|
|
72
72
|
# double emission check, but allow early resolution to work
|
73
73
|
return if !early_resolve && connection.addresses && !addresses.intersect?(connection.addresses)
|
74
74
|
|
75
|
-
log
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
log do
|
76
|
+
"resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: " \
|
77
|
+
"answer #{connection.peer.host}: #{addresses.inspect} (early resolve: #{early_resolve})"
|
78
|
+
end
|
79
|
+
|
80
|
+
if !early_resolve && # do not apply resolution delay for non-dns name resolution
|
81
|
+
@current_selector && # just in case...
|
82
|
+
family == Socket::AF_INET && # resolution delay only applies to IPv4
|
83
|
+
!connection.io && # connection already has addresses and initiated/ended handshake
|
84
|
+
connection.options.ip_families.size > 1 && # no need to delay if not supporting dual stack IP
|
85
|
+
addresses.first.to_s != connection.peer.host.to_s # connection URL host is already the IP (early resolve included perhaps?)
|
86
|
+
log { "resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: applying resolution delay..." }
|
87
|
+
|
82
88
|
@current_selector.after(0.05) do
|
83
|
-
|
84
|
-
|
85
|
-
(connection.addresses && addresses.intersect?(connection.addresses))
|
89
|
+
# double emission check
|
90
|
+
unless connection.addresses && addresses.intersect?(connection.addresses)
|
86
91
|
emit_resolved_connection(connection, addresses, early_resolve)
|
87
92
|
end
|
88
93
|
end
|
@@ -97,6 +102,8 @@ module HTTPX
|
|
97
102
|
begin
|
98
103
|
connection.addresses = addresses
|
99
104
|
|
105
|
+
return if connection.state == :closed
|
106
|
+
|
100
107
|
emit(:resolve, connection)
|
101
108
|
rescue StandardError => e
|
102
109
|
if early_resolve
|
@@ -146,7 +153,7 @@ module HTTPX
|
|
146
153
|
end
|
147
154
|
|
148
155
|
def emit_connection_error(connection, error)
|
149
|
-
return connection.
|
156
|
+
return connection.handle_connect_error(error) if connection.connecting?
|
150
157
|
|
151
158
|
connection.emit(:error, error)
|
152
159
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "forwardable"
|
4
3
|
require "resolv"
|
5
4
|
|
6
5
|
module HTTPX
|
7
6
|
class Resolver::System < Resolver::Resolver
|
8
7
|
using URIExtensions
|
9
|
-
extend Forwardable
|
10
8
|
|
11
9
|
RESOLV_ERRORS = [Resolv::ResolvError,
|
12
10
|
Resolv::DNS::Requester::RequestError,
|
@@ -24,8 +22,6 @@ module HTTPX
|
|
24
22
|
|
25
23
|
attr_reader :state
|
26
24
|
|
27
|
-
def_delegator :@connections, :empty?
|
28
|
-
|
29
25
|
def initialize(options)
|
30
26
|
super(nil, options)
|
31
27
|
@resolver_options = @options.resolver_options
|
@@ -162,7 +158,9 @@ module HTTPX
|
|
162
158
|
|
163
159
|
hostname = connection.peer.host
|
164
160
|
scheme = connection.origin.scheme
|
165
|
-
log
|
161
|
+
log do
|
162
|
+
"resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
|
163
|
+
end if connection.peer.non_ascii_hostname
|
166
164
|
|
167
165
|
transition(:open)
|
168
166
|
|
data/lib/httpx/selector.rb
CHANGED
@@ -19,6 +19,7 @@ module HTTPX
|
|
19
19
|
def initialize
|
20
20
|
@timers = Timers.new
|
21
21
|
@selectables = []
|
22
|
+
@is_timer_interval = false
|
22
23
|
end
|
23
24
|
|
24
25
|
def each(&blk)
|
@@ -43,7 +44,11 @@ module HTTPX
|
|
43
44
|
rescue StandardError => e
|
44
45
|
emit_error(e)
|
45
46
|
rescue Exception # rubocop:disable Lint/RescueException
|
46
|
-
each_connection
|
47
|
+
each_connection do |conn|
|
48
|
+
conn.force_reset
|
49
|
+
conn.disconnect
|
50
|
+
end
|
51
|
+
|
47
52
|
raise
|
48
53
|
end
|
49
54
|
|
@@ -92,10 +97,6 @@ module HTTPX
|
|
92
97
|
end
|
93
98
|
end
|
94
99
|
|
95
|
-
def empty?
|
96
|
-
@selectables.empty?
|
97
|
-
end
|
98
|
-
|
99
100
|
# deregisters +io+ from selectables.
|
100
101
|
def deregister(io)
|
101
102
|
@selectables.delete(io)
|
@@ -129,24 +130,22 @@ module HTTPX
|
|
129
130
|
# first, we group IOs based on interest type. On call to #interests however,
|
130
131
|
# things might already happen, and new IOs might be registered, so we might
|
131
132
|
# have to start all over again. We do this until we group all selectables
|
132
|
-
|
133
|
-
|
134
|
-
interests = io.interests
|
133
|
+
@selectables.delete_if do |io|
|
134
|
+
interests = io.interests
|
135
135
|
|
136
|
-
|
137
|
-
|
136
|
+
(r ||= []) << io if READABLE.include?(interests)
|
137
|
+
(w ||= []) << io if WRITABLE.include?(interests)
|
138
138
|
|
139
|
-
|
140
|
-
|
139
|
+
io.state == :closed
|
140
|
+
end
|
141
141
|
|
142
|
-
|
142
|
+
# TODO: what to do if there are no selectables?
|
143
143
|
|
144
|
-
|
144
|
+
readers, writers = IO.select(r, w, nil, interval)
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
146
|
+
if readers.nil? && writers.nil? && interval
|
147
|
+
[*r, *w].each { |io| io.handle_socket_timeout(interval) }
|
148
|
+
return
|
150
149
|
end
|
151
150
|
|
152
151
|
if writers
|
@@ -178,7 +177,7 @@ module HTTPX
|
|
178
177
|
end
|
179
178
|
|
180
179
|
unless result || interval.nil?
|
181
|
-
io.handle_socket_timeout(interval)
|
180
|
+
io.handle_socket_timeout(interval) unless @is_timer_interval
|
182
181
|
return
|
183
182
|
end
|
184
183
|
# raise TimeoutError.new(interval, "timed out while waiting on select")
|
@@ -190,10 +189,21 @@ module HTTPX
|
|
190
189
|
end
|
191
190
|
|
192
191
|
def next_timeout
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
192
|
+
@is_timer_interval = false
|
193
|
+
|
194
|
+
timer_interval = @timers.wait_interval
|
195
|
+
|
196
|
+
connection_interval = @selectables.filter_map(&:timeout).min
|
197
|
+
|
198
|
+
return connection_interval unless timer_interval
|
199
|
+
|
200
|
+
if connection_interval.nil? || timer_interval <= connection_interval
|
201
|
+
@is_timer_interval = true
|
202
|
+
|
203
|
+
return timer_interval
|
204
|
+
end
|
205
|
+
|
206
|
+
connection_interval
|
197
207
|
end
|
198
208
|
|
199
209
|
def emit_error(e)
|
data/lib/httpx/session.rb
CHANGED
@@ -69,8 +69,6 @@ module HTTPX
|
|
69
69
|
while (connection = @pool.pop_connection)
|
70
70
|
next if connection.state == :closed
|
71
71
|
|
72
|
-
connection.current_session = self
|
73
|
-
connection.current_selector = selector
|
74
72
|
select_connection(connection, selector)
|
75
73
|
end
|
76
74
|
begin
|
@@ -126,9 +124,15 @@ module HTTPX
|
|
126
124
|
end
|
127
125
|
|
128
126
|
def select_connection(connection, selector)
|
127
|
+
pin_connection(connection, selector)
|
129
128
|
selector.register(connection)
|
130
129
|
end
|
131
130
|
|
131
|
+
def pin_connection(connection, selector)
|
132
|
+
connection.current_session = self
|
133
|
+
connection.current_selector = selector
|
134
|
+
end
|
135
|
+
|
132
136
|
alias_method :select_resolver, :select_connection
|
133
137
|
|
134
138
|
def deselect_connection(connection, selector, cloned = false)
|
@@ -160,36 +164,8 @@ module HTTPX
|
|
160
164
|
new_connection = connection.class.new(connection.origin, connection.options)
|
161
165
|
|
162
166
|
new_connection.family = family
|
163
|
-
new_connection.current_session = self
|
164
|
-
new_connection.current_selector = selector
|
165
|
-
|
166
|
-
connection.once(:tcp_open) { new_connection.force_reset(true) }
|
167
|
-
connection.once(:connect_error) do |err|
|
168
|
-
if new_connection.connecting?
|
169
|
-
new_connection.merge(connection)
|
170
|
-
connection.emit(:cloned, new_connection)
|
171
|
-
connection.force_reset(true)
|
172
|
-
else
|
173
|
-
connection.__send__(:handle_error, err)
|
174
|
-
end
|
175
|
-
end
|
176
167
|
|
177
|
-
|
178
|
-
if new_conn != connection
|
179
|
-
new_conn.merge(connection)
|
180
|
-
connection.force_reset(true)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
new_connection.once(:connect_error) do |err|
|
184
|
-
if connection.connecting?
|
185
|
-
# main connection has the requests
|
186
|
-
connection.merge(new_connection)
|
187
|
-
new_connection.emit(:cloned, connection)
|
188
|
-
new_connection.force_reset(true)
|
189
|
-
else
|
190
|
-
new_connection.__send__(:handle_error, err)
|
191
|
-
end
|
192
|
-
end
|
168
|
+
connection.sibling = new_connection
|
193
169
|
|
194
170
|
do_init_connection(new_connection, selector)
|
195
171
|
new_connection
|
@@ -203,14 +179,15 @@ module HTTPX
|
|
203
179
|
|
204
180
|
connection = @pool.checkout_connection(request_uri, options)
|
205
181
|
|
206
|
-
connection.current_session = self
|
207
|
-
connection.current_selector = selector
|
208
|
-
|
209
182
|
case connection.state
|
210
183
|
when :idle
|
211
184
|
do_init_connection(connection, selector)
|
212
185
|
when :open
|
213
|
-
|
186
|
+
if options.io
|
187
|
+
select_connection(connection, selector)
|
188
|
+
else
|
189
|
+
pin_connection(connection, selector)
|
190
|
+
end
|
214
191
|
when :closed
|
215
192
|
connection.idling
|
216
193
|
select_connection(connection, selector)
|
@@ -219,6 +196,8 @@ module HTTPX
|
|
219
196
|
connection.idling
|
220
197
|
select_connection(connection, selector)
|
221
198
|
end
|
199
|
+
else
|
200
|
+
pin_connection(connection, selector)
|
222
201
|
end
|
223
202
|
|
224
203
|
connection
|
@@ -261,11 +240,9 @@ module HTTPX
|
|
261
240
|
end
|
262
241
|
return unless error && error.is_a?(Exception)
|
263
242
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
raise error if selector.empty?
|
268
|
-
end
|
243
|
+
raise error unless error.is_a?(Error)
|
244
|
+
|
245
|
+
request.emit(:response, ErrorResponse.new(request, error))
|
269
246
|
end
|
270
247
|
|
271
248
|
# returns a set of HTTPX::Request objects built from the given +args+ and +options+.
|
@@ -372,7 +349,6 @@ module HTTPX
|
|
372
349
|
# resolve a name (not the same as name being an IP, yet)
|
373
350
|
# 2. when the connection is initialized with an external already open IO.
|
374
351
|
#
|
375
|
-
connection.once(:connect_error, &connection.method(:handle_error))
|
376
352
|
on_resolver_connection(connection, selector)
|
377
353
|
return
|
378
354
|
end
|
@@ -428,8 +404,6 @@ module HTTPX
|
|
428
404
|
return false
|
429
405
|
end
|
430
406
|
|
431
|
-
conn2.emit(:tcp_open, conn1)
|
432
|
-
conn1.merge(conn2)
|
433
407
|
conn2.coalesced_connection = conn1
|
434
408
|
select_connection(conn1, selector) if from_pool
|
435
409
|
deselect_connection(conn2, selector)
|
data/lib/httpx/timers.rb
CHANGED
@@ -26,7 +26,7 @@ module HTTPX
|
|
26
26
|
|
27
27
|
@next_interval_at = nil
|
28
28
|
|
29
|
-
interval
|
29
|
+
Timer.new(interval, callback)
|
30
30
|
end
|
31
31
|
|
32
32
|
def wait_interval
|
@@ -48,6 +48,17 @@ module HTTPX
|
|
48
48
|
@next_interval_at = nil if @intervals.empty?
|
49
49
|
end
|
50
50
|
|
51
|
+
class Timer
|
52
|
+
def initialize(interval, callback)
|
53
|
+
@interval = interval
|
54
|
+
@callback = callback
|
55
|
+
end
|
56
|
+
|
57
|
+
def cancel
|
58
|
+
@interval.delete(@callback)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
51
62
|
class Interval
|
52
63
|
include Comparable
|
53
64
|
|
@@ -63,6 +74,10 @@ module HTTPX
|
|
63
74
|
@on_empty = blk
|
64
75
|
end
|
65
76
|
|
77
|
+
def cancel
|
78
|
+
@on_empty.call
|
79
|
+
end
|
80
|
+
|
66
81
|
def <=>(other)
|
67
82
|
@interval <=> other.interval
|
68
83
|
end
|