httpx 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/httpx.rb +8 -2
- data/lib/httpx/adapters/faraday.rb +203 -0
- data/lib/httpx/altsvc.rb +4 -0
- data/lib/httpx/callbacks.rb +1 -4
- data/lib/httpx/chainable.rb +4 -3
- data/lib/httpx/connection.rb +326 -104
- data/lib/httpx/{channel → connection}/http1.rb +29 -15
- data/lib/httpx/{channel → connection}/http2.rb +12 -6
- data/lib/httpx/errors.rb +2 -0
- data/lib/httpx/headers.rb +4 -1
- data/lib/httpx/io/ssl.rb +5 -1
- data/lib/httpx/io/tcp.rb +13 -7
- data/lib/httpx/io/udp.rb +1 -0
- data/lib/httpx/io/unix.rb +1 -0
- data/lib/httpx/loggable.rb +34 -9
- data/lib/httpx/options.rb +57 -31
- data/lib/httpx/parser/http1.rb +8 -0
- data/lib/httpx/plugins/authentication.rb +4 -0
- data/lib/httpx/plugins/basic_authentication.rb +4 -0
- data/lib/httpx/plugins/compression.rb +22 -5
- data/lib/httpx/plugins/cookies.rb +89 -36
- data/lib/httpx/plugins/digest_authentication.rb +45 -26
- data/lib/httpx/plugins/follow_redirects.rb +61 -62
- data/lib/httpx/plugins/h2c.rb +78 -39
- data/lib/httpx/plugins/multipart.rb +5 -0
- data/lib/httpx/plugins/persistent.rb +29 -0
- data/lib/httpx/plugins/proxy.rb +125 -78
- data/lib/httpx/plugins/proxy/http.rb +31 -27
- data/lib/httpx/plugins/proxy/socks4.rb +30 -24
- data/lib/httpx/plugins/proxy/socks5.rb +49 -39
- data/lib/httpx/plugins/proxy/ssh.rb +81 -0
- data/lib/httpx/plugins/push_promise.rb +18 -9
- data/lib/httpx/plugins/retries.rb +43 -15
- data/lib/httpx/pool.rb +159 -0
- data/lib/httpx/registry.rb +2 -0
- data/lib/httpx/request.rb +10 -0
- data/lib/httpx/resolver.rb +2 -1
- data/lib/httpx/resolver/https.rb +62 -56
- data/lib/httpx/resolver/native.rb +48 -37
- data/lib/httpx/resolver/resolver_mixin.rb +16 -11
- data/lib/httpx/resolver/system.rb +11 -7
- data/lib/httpx/response.rb +24 -10
- data/lib/httpx/selector.rb +32 -39
- data/lib/httpx/{client.rb → session.rb} +99 -62
- data/lib/httpx/timeout.rb +7 -15
- data/lib/httpx/transcoder/body.rb +4 -0
- data/lib/httpx/transcoder/chunker.rb +4 -0
- data/lib/httpx/version.rb +1 -1
- metadata +10 -8
- data/lib/httpx/channel.rb +0 -367
@@ -33,9 +33,9 @@ module HTTPX
|
|
33
33
|
|
34
34
|
DNS_PORT = 53
|
35
35
|
|
36
|
-
def_delegator :@
|
36
|
+
def_delegator :@connections, :empty?
|
37
37
|
|
38
|
-
def initialize(
|
38
|
+
def initialize(options)
|
39
39
|
@options = Options.new(options)
|
40
40
|
@ns_index = 0
|
41
41
|
@resolver_options = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
|
@@ -43,7 +43,7 @@ module HTTPX
|
|
43
43
|
@_timeouts = Array(@resolver_options.timeouts)
|
44
44
|
@timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup }
|
45
45
|
@_record_types = Hash.new { |types, host| types[host] = @resolver_options.record_types.dup }
|
46
|
-
@
|
46
|
+
@connections = []
|
47
47
|
@queries = {}
|
48
48
|
@read_buffer = Buffer.new(@resolver_options.packet_size)
|
49
49
|
@write_buffer = Buffer.new(@resolver_options.packet_size)
|
@@ -81,8 +81,8 @@ module HTTPX
|
|
81
81
|
if @ns_index < @nameserver.size
|
82
82
|
transition(:idle)
|
83
83
|
else
|
84
|
-
@queries.each do |host,
|
85
|
-
emit_resolve_error(
|
84
|
+
@queries.each do |host, connection|
|
85
|
+
emit_resolve_error(connection, host, e)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -97,14 +97,16 @@ module HTTPX
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
-
def <<(
|
101
|
-
return if early_resolve(
|
100
|
+
def <<(connection)
|
101
|
+
return if early_resolve(connection)
|
102
|
+
|
102
103
|
if @nameserver.nil?
|
103
|
-
ex = ResolveError.new("Can't resolve #{
|
104
|
+
ex = ResolveError.new("Can't resolve #{connection.origin.host}: no nameserver")
|
104
105
|
ex.set_backtrace(caller)
|
105
|
-
emit(:error,
|
106
|
+
emit(:error, connection, ex)
|
106
107
|
else
|
107
|
-
@
|
108
|
+
@connections << connection
|
109
|
+
resolve
|
108
110
|
end
|
109
111
|
end
|
110
112
|
|
@@ -124,31 +126,32 @@ module HTTPX
|
|
124
126
|
|
125
127
|
def do_retry
|
126
128
|
return if @queries.empty?
|
129
|
+
|
127
130
|
loop_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start_timeout
|
128
|
-
|
131
|
+
connections = []
|
129
132
|
queries = {}
|
130
133
|
while (query = @queries.shift)
|
131
|
-
h,
|
132
|
-
host =
|
134
|
+
h, connection = query
|
135
|
+
host = connection.origin.host
|
133
136
|
timeout = (@timeouts[host][0] -= loop_time)
|
134
137
|
unless timeout.negative?
|
135
|
-
queries[h] =
|
138
|
+
queries[h] = connection
|
136
139
|
next
|
137
140
|
end
|
138
141
|
@timeouts[host].shift
|
139
142
|
if @timeouts[host].empty?
|
140
143
|
@timeouts.delete(host)
|
141
|
-
emit_resolve_error(
|
144
|
+
emit_resolve_error(connection, host)
|
142
145
|
return
|
143
146
|
else
|
144
|
-
|
147
|
+
connections << connection
|
145
148
|
log(label: "resolver: ") do
|
146
149
|
"timeout after #{prev_timeout}s, retry(#{timeouts.first}) #{host}..."
|
147
150
|
end
|
148
151
|
end
|
149
152
|
end
|
150
153
|
@queries = queries
|
151
|
-
|
154
|
+
connections.each { |ch| resolve(ch) }
|
152
155
|
end
|
153
156
|
|
154
157
|
def dread(wsize = @read_buffer.limit)
|
@@ -159,6 +162,7 @@ module HTTPX
|
|
159
162
|
return
|
160
163
|
end
|
161
164
|
return if siz.zero?
|
165
|
+
|
162
166
|
log(label: "resolver: ") { "READ: #{siz} bytes..." }
|
163
167
|
parse(@read_buffer.to_s)
|
164
168
|
end
|
@@ -167,6 +171,7 @@ module HTTPX
|
|
167
171
|
def dwrite
|
168
172
|
loop do
|
169
173
|
return if @write_buffer.empty?
|
174
|
+
|
170
175
|
siz = @io.write(@write_buffer)
|
171
176
|
unless siz
|
172
177
|
emit(:close)
|
@@ -178,62 +183,66 @@ module HTTPX
|
|
178
183
|
end
|
179
184
|
|
180
185
|
def parse(buffer)
|
181
|
-
|
182
|
-
Resolver.decode_dns_answer(buffer)
|
186
|
+
begin
|
187
|
+
addresses = Resolver.decode_dns_answer(buffer)
|
183
188
|
rescue Resolv::DNS::DecodeError => e
|
184
|
-
hostname,
|
189
|
+
hostname, connection = @queries.first
|
185
190
|
if @_record_types[hostname].empty?
|
186
|
-
emit_resolve_error(
|
191
|
+
emit_resolve_error(connection, hostname, e)
|
187
192
|
return
|
188
193
|
end
|
189
194
|
end
|
190
195
|
|
191
196
|
if addresses.empty?
|
192
|
-
hostname,
|
197
|
+
hostname, connection = @queries.first
|
193
198
|
@_record_types[hostname].shift
|
194
199
|
if @_record_types[hostname].empty?
|
195
200
|
@_record_types.delete(hostname)
|
196
|
-
emit_resolve_error(
|
201
|
+
emit_resolve_error(connection, hostname)
|
197
202
|
return
|
198
203
|
end
|
199
204
|
else
|
200
205
|
address = addresses.first
|
201
|
-
|
202
|
-
return unless
|
206
|
+
connection = @queries.delete(address["name"])
|
207
|
+
return unless connection # probably a retried query for which there's an answer
|
208
|
+
|
203
209
|
if address.key?("alias") # CNAME
|
204
|
-
if early_resolve(
|
205
|
-
@
|
210
|
+
if early_resolve(connection, hostname: address["alias"])
|
211
|
+
@connections.delete(connection)
|
206
212
|
else
|
207
|
-
resolve(
|
213
|
+
resolve(connection, address["alias"])
|
208
214
|
@queries.delete(address["name"])
|
209
215
|
return
|
210
216
|
end
|
211
217
|
else
|
212
|
-
@
|
213
|
-
Resolver.cached_lookup_set(
|
214
|
-
emit_addresses(
|
218
|
+
@connections.delete(connection)
|
219
|
+
Resolver.cached_lookup_set(connection.origin.host, addresses)
|
220
|
+
emit_addresses(connection, addresses.map { |addr| addr["data"] })
|
215
221
|
end
|
216
222
|
end
|
217
|
-
return emit(:close) if @
|
223
|
+
return emit(:close) if @connections.empty?
|
224
|
+
|
218
225
|
resolve
|
219
226
|
end
|
220
227
|
|
221
|
-
def resolve(
|
222
|
-
raise Error, "no URI to resolve" unless
|
228
|
+
def resolve(connection = @connections.first, hostname = nil)
|
229
|
+
raise Error, "no URI to resolve" unless connection
|
223
230
|
return unless @write_buffer.empty?
|
224
|
-
|
225
|
-
|
231
|
+
|
232
|
+
hostname = hostname || @queries.key(connection) || connection.origin.host
|
233
|
+
@queries[hostname] = connection
|
226
234
|
type = @_record_types[hostname].first
|
227
235
|
log(label: "resolver: ") { "query #{type} for #{hostname}" }
|
228
236
|
begin
|
229
237
|
@write_buffer << Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
|
230
238
|
rescue Resolv::DNS::EncodeError => e
|
231
|
-
emit_resolve_error(
|
239
|
+
emit_resolve_error(connection, hostname, e)
|
232
240
|
end
|
233
241
|
end
|
234
242
|
|
235
243
|
def build_socket
|
236
244
|
return if @io
|
245
|
+
|
237
246
|
ip, port = @nameserver[@ns_index]
|
238
247
|
port ||= DNS_PORT
|
239
248
|
uri = URI::Generic.build(scheme: "udp", port: port)
|
@@ -253,11 +262,13 @@ module HTTPX
|
|
253
262
|
@timeouts.clear
|
254
263
|
when :open
|
255
264
|
return unless @state == :idle
|
265
|
+
|
256
266
|
build_socket
|
257
267
|
@io.connect
|
258
268
|
return unless @io.connected?
|
259
269
|
when :closed
|
260
270
|
return unless @state == :open
|
271
|
+
|
261
272
|
@io.close if @io
|
262
273
|
end
|
263
274
|
@state = nextstate
|
@@ -18,27 +18,31 @@ module HTTPX
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def uncache(
|
22
|
-
hostname = hostname || @queries.key(
|
21
|
+
def uncache(connection)
|
22
|
+
hostname = hostname || @queries.key(connection) || connection.origin.host
|
23
23
|
Resolver.uncache(hostname)
|
24
24
|
@_record_types[hostname].shift
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
def emit_addresses(
|
29
|
+
def emit_addresses(connection, addresses)
|
30
30
|
addresses.map! do |address|
|
31
31
|
address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
|
32
32
|
end
|
33
|
-
log(label: "resolver: ") { "answer #{
|
34
|
-
|
35
|
-
emit(:resolve,
|
33
|
+
log(label: "resolver: ") { "answer #{connection.origin.host}: #{addresses.inspect}" }
|
34
|
+
connection.addresses = addresses
|
35
|
+
emit(:resolve, connection)
|
36
36
|
end
|
37
37
|
|
38
|
-
def early_resolve(
|
39
|
-
addresses =
|
38
|
+
def early_resolve(connection, hostname: connection.origin.host)
|
39
|
+
addresses = connection.addresses ||
|
40
|
+
ip_resolve(hostname) ||
|
41
|
+
Resolver.cached_lookup(hostname) ||
|
42
|
+
system_resolve(hostname)
|
40
43
|
return unless addresses
|
41
|
-
|
44
|
+
|
45
|
+
emit_addresses(connection, addresses)
|
42
46
|
end
|
43
47
|
|
44
48
|
def ip_resolve(hostname)
|
@@ -49,14 +53,15 @@ module HTTPX
|
|
49
53
|
@system_resolver ||= Resolv::Hosts.new
|
50
54
|
ips = @system_resolver.getaddresses(hostname)
|
51
55
|
return if ips.empty?
|
56
|
+
|
52
57
|
ips.map { |ip| IPAddr.new(ip) }
|
53
58
|
end
|
54
59
|
|
55
|
-
def emit_resolve_error(
|
60
|
+
def emit_resolve_error(connection, hostname, ex = nil)
|
56
61
|
message = ex ? ex.message : "Can't resolve #{hostname}"
|
57
62
|
error = ResolveError.new(message)
|
58
63
|
error.set_backtrace(ex ? ex.backtrace : caller)
|
59
|
-
emit(:error,
|
64
|
+
emit(:error, connection, error)
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
@@ -12,7 +12,7 @@ module HTTPX
|
|
12
12
|
Resolv::DNS::EncodeError,
|
13
13
|
Resolv::DNS::DecodeError].freeze
|
14
14
|
|
15
|
-
def initialize(
|
15
|
+
def initialize(options)
|
16
16
|
@options = Options.new(options)
|
17
17
|
roptions = @options.resolver_options
|
18
18
|
@state = :idle
|
@@ -28,13 +28,17 @@ module HTTPX
|
|
28
28
|
true
|
29
29
|
end
|
30
30
|
|
31
|
-
def <<(
|
32
|
-
hostname =
|
33
|
-
addresses =
|
34
|
-
|
35
|
-
|
31
|
+
def <<(connection)
|
32
|
+
hostname = connection.origin.host
|
33
|
+
addresses = connection.addresses ||
|
34
|
+
ip_resolve(hostname) ||
|
35
|
+
system_resolve(hostname) ||
|
36
|
+
@resolver.getaddresses(hostname)
|
37
|
+
return emit_resolve_error(connection, hostname) if addresses.empty?
|
38
|
+
|
39
|
+
emit_addresses(connection, addresses)
|
36
40
|
rescue Errno::EHOSTUNREACH, *RESOLV_ERRORS => e
|
37
|
-
emit_resolve_error(
|
41
|
+
emit_resolve_error(connection, hostname, e)
|
38
42
|
end
|
39
43
|
|
40
44
|
def uncache(*); end
|
data/lib/httpx/response.rb
CHANGED
@@ -21,10 +21,10 @@ module HTTPX
|
|
21
21
|
|
22
22
|
def_delegator :@request, :uri
|
23
23
|
|
24
|
-
def initialize(request, status, version, headers
|
25
|
-
@options = Options.new(options)
|
26
|
-
@version = version
|
24
|
+
def initialize(request, status, version, headers)
|
27
25
|
@request = request
|
26
|
+
@options = request.options
|
27
|
+
@version = version
|
28
28
|
@status = Integer(status)
|
29
29
|
@headers = @options.headers_class.new(headers)
|
30
30
|
@body = @options.response_body_class.new(self, threshold_size: @options.body_threshold_size,
|
@@ -58,6 +58,7 @@ module HTTPX
|
|
58
58
|
|
59
59
|
def raise_for_status
|
60
60
|
return if @status < 400
|
61
|
+
|
61
62
|
raise HTTPError, self
|
62
63
|
end
|
63
64
|
|
@@ -70,6 +71,7 @@ module HTTPX
|
|
70
71
|
@status == 304 || begin
|
71
72
|
content_length = @headers["content-length"]
|
72
73
|
return false if content_length.nil?
|
74
|
+
|
73
75
|
content_length == "0"
|
74
76
|
end
|
75
77
|
end
|
@@ -94,6 +96,7 @@ module HTTPX
|
|
94
96
|
|
95
97
|
def read(*args)
|
96
98
|
return unless @buffer
|
99
|
+
|
97
100
|
@buffer.read(*args)
|
98
101
|
end
|
99
102
|
|
@@ -103,6 +106,7 @@ module HTTPX
|
|
103
106
|
|
104
107
|
def each
|
105
108
|
return enum_for(__method__) unless block_given?
|
109
|
+
|
106
110
|
begin
|
107
111
|
unless @state == :idle
|
108
112
|
rewind
|
@@ -137,6 +141,7 @@ module HTTPX
|
|
137
141
|
|
138
142
|
def copy_to(dest)
|
139
143
|
return unless @buffer
|
144
|
+
|
140
145
|
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
141
146
|
FileUtils.mv(@buffer.path, dest.path)
|
142
147
|
else
|
@@ -148,6 +153,7 @@ module HTTPX
|
|
148
153
|
# closes/cleans the buffer, resets everything
|
149
154
|
def close
|
150
155
|
return if @state == :idle
|
156
|
+
|
151
157
|
@buffer.close
|
152
158
|
@buffer.unlink if @buffer.respond_to?(:unlink)
|
153
159
|
@buffer = nil
|
@@ -163,6 +169,7 @@ module HTTPX
|
|
163
169
|
|
164
170
|
def rewind
|
165
171
|
return if @state == :idle
|
172
|
+
|
166
173
|
@buffer.rewind
|
167
174
|
end
|
168
175
|
|
@@ -182,10 +189,9 @@ module HTTPX
|
|
182
189
|
@buffer = Tempfile.new("httpx", encoding: @encoding, mode: File::RDWR)
|
183
190
|
aux.rewind
|
184
191
|
::IO.copy_stream(aux, @buffer)
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
#############################################
|
192
|
+
# (this looks like a bug from Ruby < 2.3
|
193
|
+
@buffer.pos = aux.pos ##################
|
194
|
+
########################################
|
189
195
|
aux.close
|
190
196
|
@state = :buffer
|
191
197
|
end
|
@@ -197,8 +203,8 @@ module HTTPX
|
|
197
203
|
end
|
198
204
|
|
199
205
|
class ContentType
|
200
|
-
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}
|
201
|
-
CHARSET_RE = /;\s*charset=([^;]+)/i
|
206
|
+
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
|
207
|
+
CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
|
202
208
|
|
203
209
|
attr_reader :mime_type, :charset
|
204
210
|
|
@@ -237,7 +243,7 @@ module HTTPX
|
|
237
243
|
def initialize(error, options)
|
238
244
|
@error = error
|
239
245
|
@options = Options.new(options)
|
240
|
-
|
246
|
+
log_exception(@error)
|
241
247
|
end
|
242
248
|
|
243
249
|
def status
|
@@ -247,5 +253,13 @@ module HTTPX
|
|
247
253
|
def raise_for_status
|
248
254
|
raise @error
|
249
255
|
end
|
256
|
+
|
257
|
+
# rubocop:disable Style/MissingRespondToMissing
|
258
|
+
def method_missing(meth, *, &block)
|
259
|
+
raise NoMethodError, "undefined response method `#{meth}' for error response" if Response.public_method_defined?(meth)
|
260
|
+
|
261
|
+
super
|
262
|
+
end
|
263
|
+
# rubocop:enable Style/MissingRespondToMissing
|
250
264
|
end
|
251
265
|
end
|
data/lib/httpx/selector.rb
CHANGED
@@ -11,7 +11,7 @@ class HTTPX::Selector
|
|
11
11
|
# I/O monitor
|
12
12
|
#
|
13
13
|
class Monitor
|
14
|
-
attr_accessor :
|
14
|
+
attr_accessor :io, :interests, :readiness
|
15
15
|
|
16
16
|
def initialize(io, interests, reactor)
|
17
17
|
@io = io
|
@@ -31,6 +31,7 @@ class HTTPX::Selector
|
|
31
31
|
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
32
32
|
def close(deregister = true)
|
33
33
|
return if @closed
|
34
|
+
|
34
35
|
@closed = true
|
35
36
|
@reactor.deregister(@io) if deregister
|
36
37
|
end
|
@@ -49,49 +50,44 @@ class HTTPX::Selector
|
|
49
50
|
def initialize
|
50
51
|
@readers = {}
|
51
52
|
@writers = {}
|
52
|
-
@lock = Mutex.new
|
53
53
|
@__r__, @__w__ = IO.pipe
|
54
54
|
@closed = false
|
55
55
|
end
|
56
56
|
|
57
57
|
# deregisters +io+ from selectables.
|
58
58
|
def deregister(io)
|
59
|
-
@
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
monitor.close(false) if monitor
|
64
|
-
end
|
59
|
+
rmonitor = @readers.delete(io)
|
60
|
+
wmonitor = @writers.delete(io)
|
61
|
+
monitor = rmonitor || wmonitor
|
62
|
+
monitor.close(false) if monitor
|
65
63
|
end
|
66
64
|
|
67
65
|
# register +io+ for +interests+ events.
|
68
66
|
def register(io, interests)
|
69
67
|
readable = READABLE.include?(interests)
|
70
68
|
writable = WRITABLE.include?(interests)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
monitor = Monitor.new(io, interests, self)
|
78
|
-
end
|
79
|
-
@readers[io] = monitor
|
80
|
-
@writers.delete(io) unless writable
|
69
|
+
if readable
|
70
|
+
monitor = @readers[io]
|
71
|
+
if monitor
|
72
|
+
monitor.interests = interests
|
73
|
+
else
|
74
|
+
monitor = Monitor.new(io, interests, self)
|
81
75
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@readers.
|
76
|
+
@readers[io] = monitor
|
77
|
+
@writers.delete(io) unless writable
|
78
|
+
end
|
79
|
+
if writable
|
80
|
+
monitor = @writers[io]
|
81
|
+
if monitor
|
82
|
+
monitor.interests = interests
|
83
|
+
else
|
84
|
+
# reuse object
|
85
|
+
monitor = readable ? @readers[io] : Monitor.new(io, interests, self)
|
92
86
|
end
|
93
|
-
monitor
|
87
|
+
@writers[io] = monitor
|
88
|
+
@readers.delete(io) unless readable
|
94
89
|
end
|
90
|
+
monitor
|
95
91
|
end
|
96
92
|
|
97
93
|
# waits for read/write events for +interval+. Yields for monitors of
|
@@ -99,22 +95,16 @@ class HTTPX::Selector
|
|
99
95
|
#
|
100
96
|
def select(interval)
|
101
97
|
begin
|
102
|
-
r =
|
103
|
-
w =
|
104
|
-
@lock.synchronize do
|
105
|
-
r = @readers.keys
|
106
|
-
w = @writers.keys
|
107
|
-
end
|
98
|
+
r = @readers.keys
|
99
|
+
w = @writers.keys
|
108
100
|
r.unshift(@__r__)
|
109
101
|
|
110
102
|
readers, writers = IO.select(r, w, nil, interval)
|
111
103
|
|
112
104
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
113
105
|
rescue IOError, SystemCallError
|
114
|
-
@
|
115
|
-
|
116
|
-
@writers.reject! { |io, _| io.closed? }
|
117
|
-
end
|
106
|
+
@readers.reject! { |io, _| io.closed? }
|
107
|
+
@writers.reject! { |io, _| io.closed? }
|
118
108
|
retry
|
119
109
|
end
|
120
110
|
|
@@ -125,6 +115,7 @@ class HTTPX::Selector
|
|
125
115
|
else
|
126
116
|
monitor = io.closed? ? @readers.delete(io) : @readers[io]
|
127
117
|
next unless monitor
|
118
|
+
|
128
119
|
monitor.readiness = writers.delete(io) ? :rw : :r
|
129
120
|
yield monitor
|
130
121
|
end
|
@@ -133,6 +124,7 @@ class HTTPX::Selector
|
|
133
124
|
writers.each do |io|
|
134
125
|
monitor = io.closed? ? @writers.delete(io) : @writers[io]
|
135
126
|
next unless monitor
|
127
|
+
|
136
128
|
# don't double run this, the last iteration might have run this task already
|
137
129
|
monitor.readiness = :w
|
138
130
|
yield monitor
|
@@ -143,6 +135,7 @@ class HTTPX::Selector
|
|
143
135
|
#
|
144
136
|
def close
|
145
137
|
return if @closed
|
138
|
+
|
146
139
|
@__r__.close
|
147
140
|
@__w__.close
|
148
141
|
rescue IOError
|