httpx 1.3.4 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/1_4_0.md +43 -0
- data/lib/httpx/adapters/faraday.rb +2 -0
- data/lib/httpx/adapters/webmock.rb +11 -5
- data/lib/httpx/callbacks.rb +0 -5
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http2.rb +11 -7
- data/lib/httpx/connection.rb +128 -16
- data/lib/httpx/errors.rb +12 -0
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +26 -16
- data/lib/httpx/plugins/aws_sigv4.rb +31 -16
- data/lib/httpx/plugins/callbacks.rb +12 -2
- data/lib/httpx/plugins/circuit_breaker.rb +0 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/expect.rb +4 -3
- data/lib/httpx/plugins/follow_redirects.rb +7 -8
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +27 -0
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +17 -19
- data/lib/httpx/plugins/proxy.rb +91 -93
- data/lib/httpx/plugins/retries.rb +5 -8
- data/lib/httpx/plugins/upgrade.rb +5 -10
- data/lib/httpx/plugins/webdav.rb +6 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pool.rb +73 -244
- data/lib/httpx/request/body.rb +16 -12
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/https.rb +12 -19
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +36 -13
- data/lib/httpx/resolver/resolver.rb +49 -11
- data/lib/httpx/resolver/system.rb +29 -11
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response.rb +5 -3
- data/lib/httpx/selector.rb +164 -95
- data/lib/httpx/session.rb +296 -139
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +14 -2
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder/utils/inflater.rb +2 -0
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +19 -20
- data/sig/callbacks.rbs +0 -1
- data/sig/chainable.rbs +4 -0
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +14 -3
- data/sig/errors.rbs +6 -0
- data/sig/loggable.rbs +2 -0
- data/sig/options.rbs +7 -0
- data/sig/plugins/aws_sigv4.rbs +8 -2
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +9 -0
- data/sig/plugins/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -11
- data/sig/plugins/push_promise.rbs +3 -0
- data/sig/plugins/rate_limiter.rbs +2 -0
- data/sig/plugins/retries.rbs +1 -1
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +25 -33
- data/sig/request/body.rbs +5 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +21 -2
- data/sig/resolver.rbs +5 -1
- data/sig/response/buffer.rbs +1 -1
- data/sig/selector.rbs +30 -4
- data/sig/session.rbs +45 -18
- data/sig/transcoder/body.rbs +1 -1
- data/sig/transcoder/chunker.rbs +1 -1
- data/sig/transcoder/deflate.rbs +1 -0
- data/sig/transcoder/form.rbs +8 -0
- data/sig/transcoder/gzip.rbs +4 -1
- data/sig/transcoder/utils/body_reader.rbs +2 -2
- data/sig/transcoder/utils/deflater.rbs +2 -2
- metadata +10 -4
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/xml.rbs +0 -22
data/lib/httpx/selector.rb
CHANGED
@@ -2,137 +2,206 @@
|
|
2
2
|
|
3
3
|
require "io/wait"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module HTTPX
|
6
|
+
class Selector
|
7
|
+
extend Forwardable
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
READABLE = %i[rw r].freeze
|
10
|
+
WRITABLE = %i[rw w].freeze
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
12
|
+
private_constant :READABLE
|
13
|
+
private_constant :WRITABLE
|
15
14
|
|
16
|
-
|
17
|
-
def deregister(io)
|
18
|
-
@selectables.delete(io)
|
19
|
-
end
|
15
|
+
def_delegator :@timers, :after
|
20
16
|
|
21
|
-
|
22
|
-
def register(io)
|
23
|
-
return if @selectables.include?(io)
|
17
|
+
def_delegator :@selectables, :empty?
|
24
18
|
|
25
|
-
|
26
|
-
|
19
|
+
def initialize
|
20
|
+
@timers = Timers.new
|
21
|
+
@selectables = []
|
22
|
+
end
|
27
23
|
|
28
|
-
|
24
|
+
def each(&blk)
|
25
|
+
@selectables.each(&blk)
|
26
|
+
end
|
29
27
|
|
30
|
-
|
31
|
-
|
28
|
+
def next_tick
|
29
|
+
catch(:jump_tick) do
|
30
|
+
timeout = next_timeout
|
31
|
+
if timeout && timeout.negative?
|
32
|
+
@timers.fire
|
33
|
+
throw(:jump_tick)
|
34
|
+
end
|
32
35
|
|
33
|
-
# first, we group IOs based on interest type. On call to #interests however,
|
34
|
-
# things might already happen, and new IOs might be registered, so we might
|
35
|
-
# have to start all over again. We do this until we group all selectables
|
36
|
-
begin
|
37
|
-
loop do
|
38
36
|
begin
|
39
|
-
|
40
|
-
|
37
|
+
select(timeout, &:call)
|
38
|
+
@timers.fire
|
39
|
+
rescue TimeoutError => e
|
40
|
+
@timers.fire(e)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
rescue StandardError => e
|
44
|
+
emit_error(e)
|
45
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
46
|
+
each_connection(&:force_reset)
|
47
|
+
raise
|
48
|
+
end
|
41
49
|
|
42
|
-
|
43
|
-
|
50
|
+
def terminate
|
51
|
+
# array may change during iteration
|
52
|
+
selectables = @selectables.reject(&:inflight?)
|
44
53
|
|
45
|
-
|
46
|
-
interests = io.interests
|
54
|
+
selectables.each(&:terminate)
|
47
55
|
|
48
|
-
|
49
|
-
|
56
|
+
until selectables.empty?
|
57
|
+
next_tick
|
50
58
|
|
51
|
-
|
52
|
-
|
59
|
+
selectables &= @selectables
|
60
|
+
end
|
61
|
+
end
|
53
62
|
|
54
|
-
|
55
|
-
|
63
|
+
def find_resolver(options)
|
64
|
+
res = @selectables.find do |c|
|
65
|
+
c.is_a?(Resolver::Resolver) && options == c.options
|
66
|
+
end
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
return if (!r || r.empty?) && (!w || w.empty?) && !selectables.empty?
|
68
|
+
res.multi if res
|
69
|
+
end
|
60
70
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
def each_connection(&block)
|
72
|
+
return enum_for(__method__) unless block
|
73
|
+
|
74
|
+
@selectables.each do |c|
|
75
|
+
if c.is_a?(Resolver::Resolver)
|
76
|
+
c.each_connection(&block)
|
77
|
+
else
|
78
|
+
yield c
|
68
79
|
end
|
69
80
|
end
|
81
|
+
end
|
70
82
|
|
71
|
-
|
72
|
-
|
73
|
-
|
83
|
+
def find_connection(request_uri, options)
|
84
|
+
each_connection.find do |connection|
|
85
|
+
connection.match?(request_uri, options)
|
86
|
+
end
|
87
|
+
end
|
74
88
|
|
75
|
-
|
76
|
-
|
77
|
-
|
89
|
+
def find_mergeable_connection(connection)
|
90
|
+
each_connection.find do |ch|
|
91
|
+
ch != connection && ch.mergeable?(connection)
|
78
92
|
end
|
79
|
-
rescue IOError, SystemCallError
|
80
|
-
@selectables.reject!(&:closed?)
|
81
|
-
retry
|
82
93
|
end
|
83
94
|
|
84
|
-
|
85
|
-
|
86
|
-
|
95
|
+
def empty?
|
96
|
+
@selectables.empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
# deregisters +io+ from selectables.
|
100
|
+
def deregister(io)
|
101
|
+
@selectables.delete(io)
|
102
|
+
end
|
87
103
|
|
88
|
-
|
89
|
-
|
90
|
-
|
104
|
+
# register +io+.
|
105
|
+
def register(io)
|
106
|
+
return if @selectables.include?(io)
|
91
107
|
|
92
|
-
|
93
|
-
else
|
94
|
-
readers.each(&block) if readers
|
108
|
+
@selectables << io
|
95
109
|
end
|
96
|
-
end
|
97
110
|
|
98
|
-
|
99
|
-
io = @selectables.first
|
111
|
+
private
|
100
112
|
|
101
|
-
|
113
|
+
def select(interval, &block)
|
114
|
+
# do not cause an infinite loop here.
|
115
|
+
#
|
116
|
+
# this may happen if timeout calculation actually triggered an error which causes
|
117
|
+
# the connections to be reaped (such as the total timeout error) before #select
|
118
|
+
# gets called.
|
119
|
+
return if interval.nil? && @selectables.empty?
|
102
120
|
|
103
|
-
|
121
|
+
return select_one(interval, &block) if @selectables.size == 1
|
104
122
|
|
105
|
-
|
106
|
-
when :r then io.to_io.wait_readable(interval)
|
107
|
-
when :w then io.to_io.wait_writable(interval)
|
108
|
-
when :rw then io.to_io.wait(interval, :read_write)
|
109
|
-
when nil then return
|
123
|
+
select_many(interval, &block)
|
110
124
|
end
|
111
125
|
|
112
|
-
|
113
|
-
|
114
|
-
|
126
|
+
def select_many(interval, &block)
|
127
|
+
r, w = nil
|
128
|
+
|
129
|
+
# first, we group IOs based on interest type. On call to #interests however,
|
130
|
+
# things might already happen, and new IOs might be registered, so we might
|
131
|
+
# have to start all over again. We do this until we group all selectables
|
132
|
+
begin
|
133
|
+
@selectables.delete_if do |io|
|
134
|
+
interests = io.interests
|
135
|
+
|
136
|
+
(r ||= []) << io if READABLE.include?(interests)
|
137
|
+
(w ||= []) << io if WRITABLE.include?(interests)
|
138
|
+
|
139
|
+
io.state == :closed
|
140
|
+
end
|
141
|
+
|
142
|
+
# TODO: what to do if there are no selectables?
|
143
|
+
|
144
|
+
readers, writers = IO.select(r, w, nil, interval)
|
145
|
+
|
146
|
+
if readers.nil? && writers.nil? && interval
|
147
|
+
[*r, *w].each { |io| io.handle_socket_timeout(interval) }
|
148
|
+
return
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
if writers
|
153
|
+
readers.each do |io|
|
154
|
+
yield io
|
155
|
+
|
156
|
+
# so that we don't yield 2 times
|
157
|
+
writers.delete(io)
|
158
|
+
end if readers
|
159
|
+
|
160
|
+
writers.each(&block)
|
161
|
+
else
|
162
|
+
readers.each(&block) if readers
|
163
|
+
end
|
115
164
|
end
|
116
|
-
# raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
|
117
165
|
|
118
|
-
|
119
|
-
|
120
|
-
@selectables.reject!(&:closed?)
|
121
|
-
raise unless @selectables.empty?
|
122
|
-
end
|
166
|
+
def select_one(interval)
|
167
|
+
io = @selectables.first
|
123
168
|
|
124
|
-
|
125
|
-
# do not cause an infinite loop here.
|
126
|
-
#
|
127
|
-
# this may happen if timeout calculation actually triggered an error which causes
|
128
|
-
# the connections to be reaped (such as the total timeout error) before #select
|
129
|
-
# gets called.
|
130
|
-
return if interval.nil? && @selectables.empty?
|
169
|
+
return unless io
|
131
170
|
|
132
|
-
|
171
|
+
interests = io.interests
|
133
172
|
|
134
|
-
|
135
|
-
|
173
|
+
result = case interests
|
174
|
+
when :r then io.to_io.wait_readable(interval)
|
175
|
+
when :w then io.to_io.wait_writable(interval)
|
176
|
+
when :rw then io.to_io.wait(interval, :read_write)
|
177
|
+
when nil then return
|
178
|
+
end
|
179
|
+
|
180
|
+
unless result || interval.nil?
|
181
|
+
io.handle_socket_timeout(interval)
|
182
|
+
return
|
183
|
+
end
|
184
|
+
# raise TimeoutError.new(interval, "timed out while waiting on select")
|
185
|
+
|
186
|
+
yield io
|
187
|
+
# rescue IOError, SystemCallError
|
188
|
+
# @selectables.reject!(&:closed?)
|
189
|
+
# raise unless @selectables.empty?
|
190
|
+
end
|
136
191
|
|
137
|
-
|
192
|
+
def next_timeout
|
193
|
+
[
|
194
|
+
@timers.wait_interval,
|
195
|
+
@selectables.filter_map(&:timeout).min,
|
196
|
+
].compact.min
|
197
|
+
end
|
198
|
+
|
199
|
+
def emit_error(e)
|
200
|
+
@selectables.each do |c|
|
201
|
+
next if c.is_a?(Resolver::Resolver)
|
202
|
+
|
203
|
+
c.emit(:error, e)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
138
207
|
end
|