httpx 1.3.4 → 1.4.1
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_4_0.md +43 -0
- data/doc/release_notes/1_4_1.md +19 -0
- data/lib/httpx/adapters/datadog.rb +55 -83
- data/lib/httpx/adapters/faraday.rb +2 -0
- data/lib/httpx/adapters/webmock.rb +18 -6
- data/lib/httpx/callbacks.rb +0 -5
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http2.rb +12 -8
- data/lib/httpx/connection.rb +192 -22
- 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/grpc/grpc_encoding.rb +2 -0
- 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 +25 -26
- data/lib/httpx/request.rb +7 -1
- data/lib/httpx/resolver/https.rb +15 -20
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +66 -25
- data/lib/httpx/resolver/resolver.rb +59 -15
- data/lib/httpx/resolver/system.rb +31 -15
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response.rb +5 -3
- data/lib/httpx/selector.rb +160 -95
- data/lib/httpx/session.rb +273 -140
- data/lib/httpx/transcoder/body.rb +15 -31
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +14 -2
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- 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 +20 -21
- data/sig/callbacks.rbs +0 -1
- data/sig/chainable.rbs +4 -0
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +29 -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 -9
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +2 -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 +47 -18
- data/sig/transcoder/body.rbs +2 -4
- 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 +3 -3
- data/sig/transcoder/utils/deflater.rbs +3 -3
- metadata +12 -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,202 @@
|
|
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
|
58
|
+
|
59
|
+
selectables &= @selectables
|
60
|
+
end
|
61
|
+
end
|
50
62
|
|
51
|
-
|
52
|
-
|
63
|
+
def find_resolver(options)
|
64
|
+
res = @selectables.find do |c|
|
65
|
+
c.is_a?(Resolver::Resolver) && options == c.options
|
66
|
+
end
|
53
67
|
|
54
|
-
|
55
|
-
|
68
|
+
res.multi if res
|
69
|
+
end
|
56
70
|
|
57
|
-
|
58
|
-
|
59
|
-
return if (!r || r.empty?) && (!w || w.empty?) && !selectables.empty?
|
71
|
+
def each_connection(&block)
|
72
|
+
return enum_for(__method__) unless block
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@selectables = selectables if selectables
|
67
|
-
raise
|
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
|
+
# deregisters +io+ from selectables.
|
96
|
+
def deregister(io)
|
97
|
+
@selectables.delete(io)
|
98
|
+
end
|
87
99
|
|
88
|
-
|
89
|
-
|
90
|
-
|
100
|
+
# register +io+.
|
101
|
+
def register(io)
|
102
|
+
return if @selectables.include?(io)
|
91
103
|
|
92
|
-
|
93
|
-
else
|
94
|
-
readers.each(&block) if readers
|
104
|
+
@selectables << io
|
95
105
|
end
|
96
|
-
end
|
97
106
|
|
98
|
-
|
99
|
-
io = @selectables.first
|
107
|
+
private
|
100
108
|
|
101
|
-
|
109
|
+
def select(interval, &block)
|
110
|
+
# do not cause an infinite loop here.
|
111
|
+
#
|
112
|
+
# this may happen if timeout calculation actually triggered an error which causes
|
113
|
+
# the connections to be reaped (such as the total timeout error) before #select
|
114
|
+
# gets called.
|
115
|
+
return if interval.nil? && @selectables.empty?
|
102
116
|
|
103
|
-
|
117
|
+
return select_one(interval, &block) if @selectables.size == 1
|
104
118
|
|
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
|
119
|
+
select_many(interval, &block)
|
110
120
|
end
|
111
121
|
|
112
|
-
|
113
|
-
|
114
|
-
|
122
|
+
def select_many(interval, &block)
|
123
|
+
r, w = nil
|
124
|
+
|
125
|
+
# first, we group IOs based on interest type. On call to #interests however,
|
126
|
+
# things might already happen, and new IOs might be registered, so we might
|
127
|
+
# have to start all over again. We do this until we group all selectables
|
128
|
+
begin
|
129
|
+
@selectables.delete_if do |io|
|
130
|
+
interests = io.interests
|
131
|
+
|
132
|
+
(r ||= []) << io if READABLE.include?(interests)
|
133
|
+
(w ||= []) << io if WRITABLE.include?(interests)
|
134
|
+
|
135
|
+
io.state == :closed
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO: what to do if there are no selectables?
|
139
|
+
|
140
|
+
readers, writers = IO.select(r, w, nil, interval)
|
141
|
+
|
142
|
+
if readers.nil? && writers.nil? && interval
|
143
|
+
[*r, *w].each { |io| io.handle_socket_timeout(interval) }
|
144
|
+
return
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if writers
|
149
|
+
readers.each do |io|
|
150
|
+
yield io
|
151
|
+
|
152
|
+
# so that we don't yield 2 times
|
153
|
+
writers.delete(io)
|
154
|
+
end if readers
|
155
|
+
|
156
|
+
writers.each(&block)
|
157
|
+
else
|
158
|
+
readers.each(&block) if readers
|
159
|
+
end
|
115
160
|
end
|
116
|
-
# raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
|
117
161
|
|
118
|
-
|
119
|
-
|
120
|
-
@selectables.reject!(&:closed?)
|
121
|
-
raise unless @selectables.empty?
|
122
|
-
end
|
162
|
+
def select_one(interval)
|
163
|
+
io = @selectables.first
|
123
164
|
|
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?
|
165
|
+
return unless io
|
131
166
|
|
132
|
-
|
167
|
+
interests = io.interests
|
133
168
|
|
134
|
-
|
135
|
-
|
169
|
+
result = case interests
|
170
|
+
when :r then io.to_io.wait_readable(interval)
|
171
|
+
when :w then io.to_io.wait_writable(interval)
|
172
|
+
when :rw then io.to_io.wait(interval, :read_write)
|
173
|
+
when nil then return
|
174
|
+
end
|
175
|
+
|
176
|
+
unless result || interval.nil?
|
177
|
+
io.handle_socket_timeout(interval)
|
178
|
+
return
|
179
|
+
end
|
180
|
+
# raise TimeoutError.new(interval, "timed out while waiting on select")
|
181
|
+
|
182
|
+
yield io
|
183
|
+
# rescue IOError, SystemCallError
|
184
|
+
# @selectables.reject!(&:closed?)
|
185
|
+
# raise unless @selectables.empty?
|
186
|
+
end
|
136
187
|
|
137
|
-
|
188
|
+
def next_timeout
|
189
|
+
[
|
190
|
+
@timers.wait_interval,
|
191
|
+
@selectables.filter_map(&:timeout).min,
|
192
|
+
].compact.min
|
193
|
+
end
|
194
|
+
|
195
|
+
def emit_error(e)
|
196
|
+
@selectables.each do |c|
|
197
|
+
next if c.is_a?(Resolver::Resolver)
|
198
|
+
|
199
|
+
c.emit(:error, e)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
138
203
|
end
|