httpx 0.6.7 → 0.9.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/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
data/lib/httpx/registry.rb
CHANGED
data/lib/httpx/request.rb
CHANGED
@@ -37,8 +37,6 @@ module HTTPX
|
|
37
37
|
|
38
38
|
attr_reader :options, :response
|
39
39
|
|
40
|
-
attr_accessor :timer
|
41
|
-
|
42
40
|
def_delegator :@body, :<<
|
43
41
|
|
44
42
|
def_delegator :@body, :empty?
|
@@ -60,6 +58,12 @@ module HTTPX
|
|
60
58
|
@state = :idle
|
61
59
|
end
|
62
60
|
|
61
|
+
def interests
|
62
|
+
return :r if @state == :done || @state == :expect
|
63
|
+
|
64
|
+
:w
|
65
|
+
end
|
66
|
+
|
63
67
|
# :nocov:
|
64
68
|
if RUBY_VERSION < "2.2"
|
65
69
|
# rubocop: disable Lint/UriEscapeUnescape:
|
@@ -83,7 +87,6 @@ module HTTPX
|
|
83
87
|
def response=(response)
|
84
88
|
return unless response
|
85
89
|
|
86
|
-
@timer.cancel if @timer
|
87
90
|
@response = response
|
88
91
|
end
|
89
92
|
|
@@ -222,6 +225,7 @@ module HTTPX
|
|
222
225
|
case nextstate
|
223
226
|
when :idle
|
224
227
|
@response = nil
|
228
|
+
@drainer = nil
|
225
229
|
when :headers
|
226
230
|
return unless @state == :idle
|
227
231
|
when :body
|
@@ -238,15 +242,13 @@ module HTTPX
|
|
238
242
|
when 100
|
239
243
|
# deallocate
|
240
244
|
@response = nil
|
241
|
-
when 417
|
242
|
-
@response = @response
|
243
|
-
return
|
244
245
|
end
|
245
246
|
end
|
246
247
|
when :done
|
247
248
|
return if @state == :expect
|
248
249
|
end
|
249
250
|
@state = nextstate
|
251
|
+
emit(@state)
|
250
252
|
nil
|
251
253
|
end
|
252
254
|
|
data/lib/httpx/resolver.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resolv"
|
4
|
+
require "httpx/resolver/resolver_mixin"
|
5
|
+
require "httpx/resolver/system"
|
6
|
+
require "httpx/resolver/native"
|
7
|
+
require "httpx/resolver/https"
|
4
8
|
|
5
9
|
module HTTPX
|
6
10
|
module Resolver
|
7
|
-
autoload :ResolverMixin, "httpx/resolver/resolver_mixin"
|
8
|
-
autoload :System, "httpx/resolver/system"
|
9
|
-
autoload :Native, "httpx/resolver/native"
|
10
|
-
autoload :HTTPS, "httpx/resolver/https"
|
11
|
-
|
12
11
|
extend Registry
|
13
12
|
|
14
|
-
register :system,
|
15
|
-
register :native,
|
16
|
-
register :https,
|
13
|
+
register :system, System
|
14
|
+
register :native, Native
|
15
|
+
register :https, HTTPS
|
17
16
|
|
18
17
|
@lookup_mutex = Mutex.new
|
19
18
|
@lookups = Hash.new { |h, k| h[k] = [] }
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -25,7 +25,7 @@ module HTTPX
|
|
25
25
|
|
26
26
|
def_delegator :@connections, :empty?
|
27
27
|
|
28
|
-
def_delegators :@resolver_connection, :to_io, :call, :
|
28
|
+
def_delegators :@resolver_connection, :connecting?, :to_io, :call, :close
|
29
29
|
|
30
30
|
def initialize(options)
|
31
31
|
@options = Options.new(options)
|
@@ -62,8 +62,20 @@ module HTTPX
|
|
62
62
|
resolver_connection.closed?
|
63
63
|
end
|
64
64
|
|
65
|
+
def interests
|
66
|
+
return if @queries.empty?
|
67
|
+
|
68
|
+
resolver_connection.__send__(__method__)
|
69
|
+
end
|
70
|
+
|
65
71
|
private
|
66
72
|
|
73
|
+
def connect
|
74
|
+
return if @queries.empty?
|
75
|
+
|
76
|
+
resolver_connection.__send__(__method__)
|
77
|
+
end
|
78
|
+
|
67
79
|
def pool
|
68
80
|
Thread.current[:httpx_connection_pool] ||= Pool.new
|
69
81
|
end
|
@@ -84,7 +96,7 @@ module HTTPX
|
|
84
96
|
|
85
97
|
hostname = hostname || @queries.key(connection) || connection.origin.host
|
86
98
|
type = @_record_types[hostname].first
|
87
|
-
log
|
99
|
+
log { "resolver: query #{type} for #{hostname}" }
|
88
100
|
begin
|
89
101
|
request = build_request(hostname, type)
|
90
102
|
@requests[request] = connection
|
@@ -111,7 +123,7 @@ module HTTPX
|
|
111
123
|
end
|
112
124
|
|
113
125
|
def on_promise(_, stream)
|
114
|
-
log(level: 2
|
126
|
+
log(level: 2) { "#{stream.id}: refusing stream!" }
|
115
127
|
stream.refuse
|
116
128
|
end
|
117
129
|
|
@@ -73,14 +73,6 @@ module HTTPX
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def to_io
|
76
|
-
case @state
|
77
|
-
when :idle
|
78
|
-
transition(:open)
|
79
|
-
when :closed
|
80
|
-
transition(:idle)
|
81
|
-
transition(:open)
|
82
|
-
end
|
83
|
-
resolve if @queries.empty?
|
84
76
|
@io.to_io
|
85
77
|
end
|
86
78
|
|
@@ -93,11 +85,7 @@ module HTTPX
|
|
93
85
|
rescue Errno::EHOSTUNREACH => e
|
94
86
|
@ns_index += 1
|
95
87
|
if @ns_index < @nameserver.size
|
96
|
-
log
|
97
|
-
# :nocov:
|
98
|
-
"failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})"
|
99
|
-
# :nocov:
|
100
|
-
end
|
88
|
+
log { "resolver: failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})" }
|
101
89
|
transition(:idle)
|
102
90
|
else
|
103
91
|
handle_error(e)
|
@@ -107,6 +95,14 @@ module HTTPX
|
|
107
95
|
end
|
108
96
|
|
109
97
|
def interests
|
98
|
+
case @state
|
99
|
+
when :idle
|
100
|
+
transition(:open)
|
101
|
+
when :closed
|
102
|
+
transition(:idle)
|
103
|
+
transition(:open)
|
104
|
+
end
|
105
|
+
|
110
106
|
!@write_buffer.empty? || @queries.empty? ? :w : :r
|
111
107
|
end
|
112
108
|
|
@@ -160,11 +156,7 @@ module HTTPX
|
|
160
156
|
raise NativeResolveError.new(connection, host)
|
161
157
|
else
|
162
158
|
connections << connection
|
163
|
-
log
|
164
|
-
# :nocov:
|
165
|
-
"timeout after #{prev_timeout}s, retry(#{timeouts.first}) #{host}..."
|
166
|
-
# :nocov:
|
167
|
-
end
|
159
|
+
log { "resolver: timeout after #{prev_timeout}s, retry(#{timeouts.first}) #{host}..." }
|
168
160
|
end
|
169
161
|
end
|
170
162
|
@queries = queries
|
@@ -174,14 +166,11 @@ module HTTPX
|
|
174
166
|
def dread(wsize = @resolver_options.packet_size)
|
175
167
|
loop do
|
176
168
|
siz = @io.read(wsize, @read_buffer)
|
177
|
-
unless siz
|
178
|
-
emit(:close)
|
179
|
-
return
|
180
|
-
end
|
181
|
-
return if siz.zero?
|
169
|
+
return unless siz && siz.positive?
|
182
170
|
|
183
|
-
log
|
171
|
+
log { "resolver: READ: #{siz} bytes..." }
|
184
172
|
parse(@read_buffer)
|
173
|
+
return if @state == :closed
|
185
174
|
end
|
186
175
|
end
|
187
176
|
|
@@ -190,12 +179,10 @@ module HTTPX
|
|
190
179
|
return if @write_buffer.empty?
|
191
180
|
|
192
181
|
siz = @io.write(@write_buffer)
|
193
|
-
unless siz
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
log(label: "resolver: ") { "WRITE: #{siz} bytes..." }
|
198
|
-
return if siz.zero?
|
182
|
+
return unless siz && siz.positive?
|
183
|
+
|
184
|
+
log { "resolver: WRITE: #{siz} bytes..." }
|
185
|
+
return if @state == :closed
|
199
186
|
end
|
200
187
|
end
|
201
188
|
|
@@ -253,7 +240,7 @@ module HTTPX
|
|
253
240
|
hostname = hostname || @queries.key(connection) || connection.origin.host
|
254
241
|
@queries[hostname] = connection
|
255
242
|
type = @_record_types[hostname].first
|
256
|
-
log
|
243
|
+
log { "resolver: query #{type} for #{hostname}" }
|
257
244
|
begin
|
258
245
|
@write_buffer << Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
|
259
246
|
rescue Resolv::DNS::EncodeError => e
|
@@ -269,7 +256,7 @@ module HTTPX
|
|
269
256
|
uri = URI::Generic.build(scheme: "udp", port: port)
|
270
257
|
uri.hostname = ip
|
271
258
|
type = IO.registry(uri.scheme)
|
272
|
-
log
|
259
|
+
log { "resolver: server: #{uri}..." }
|
273
260
|
@io = type.new(uri, [IPAddr.new(ip)], @options)
|
274
261
|
end
|
275
262
|
|
@@ -285,8 +272,11 @@ module HTTPX
|
|
285
272
|
return unless @state == :idle
|
286
273
|
|
287
274
|
build_socket
|
275
|
+
|
288
276
|
@io.connect
|
289
277
|
return unless @io.connected?
|
278
|
+
|
279
|
+
resolve if @queries.empty?
|
290
280
|
when :closed
|
291
281
|
return unless @state == :open
|
292
282
|
|
@@ -6,7 +6,7 @@ module HTTPX
|
|
6
6
|
@options = options
|
7
7
|
end
|
8
8
|
|
9
|
-
def method_missing(m,
|
9
|
+
def method_missing(m, *, &block)
|
10
10
|
if @options.key?(m)
|
11
11
|
@options[m]
|
12
12
|
else
|
@@ -14,7 +14,7 @@ module HTTPX
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def respond_to_missing?(m)
|
17
|
+
def respond_to_missing?(m, *)
|
18
18
|
@options.key?(m) || super
|
19
19
|
end
|
20
20
|
|
@@ -30,7 +30,7 @@ module HTTPX
|
|
30
30
|
addresses.map! do |address|
|
31
31
|
address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
|
32
32
|
end
|
33
|
-
log
|
33
|
+
log { "resolver: answer #{connection.origin.host}: #{addresses.inspect}" }
|
34
34
|
connection.addresses = addresses
|
35
35
|
catch(:coalesced) { emit(:resolve, connection) }
|
36
36
|
end
|
data/lib/httpx/response.rb
CHANGED
@@ -58,7 +58,7 @@ module HTTPX
|
|
58
58
|
"HTTP/#{version} " \
|
59
59
|
"@status=#{@status} " \
|
60
60
|
"@headers=#{@headers} " \
|
61
|
-
"@body=#{@body}>"
|
61
|
+
"@body=#{@body.bytesize}>"
|
62
62
|
end
|
63
63
|
# :nocov:
|
64
64
|
|
@@ -103,6 +103,8 @@ module HTTPX
|
|
103
103
|
def read(*args)
|
104
104
|
return unless @buffer
|
105
105
|
|
106
|
+
rewind
|
107
|
+
|
106
108
|
@buffer.read(*args)
|
107
109
|
end
|
108
110
|
|
@@ -148,6 +150,8 @@ module HTTPX
|
|
148
150
|
def copy_to(dest)
|
149
151
|
return unless @buffer
|
150
152
|
|
153
|
+
rewind
|
154
|
+
|
151
155
|
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
152
156
|
FileUtils.mv(@buffer.path, dest.path)
|
153
157
|
else
|
@@ -235,13 +239,11 @@ module HTTPX
|
|
235
239
|
|
236
240
|
private
|
237
241
|
|
238
|
-
# :nodoc:
|
239
242
|
def mime_type(str)
|
240
243
|
m = str.to_s[MIME_TYPE_RE, 1]
|
241
244
|
m && m.strip.downcase
|
242
245
|
end
|
243
246
|
|
244
|
-
# :nodoc:
|
245
247
|
def charset(str)
|
246
248
|
m = str.to_s[CHARSET_RE, 1]
|
247
249
|
m && m.strip.delete('"')
|
@@ -265,6 +267,18 @@ module HTTPX
|
|
265
267
|
@error.message
|
266
268
|
end
|
267
269
|
|
270
|
+
def reason
|
271
|
+
@error.class.name
|
272
|
+
end
|
273
|
+
|
274
|
+
def headers
|
275
|
+
{}
|
276
|
+
end
|
277
|
+
|
278
|
+
def body
|
279
|
+
@error.backtrace.join("\n")
|
280
|
+
end
|
281
|
+
|
268
282
|
def raise_for_status
|
269
283
|
raise @error
|
270
284
|
end
|
data/lib/httpx/selector.rb
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "io/wait"
|
4
|
+
|
5
|
+
module IOExtensions
|
6
|
+
refine IO do
|
7
|
+
def wait(timeout = nil, mode = :read)
|
8
|
+
case mode
|
9
|
+
when :read
|
10
|
+
wait_readable(timeout)
|
11
|
+
when :write
|
12
|
+
wait_writable(timeout)
|
13
|
+
when :read_write
|
14
|
+
r, w = IO.select([self], [self], nil, timeout)
|
15
|
+
|
16
|
+
return unless r || w
|
17
|
+
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
3
24
|
class HTTPX::Selector
|
4
25
|
READABLE = %i[rw r].freeze
|
5
26
|
WRITABLE = %i[rw w].freeze
|
@@ -7,129 +28,109 @@ class HTTPX::Selector
|
|
7
28
|
private_constant :READABLE
|
8
29
|
private_constant :WRITABLE
|
9
30
|
|
10
|
-
|
11
|
-
# I/O monitor
|
12
|
-
#
|
13
|
-
class Monitor
|
14
|
-
attr_accessor :io, :interests, :readiness
|
15
|
-
|
16
|
-
def initialize(io, interests, reactor)
|
17
|
-
@io = io
|
18
|
-
@interests = interests
|
19
|
-
@reactor = reactor
|
20
|
-
@closed = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def readable?
|
24
|
-
READABLE.include?(@interests)
|
25
|
-
end
|
26
|
-
|
27
|
-
def writable?
|
28
|
-
WRITABLE.include?(@interests)
|
29
|
-
end
|
30
|
-
|
31
|
-
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
32
|
-
def close(deregister = true)
|
33
|
-
return if @closed
|
34
|
-
|
35
|
-
@closed = true
|
36
|
-
@reactor.deregister(@io) if deregister
|
37
|
-
end
|
38
|
-
|
39
|
-
def closed?
|
40
|
-
@closed
|
41
|
-
end
|
42
|
-
|
43
|
-
# :nocov:
|
44
|
-
def to_s
|
45
|
-
"#<#{self.class}: #{@io}(closed:#{@closed}) #{@interests} #{object_id.to_s(16)}>"
|
46
|
-
end
|
47
|
-
# :nocov:
|
48
|
-
end
|
31
|
+
using IOExtensions unless IO.method_defined?(:wait) && IO.instance_method(:wait).arity == 2
|
49
32
|
|
50
33
|
def initialize
|
51
|
-
@selectables =
|
52
|
-
@__r__, @__w__ = IO.pipe
|
53
|
-
@closed = false
|
34
|
+
@selectables = []
|
54
35
|
end
|
55
36
|
|
56
37
|
# deregisters +io+ from selectables.
|
57
38
|
def deregister(io)
|
58
|
-
|
59
|
-
monitor.close(false) if monitor
|
39
|
+
@selectables.delete(io)
|
60
40
|
end
|
61
41
|
|
62
|
-
# register +io
|
63
|
-
def register(io
|
64
|
-
|
65
|
-
if monitor
|
66
|
-
monitor.interests = interests
|
67
|
-
else
|
68
|
-
monitor = Monitor.new(io, interests, self)
|
69
|
-
@selectables[io] = monitor
|
70
|
-
end
|
71
|
-
monitor
|
72
|
-
end
|
42
|
+
# register +io+.
|
43
|
+
def register(io)
|
44
|
+
return if @selectables.include?(io)
|
73
45
|
|
74
|
-
|
75
|
-
|
76
|
-
#
|
77
|
-
def select(interval)
|
78
|
-
begin
|
79
|
-
r = [@__r__]
|
80
|
-
w = []
|
46
|
+
@selectables << io
|
47
|
+
end
|
81
48
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
49
|
+
private
|
50
|
+
|
51
|
+
READ_INTERESTS = %i[r rw].freeze
|
52
|
+
WRITE_INTERESTS = %i[w rw].freeze
|
53
|
+
|
54
|
+
def select_many(interval)
|
55
|
+
selectables, r, w = nil
|
56
|
+
|
57
|
+
# first, we group IOs based on interest type. On call to #interests however,
|
58
|
+
# things might already happen, and new IOs might be registered, so we might
|
59
|
+
# have to start all over again. We do this until we group all selectables
|
60
|
+
loop do
|
61
|
+
begin
|
62
|
+
r = nil
|
63
|
+
w = nil
|
64
|
+
|
65
|
+
selectables = @selectables
|
66
|
+
@selectables = []
|
67
|
+
|
68
|
+
selectables.each do |io|
|
69
|
+
interests = io.interests
|
70
|
+
|
71
|
+
(r ||= []) << io if READ_INTERESTS.include?(interests)
|
72
|
+
(w ||= []) << io if WRITE_INTERESTS.include?(interests)
|
73
|
+
end
|
74
|
+
|
75
|
+
if @selectables.empty?
|
76
|
+
@selectables = selectables
|
77
|
+
break
|
78
|
+
else
|
79
|
+
@selectables = [*selectables, @selectables]
|
80
|
+
end
|
81
|
+
rescue StandardError
|
82
|
+
@selectables = selectables if selectables
|
83
|
+
raise
|
86
84
|
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# TODO: what to do if there are no selectables?
|
87
88
|
|
89
|
+
begin
|
88
90
|
readers, writers = IO.select(r, w, nil, interval)
|
89
91
|
|
90
92
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
91
93
|
rescue IOError, SystemCallError
|
92
|
-
@selectables.reject!
|
94
|
+
@selectables.reject!(&:closed?)
|
93
95
|
retry
|
94
96
|
end
|
95
97
|
|
96
98
|
readers.each do |io|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
monitor = io.closed? ? @selectables.delete(io) : @selectables[io]
|
102
|
-
next unless monitor
|
103
|
-
|
104
|
-
monitor.readiness = writers.delete(io) ? :rw : :r
|
105
|
-
yield monitor
|
106
|
-
end
|
99
|
+
yield io
|
100
|
+
|
101
|
+
# so that we don't yield 2 times
|
102
|
+
writers.delete(io)
|
107
103
|
end if readers
|
108
104
|
|
109
105
|
writers.each do |io|
|
110
|
-
|
111
|
-
next unless monitor
|
112
|
-
|
113
|
-
# don't double run this, the last iteration might have run this task already
|
114
|
-
monitor.readiness = :w
|
115
|
-
yield monitor
|
106
|
+
yield io
|
116
107
|
end if writers
|
117
108
|
end
|
118
109
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
110
|
+
def select_one(interval)
|
111
|
+
io = @selectables.first
|
112
|
+
|
113
|
+
interests = io.interests
|
114
|
+
|
115
|
+
result = case interests
|
116
|
+
when :r then io.to_io.wait_readable(interval)
|
117
|
+
when :w then io.to_io.wait_writable(interval)
|
118
|
+
when :rw then io.to_io.wait(interval, :read_write)
|
119
|
+
when nil then return
|
120
|
+
end
|
121
|
+
|
122
|
+
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result
|
123
123
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
ensure
|
128
|
-
@closed = true
|
124
|
+
yield io
|
125
|
+
rescue IOError, SystemCallError
|
126
|
+
@selectables.reject!(&:closed?)
|
129
127
|
end
|
130
128
|
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
def select(interval, &block)
|
130
|
+
return select_one(interval, &block) if @selectables.size == 1
|
131
|
+
|
132
|
+
select_many(interval, &block)
|
134
133
|
end
|
134
|
+
|
135
|
+
public :select
|
135
136
|
end
|