httpx 1.6.2 → 1.6.3
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/doc/release_notes/1_6_3.md +47 -0
- data/lib/httpx/adapters/sentry.rb +1 -1
- data/lib/httpx/connection/http1.rb +9 -9
- data/lib/httpx/connection/http2.rb +14 -15
- data/lib/httpx/connection.rb +115 -102
- data/lib/httpx/extensions.rb +0 -14
- data/lib/httpx/io/ssl.rb +1 -1
- data/lib/httpx/loggable.rb +12 -2
- data/lib/httpx/options.rb +20 -0
- data/lib/httpx/plugins/callbacks.rb +15 -1
- data/lib/httpx/plugins/digest_auth.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +37 -9
- data/lib/httpx/plugins/response_cache/file_store.rb +1 -0
- data/lib/httpx/plugins/response_cache.rb +13 -2
- data/lib/httpx/plugins/stream_bidi.rb +15 -6
- data/lib/httpx/pool.rb +53 -19
- data/lib/httpx/request.rb +3 -13
- data/lib/httpx/resolver/https.rb +35 -19
- data/lib/httpx/resolver/multi.rb +8 -27
- data/lib/httpx/resolver/native.rb +46 -38
- data/lib/httpx/resolver/resolver.rb +45 -28
- data/lib/httpx/resolver/system.rb +63 -39
- data/lib/httpx/selector.rb +35 -20
- data/lib/httpx/session.rb +18 -28
- data/lib/httpx/transcoder/deflate.rb +13 -8
- data/lib/httpx/transcoder/utils/body_reader.rb +1 -2
- data/lib/httpx/transcoder/utils/deflater.rb +1 -2
- data/lib/httpx/version.rb +1 -1
- data/sig/connection.rbs +12 -3
- data/sig/loggable.rbs +5 -1
- data/sig/options.rbs +5 -1
- data/sig/plugins/callbacks.rbs +3 -0
- data/sig/plugins/stream_bidi.rbs +3 -5
- data/sig/resolver/https.rbs +2 -0
- data/sig/resolver/multi.rbs +0 -9
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +9 -8
- data/sig/resolver/system.rbs +4 -2
- data/sig/selector.rbs +2 -0
- data/sig/session.rbs +5 -3
- metadata +3 -1
|
@@ -49,8 +49,19 @@ module HTTPX
|
|
|
49
49
|
transition(:closed)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def force_close(*)
|
|
53
|
+
@timer.cancel if @timer
|
|
54
|
+
@timer = @name = nil
|
|
55
|
+
@queries.clear
|
|
56
|
+
@timeouts.clear
|
|
57
|
+
close
|
|
58
|
+
super
|
|
59
|
+
ensure
|
|
60
|
+
terminate
|
|
61
|
+
end
|
|
62
|
+
|
|
52
63
|
def terminate
|
|
53
|
-
|
|
64
|
+
disconnect
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
def closed?
|
|
@@ -84,7 +95,7 @@ module HTTPX
|
|
|
84
95
|
if @nameserver.nil?
|
|
85
96
|
ex = ResolveError.new("No available nameserver")
|
|
86
97
|
ex.set_backtrace(caller)
|
|
87
|
-
connection.
|
|
98
|
+
connection.force_close
|
|
88
99
|
throw(:resolve_error, ex)
|
|
89
100
|
else
|
|
90
101
|
@connections << connection
|
|
@@ -93,15 +104,34 @@ module HTTPX
|
|
|
93
104
|
end
|
|
94
105
|
|
|
95
106
|
def timeout
|
|
96
|
-
return
|
|
107
|
+
return unless @name
|
|
97
108
|
|
|
98
109
|
@start_timeout = Utils.now
|
|
99
|
-
|
|
100
|
-
@timeouts
|
|
110
|
+
|
|
111
|
+
timeouts = @timeouts[@name]
|
|
112
|
+
|
|
113
|
+
return if timeouts.empty?
|
|
114
|
+
|
|
115
|
+
log(level: 2) { "resolver #{FAMILY_TYPES[@record_type]}: next timeout #{timeouts.first} secs... (#{timeouts.size - 1} left)" }
|
|
116
|
+
|
|
117
|
+
timeouts.first
|
|
101
118
|
end
|
|
102
119
|
|
|
103
120
|
def handle_socket_timeout(interval); end
|
|
104
121
|
|
|
122
|
+
def handle_error(error)
|
|
123
|
+
if error.respond_to?(:connection) &&
|
|
124
|
+
error.respond_to?(:host)
|
|
125
|
+
reset_hostname(error.host, connection: error.connection)
|
|
126
|
+
else
|
|
127
|
+
@queries.each do |host, connection|
|
|
128
|
+
reset_hostname(host, connection: connection)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
super
|
|
133
|
+
end
|
|
134
|
+
|
|
105
135
|
private
|
|
106
136
|
|
|
107
137
|
def calculate_interests
|
|
@@ -118,7 +148,6 @@ module HTTPX
|
|
|
118
148
|
|
|
119
149
|
break unless calculate_interests == :w
|
|
120
150
|
|
|
121
|
-
# do_retry
|
|
122
151
|
dwrite
|
|
123
152
|
|
|
124
153
|
break unless calculate_interests == :r
|
|
@@ -133,7 +162,7 @@ module HTTPX
|
|
|
133
162
|
retry
|
|
134
163
|
else
|
|
135
164
|
handle_error(e)
|
|
136
|
-
|
|
165
|
+
disconnect
|
|
137
166
|
end
|
|
138
167
|
rescue NativeResolveError => e
|
|
139
168
|
handle_error(e)
|
|
@@ -154,7 +183,7 @@ module HTTPX
|
|
|
154
183
|
@timer = @current_selector.after(timeout) do
|
|
155
184
|
next unless @connections.include?(connection)
|
|
156
185
|
|
|
157
|
-
@timer = nil
|
|
186
|
+
@timer = @name = nil
|
|
158
187
|
|
|
159
188
|
do_retry(h, connection, timeout)
|
|
160
189
|
end
|
|
@@ -178,8 +207,6 @@ module HTTPX
|
|
|
178
207
|
@timeouts.clear
|
|
179
208
|
resolve(connection, h)
|
|
180
209
|
else
|
|
181
|
-
|
|
182
|
-
@timeouts.delete(h)
|
|
183
210
|
reset_hostname(h, reset_candidates: false)
|
|
184
211
|
|
|
185
212
|
unless @queries.empty?
|
|
@@ -273,7 +300,7 @@ module HTTPX
|
|
|
273
300
|
def parse(buffer)
|
|
274
301
|
@timer.cancel
|
|
275
302
|
|
|
276
|
-
@timer = nil
|
|
303
|
+
@timer = @name = nil
|
|
277
304
|
|
|
278
305
|
code, result = Resolver.decode_dns_answer(buffer)
|
|
279
306
|
|
|
@@ -431,13 +458,14 @@ module HTTPX
|
|
|
431
458
|
def generate_candidates(name)
|
|
432
459
|
return [name] if name.end_with?(".")
|
|
433
460
|
|
|
434
|
-
candidates = []
|
|
435
461
|
name_parts = name.scan(/[^.]+/)
|
|
436
|
-
candidates =
|
|
437
|
-
candidates.concat(@search.map { |domain| [*name_parts, *domain].join(".") })
|
|
462
|
+
candidates = @search.map { |domain| [*name_parts, *domain].join(".") }
|
|
438
463
|
fname = "#{name}."
|
|
439
|
-
|
|
440
|
-
|
|
464
|
+
if @ndots <= name_parts.size - 1
|
|
465
|
+
candidates.unshift(fname)
|
|
466
|
+
else
|
|
467
|
+
candidates << fname
|
|
468
|
+
end
|
|
441
469
|
candidates
|
|
442
470
|
end
|
|
443
471
|
|
|
@@ -498,27 +526,7 @@ module HTTPX
|
|
|
498
526
|
ConnectTimeoutError => e
|
|
499
527
|
# these errors may happen during TCP handshake
|
|
500
528
|
# treat them as resolve errors.
|
|
501
|
-
|
|
502
|
-
emit(:close, self)
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
def handle_error(error)
|
|
506
|
-
if error.respond_to?(:connection) &&
|
|
507
|
-
error.respond_to?(:host)
|
|
508
|
-
reset_hostname(error.host, connection: error.connection)
|
|
509
|
-
@connections.delete(error.connection)
|
|
510
|
-
emit_resolve_error(error.connection, error.host, error)
|
|
511
|
-
else
|
|
512
|
-
@queries.each do |host, connection|
|
|
513
|
-
reset_hostname(host, connection: connection)
|
|
514
|
-
@connections.delete(connection)
|
|
515
|
-
emit_resolve_error(connection, host, error)
|
|
516
|
-
end
|
|
517
|
-
|
|
518
|
-
while (connection = @connections.shift)
|
|
519
|
-
emit_resolve_error(connection, connection.peer.host, error)
|
|
520
|
-
end
|
|
521
|
-
end
|
|
529
|
+
on_error(e)
|
|
522
530
|
end
|
|
523
531
|
|
|
524
532
|
def reset_hostname(hostname, connection: @queries.delete(hostname), reset_candidates: true)
|
|
@@ -538,7 +546,7 @@ module HTTPX
|
|
|
538
546
|
@connections.shift until @connections.empty? || @connections.first.state != :closed
|
|
539
547
|
|
|
540
548
|
if (@connections - @queries.values).empty?
|
|
541
|
-
|
|
549
|
+
disconnect
|
|
542
550
|
else
|
|
543
551
|
resolve
|
|
544
552
|
end
|
|
@@ -7,7 +7,6 @@ module HTTPX
|
|
|
7
7
|
# from the Selectable API.
|
|
8
8
|
#
|
|
9
9
|
class Resolver::Resolver
|
|
10
|
-
include Callbacks
|
|
11
10
|
include Loggable
|
|
12
11
|
|
|
13
12
|
using ArrayExtensions::Intersect
|
|
@@ -39,8 +38,6 @@ module HTTPX
|
|
|
39
38
|
@record_type = RECORD_TYPES[family]
|
|
40
39
|
@options = options
|
|
41
40
|
@connections = []
|
|
42
|
-
|
|
43
|
-
set_resolver_callbacks
|
|
44
41
|
end
|
|
45
42
|
|
|
46
43
|
def each_connection(&block)
|
|
@@ -55,6 +52,12 @@ module HTTPX
|
|
|
55
52
|
|
|
56
53
|
alias_method :terminate, :close
|
|
57
54
|
|
|
55
|
+
def force_close(*args)
|
|
56
|
+
while (connection = @connections.shift)
|
|
57
|
+
connection.force_close(*args)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
58
61
|
def closed?
|
|
59
62
|
true
|
|
60
63
|
end
|
|
@@ -72,7 +75,7 @@ module HTTPX
|
|
|
72
75
|
|
|
73
76
|
# double emission check, but allow early resolution to work
|
|
74
77
|
conn_addrs = connection.addresses
|
|
75
|
-
return if !early_resolve && conn_addrs && (!conn_addrs.empty? && !addresses.intersect?(
|
|
78
|
+
return if !early_resolve && conn_addrs && (!conn_addrs.empty? && !addresses.intersect?(conn_addrs))
|
|
76
79
|
|
|
77
80
|
log do
|
|
78
81
|
"resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: " \
|
|
@@ -104,25 +107,23 @@ module HTTPX
|
|
|
104
107
|
end
|
|
105
108
|
end
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
connection.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
emit(:resolve, connection)
|
|
116
|
-
rescue StandardError => e
|
|
117
|
-
if early_resolve
|
|
118
|
-
connection.force_reset
|
|
119
|
-
throw(:resolve_error, e)
|
|
120
|
-
else
|
|
121
|
-
emit(:error, connection, e)
|
|
110
|
+
def handle_error(error)
|
|
111
|
+
if error.respond_to?(:connection) &&
|
|
112
|
+
error.respond_to?(:host)
|
|
113
|
+
@connections.delete(error.connection)
|
|
114
|
+
emit_resolve_error(error.connection, error.host, error)
|
|
115
|
+
else
|
|
116
|
+
while (connection = @connections.shift)
|
|
117
|
+
emit_resolve_error(connection, connection.peer.host, error)
|
|
122
118
|
end
|
|
123
119
|
end
|
|
124
120
|
end
|
|
125
121
|
|
|
122
|
+
def on_error(error)
|
|
123
|
+
handle_error(error)
|
|
124
|
+
disconnect
|
|
125
|
+
end
|
|
126
|
+
|
|
126
127
|
def early_resolve(connection, hostname: connection.peer.host)
|
|
127
128
|
addresses = @resolver_options[:cache] && (connection.addresses || HTTPX::Resolver.nolookup_resolve(hostname))
|
|
128
129
|
|
|
@@ -137,6 +138,25 @@ module HTTPX
|
|
|
137
138
|
true
|
|
138
139
|
end
|
|
139
140
|
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def emit_resolved_connection(connection, addresses, early_resolve)
|
|
144
|
+
begin
|
|
145
|
+
connection.addresses = addresses
|
|
146
|
+
|
|
147
|
+
return if connection.state == :closed
|
|
148
|
+
|
|
149
|
+
resolve_connection(connection)
|
|
150
|
+
rescue StandardError => e
|
|
151
|
+
if early_resolve
|
|
152
|
+
connection.force_close
|
|
153
|
+
throw(:resolve_error, e)
|
|
154
|
+
else
|
|
155
|
+
emit_connection_error(connection, e)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
140
160
|
def emit_resolve_error(connection, hostname = connection.peer.host, ex = nil)
|
|
141
161
|
emit_connection_error(connection, resolve_error(hostname, ex))
|
|
142
162
|
end
|
|
@@ -150,12 +170,6 @@ module HTTPX
|
|
|
150
170
|
error
|
|
151
171
|
end
|
|
152
172
|
|
|
153
|
-
def set_resolver_callbacks
|
|
154
|
-
on(:resolve, &method(:resolve_connection))
|
|
155
|
-
on(:error, &method(:emit_connection_error))
|
|
156
|
-
on(:close, &method(:close_resolver))
|
|
157
|
-
end
|
|
158
|
-
|
|
159
173
|
def resolve_connection(connection)
|
|
160
174
|
@current_session.__send__(:on_resolver_connection, connection, @current_selector)
|
|
161
175
|
end
|
|
@@ -163,11 +177,14 @@ module HTTPX
|
|
|
163
177
|
def emit_connection_error(connection, error)
|
|
164
178
|
return connection.handle_connect_error(error) if connection.connecting?
|
|
165
179
|
|
|
166
|
-
connection.
|
|
180
|
+
connection.on_error(error)
|
|
167
181
|
end
|
|
168
182
|
|
|
169
|
-
def
|
|
170
|
-
|
|
183
|
+
def disconnect
|
|
184
|
+
return if closed?
|
|
185
|
+
|
|
186
|
+
close
|
|
187
|
+
@current_session.deselect_resolver(self, @current_selector)
|
|
171
188
|
end
|
|
172
189
|
end
|
|
173
190
|
end
|
|
@@ -56,13 +56,21 @@ module HTTPX
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def empty?
|
|
59
|
-
|
|
59
|
+
@connections.empty?
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def close
|
|
63
63
|
transition(:closed)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def force_close(*)
|
|
67
|
+
close
|
|
68
|
+
@queries.clear
|
|
69
|
+
@timeouts.clear
|
|
70
|
+
@ips.clear
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
66
74
|
def closed?
|
|
67
75
|
@state == :closed
|
|
68
76
|
end
|
|
@@ -86,36 +94,42 @@ module HTTPX
|
|
|
86
94
|
end
|
|
87
95
|
|
|
88
96
|
def timeout
|
|
89
|
-
return unless @queries.empty?
|
|
90
|
-
|
|
91
97
|
_, connection = @queries.first
|
|
92
98
|
|
|
93
99
|
return unless connection
|
|
94
100
|
|
|
95
|
-
@timeouts[connection.peer.host]
|
|
101
|
+
timeouts = @timeouts[connection.peer.host]
|
|
102
|
+
|
|
103
|
+
return if timeouts.empty?
|
|
104
|
+
|
|
105
|
+
log(level: 2) { "resolver #{FAMILY_TYPES[@record_type]}: next timeout #{timeouts.first} secs... (#{timeouts.size - 1} left)" }
|
|
106
|
+
|
|
107
|
+
timeouts.first
|
|
96
108
|
end
|
|
97
109
|
|
|
98
|
-
def
|
|
110
|
+
def lazy_resolve(connection)
|
|
99
111
|
@connections << connection
|
|
100
112
|
resolve
|
|
101
|
-
end
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
return if empty?
|
|
115
|
+
|
|
116
|
+
@current_session.select_resolver(self, @current_selector)
|
|
106
117
|
end
|
|
107
118
|
|
|
119
|
+
def early_resolve(connection, **); end
|
|
120
|
+
|
|
108
121
|
def handle_socket_timeout(interval)
|
|
109
122
|
error = HTTPX::ResolveTimeoutError.new(interval, "timed out while waiting on select")
|
|
110
123
|
error.set_backtrace(caller)
|
|
111
|
-
@queries.each do |
|
|
112
|
-
@connections.delete(connection)
|
|
113
|
-
emit_resolve_error(connection, host, error)
|
|
124
|
+
@queries.each do |_, connection| # rubocop:disable Style/HashEachMethods
|
|
125
|
+
emit_resolve_error(connection, connection.peer.host, error) if @connections.delete(connection)
|
|
114
126
|
end
|
|
115
127
|
|
|
116
128
|
while (connection = @connections.shift)
|
|
117
129
|
emit_resolve_error(connection, connection.peer.host, error)
|
|
118
130
|
end
|
|
131
|
+
|
|
132
|
+
close_or_resolve
|
|
119
133
|
end
|
|
120
134
|
|
|
121
135
|
private
|
|
@@ -140,34 +154,38 @@ module HTTPX
|
|
|
140
154
|
def consume
|
|
141
155
|
return if @connections.empty?
|
|
142
156
|
|
|
143
|
-
|
|
144
|
-
event = @pipe_read.getbyte
|
|
157
|
+
event = @pipe_read.read_nonblock(1, exception: false)
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
when DONE
|
|
148
|
-
*pair, addrs = @pipe_mutex.synchronize { @ips.pop }
|
|
149
|
-
if pair
|
|
150
|
-
@queries.delete(pair)
|
|
151
|
-
family, connection = pair
|
|
152
|
-
@connections.delete(connection)
|
|
159
|
+
return if event == :wait_readable
|
|
153
160
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
raise ResolveError, "socket pipe closed unexpectedly" if event.nil?
|
|
162
|
+
|
|
163
|
+
case event.unpack1("C")
|
|
164
|
+
when DONE
|
|
165
|
+
*pair, addrs = @pipe_mutex.synchronize { @ips.pop }
|
|
166
|
+
if pair
|
|
167
|
+
@queries.delete(pair)
|
|
168
|
+
family, connection = pair
|
|
169
|
+
@connections.delete(connection)
|
|
170
|
+
|
|
171
|
+
catch(:coalesced) { emit_addresses(connection, family, addrs) }
|
|
172
|
+
end
|
|
173
|
+
when ERROR
|
|
174
|
+
*pair, error = @pipe_mutex.synchronize { @ips.pop }
|
|
175
|
+
if pair && error
|
|
176
|
+
@queries.delete(pair)
|
|
177
|
+
_, connection = pair
|
|
178
|
+
@connections.delete(connection)
|
|
179
|
+
|
|
180
|
+
emit_resolve_error(connection, connection.peer.host, error)
|
|
165
181
|
end
|
|
166
182
|
end
|
|
167
183
|
|
|
168
|
-
return
|
|
184
|
+
return disconnect if @connections.empty?
|
|
169
185
|
|
|
170
186
|
resolve
|
|
187
|
+
rescue StandardError => e
|
|
188
|
+
on_error(e)
|
|
171
189
|
end
|
|
172
190
|
|
|
173
191
|
def resolve(connection = nil, hostname = nil)
|
|
@@ -240,16 +258,22 @@ module HTTPX
|
|
|
240
258
|
end
|
|
241
259
|
end
|
|
242
260
|
end
|
|
261
|
+
Thread.pass
|
|
243
262
|
end
|
|
244
263
|
|
|
245
|
-
def
|
|
246
|
-
|
|
247
|
-
|
|
264
|
+
def close_or_resolve
|
|
265
|
+
# drop already closed connections
|
|
266
|
+
@connections.shift until @connections.empty? || @connections.first.state != :closed
|
|
248
267
|
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
if (@connections - @queries.map(&:last)).empty?
|
|
269
|
+
disconnect
|
|
270
|
+
else
|
|
271
|
+
resolve
|
|
272
|
+
end
|
|
251
273
|
end
|
|
252
274
|
|
|
253
|
-
def
|
|
275
|
+
def __addrinfo_resolve(host, scheme)
|
|
276
|
+
Addrinfo.getaddrinfo(host, scheme, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
|
|
277
|
+
end
|
|
254
278
|
end
|
|
255
279
|
end
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -51,7 +51,7 @@ module HTTPX
|
|
|
51
51
|
|
|
52
52
|
begin
|
|
53
53
|
select(timeout) do |c|
|
|
54
|
-
c.log(level: 2) { "[#{c.state}] selected#{" after #{timeout} secs" unless timeout.nil?}..." }
|
|
54
|
+
c.log(level: 2) { "[#{c.state}] selected from selector##{object_id} #{" after #{timeout} secs" unless timeout.nil?}..." }
|
|
55
55
|
|
|
56
56
|
c.call
|
|
57
57
|
end
|
|
@@ -61,17 +61,6 @@ module HTTPX
|
|
|
61
61
|
@timers.fire(e)
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
-
rescue StandardError => e
|
|
65
|
-
each_connection do |c|
|
|
66
|
-
c.emit(:error, e)
|
|
67
|
-
end
|
|
68
|
-
rescue Exception # rubocop:disable Lint/RescueException
|
|
69
|
-
each_connection do |conn|
|
|
70
|
-
conn.force_reset
|
|
71
|
-
conn.disconnect
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
raise
|
|
75
64
|
end
|
|
76
65
|
|
|
77
66
|
def terminate
|
|
@@ -157,7 +146,9 @@ module HTTPX
|
|
|
157
146
|
|
|
158
147
|
next(is_closed) if is_closed
|
|
159
148
|
|
|
160
|
-
io.log(level: 2)
|
|
149
|
+
io.log(level: 2) do
|
|
150
|
+
"[#{io.state}] registering in selector##{object_id} for select (#{interests})#{" for #{interval} seconds" unless interval.nil?}"
|
|
151
|
+
end
|
|
161
152
|
|
|
162
153
|
if READABLE.include?(interests)
|
|
163
154
|
r = r.nil? ? io : (Array(r) << io)
|
|
@@ -206,7 +197,21 @@ module HTTPX
|
|
|
206
197
|
end
|
|
207
198
|
|
|
208
199
|
def select_many(r, w, interval, &block)
|
|
209
|
-
|
|
200
|
+
begin
|
|
201
|
+
readers, writers = ::IO.select(r, w, nil, interval)
|
|
202
|
+
rescue StandardError => e
|
|
203
|
+
(Array(r) + Array(w)).each do |sel|
|
|
204
|
+
sel.on_error(e)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
return
|
|
208
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
209
|
+
(Array(r) + Array(w)).each do |sel|
|
|
210
|
+
sel.force_close(true)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
raise e
|
|
214
|
+
end
|
|
210
215
|
|
|
211
216
|
if readers.nil? && writers.nil? && interval
|
|
212
217
|
[*r, *w].each { |io| io.handle_socket_timeout(interval) }
|
|
@@ -228,12 +233,22 @@ module HTTPX
|
|
|
228
233
|
end
|
|
229
234
|
|
|
230
235
|
def select_one(io, interests, interval)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
begin
|
|
237
|
+
result =
|
|
238
|
+
case interests
|
|
239
|
+
when :r then io.to_io.wait_readable(interval)
|
|
240
|
+
when :w then io.to_io.wait_writable(interval)
|
|
241
|
+
when :rw then rw_wait(io, interval)
|
|
242
|
+
end
|
|
243
|
+
rescue StandardError => e
|
|
244
|
+
io.on_error(e)
|
|
245
|
+
|
|
246
|
+
return
|
|
247
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
248
|
+
io.force_close(true)
|
|
249
|
+
|
|
250
|
+
raise e
|
|
251
|
+
end
|
|
237
252
|
|
|
238
253
|
unless result || interval.nil?
|
|
239
254
|
io.handle_socket_timeout(interval) unless @is_timer_interval
|
data/lib/httpx/session.rb
CHANGED
|
@@ -120,13 +120,16 @@ module HTTPX
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def select_connection(connection, selector)
|
|
123
|
-
|
|
123
|
+
pin(connection, selector)
|
|
124
|
+
connection.log(level: 2) do
|
|
125
|
+
"registering into selector##{selector.object_id}"
|
|
126
|
+
end
|
|
124
127
|
selector.register(connection)
|
|
125
128
|
end
|
|
126
129
|
|
|
127
|
-
def
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
def pin(conn_or_resolver, selector)
|
|
131
|
+
conn_or_resolver.current_session = self
|
|
132
|
+
conn_or_resolver.current_selector = selector
|
|
130
133
|
end
|
|
131
134
|
|
|
132
135
|
alias_method :select_resolver, :select_connection
|
|
@@ -137,9 +140,6 @@ module HTTPX
|
|
|
137
140
|
end
|
|
138
141
|
selector.deregister(connection)
|
|
139
142
|
|
|
140
|
-
# when connections coalesce
|
|
141
|
-
return if connection.state == :idle
|
|
142
|
-
|
|
143
143
|
return if cloned
|
|
144
144
|
|
|
145
145
|
return if @closing && connection.state == :closed
|
|
@@ -194,7 +194,7 @@ module HTTPX
|
|
|
194
194
|
if options.io
|
|
195
195
|
select_connection(connection, selector)
|
|
196
196
|
else
|
|
197
|
-
|
|
197
|
+
pin(connection, selector)
|
|
198
198
|
end
|
|
199
199
|
when :closing, :closed
|
|
200
200
|
connection.idling
|
|
@@ -205,7 +205,7 @@ module HTTPX
|
|
|
205
205
|
resolve_connection(connection, selector)
|
|
206
206
|
end
|
|
207
207
|
else
|
|
208
|
-
|
|
208
|
+
pin(connection, selector)
|
|
209
209
|
end
|
|
210
210
|
|
|
211
211
|
connection
|
|
@@ -224,13 +224,7 @@ module HTTPX
|
|
|
224
224
|
|
|
225
225
|
# tries deactivating connections in the +selector+, deregistering the ones that have been deactivated.
|
|
226
226
|
def deactivate(selector)
|
|
227
|
-
selector.each_connection.
|
|
228
|
-
c.deactivate
|
|
229
|
-
|
|
230
|
-
c.state == :inactive
|
|
231
|
-
end.each do |c| # rubocop:disable Style/MultilineBlockChain
|
|
232
|
-
deselect_connection(c, selector)
|
|
233
|
-
end
|
|
227
|
+
selector.each_connection.each(&:deactivate)
|
|
234
228
|
end
|
|
235
229
|
|
|
236
230
|
# callback executed when an HTTP/2 promise frame has been received.
|
|
@@ -395,6 +389,7 @@ module HTTPX
|
|
|
395
389
|
|
|
396
390
|
resolver = find_resolver_for(connection, selector)
|
|
397
391
|
|
|
392
|
+
pin(connection, selector)
|
|
398
393
|
resolver.early_resolve(connection) || resolver.lazy_resolve(connection)
|
|
399
394
|
end
|
|
400
395
|
|
|
@@ -402,6 +397,9 @@ module HTTPX
|
|
|
402
397
|
from_pool = false
|
|
403
398
|
found_connection = selector.find_mergeable_connection(connection) || begin
|
|
404
399
|
from_pool = true
|
|
400
|
+
connection.log(level: 2) do
|
|
401
|
+
"try finding a mergeable connection in pool##{@pool.object_id}"
|
|
402
|
+
end
|
|
405
403
|
@pool.checkout_mergeable_connection(connection)
|
|
406
404
|
end
|
|
407
405
|
|
|
@@ -409,19 +407,12 @@ module HTTPX
|
|
|
409
407
|
|
|
410
408
|
connection.log(level: 2) do
|
|
411
409
|
"try coalescing from #{from_pool ? "pool##{@pool.object_id}" : "selector##{selector.object_id}"} " \
|
|
412
|
-
"(
|
|
410
|
+
"(connection##{found_connection.object_id}[#{found_connection.origin}])"
|
|
413
411
|
end
|
|
414
412
|
|
|
415
413
|
coalesce_connections(found_connection, connection, selector, from_pool)
|
|
416
414
|
end
|
|
417
415
|
|
|
418
|
-
def on_resolver_close(resolver, selector)
|
|
419
|
-
return if resolver.closed?
|
|
420
|
-
|
|
421
|
-
deselect_resolver(resolver, selector)
|
|
422
|
-
resolver.close unless resolver.closed?
|
|
423
|
-
end
|
|
424
|
-
|
|
425
416
|
def find_resolver_for(connection, selector)
|
|
426
417
|
if (resolver = selector.find_resolver(connection.options))
|
|
427
418
|
resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
|
|
@@ -430,8 +421,7 @@ module HTTPX
|
|
|
430
421
|
|
|
431
422
|
resolver = @pool.checkout_resolver(connection.options)
|
|
432
423
|
resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
|
|
433
|
-
resolver
|
|
434
|
-
resolver.current_selector = selector
|
|
424
|
+
pin(resolver, selector)
|
|
435
425
|
|
|
436
426
|
resolver
|
|
437
427
|
end
|
|
@@ -449,9 +439,9 @@ module HTTPX
|
|
|
449
439
|
return false
|
|
450
440
|
end
|
|
451
441
|
|
|
452
|
-
conn2.log(level: 2) { "coalescing with
|
|
453
|
-
conn2.coalesce!(conn1)
|
|
442
|
+
conn2.log(level: 2) { "coalescing with connection##{conn1.object_id}[#{conn1.origin}])" }
|
|
454
443
|
select_connection(conn1, selector) if from_pool
|
|
444
|
+
conn2.coalesce!(conn1)
|
|
455
445
|
conn2.disconnect
|
|
456
446
|
true
|
|
457
447
|
end
|