httpx 1.4.4 → 1.5.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/doc/release_notes/1_5_0.md +126 -0
- data/lib/httpx/adapters/datadog.rb +24 -3
- data/lib/httpx/adapters/webmock.rb +1 -0
- data/lib/httpx/buffer.rb +16 -5
- data/lib/httpx/connection/http1.rb +8 -9
- data/lib/httpx/connection/http2.rb +48 -24
- data/lib/httpx/connection.rb +36 -19
- data/lib/httpx/errors.rb +2 -11
- data/lib/httpx/headers.rb +24 -23
- data/lib/httpx/io/ssl.rb +2 -1
- data/lib/httpx/io/tcp.rb +9 -7
- data/lib/httpx/io/unix.rb +1 -1
- data/lib/httpx/loggable.rb +13 -1
- data/lib/httpx/options.rb +63 -48
- data/lib/httpx/parser/http1.rb +1 -1
- data/lib/httpx/plugins/aws_sigv4.rb +1 -0
- data/lib/httpx/plugins/callbacks.rb +19 -6
- data/lib/httpx/plugins/circuit_breaker.rb +4 -3
- data/lib/httpx/plugins/cookies/jar.rb +0 -2
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +7 -4
- data/lib/httpx/plugins/cookies.rb +4 -4
- data/lib/httpx/plugins/follow_redirects.rb +4 -2
- data/lib/httpx/plugins/grpc/call.rb +1 -1
- data/lib/httpx/plugins/h2c.rb +7 -1
- data/lib/httpx/plugins/persistent.rb +22 -1
- data/lib/httpx/plugins/proxy/http.rb +3 -1
- data/lib/httpx/plugins/query.rb +35 -0
- data/lib/httpx/plugins/response_cache/file_store.rb +115 -15
- data/lib/httpx/plugins/response_cache/store.rb +7 -67
- data/lib/httpx/plugins/response_cache.rb +179 -29
- data/lib/httpx/plugins/retries.rb +26 -14
- data/lib/httpx/plugins/stream.rb +4 -2
- data/lib/httpx/plugins/stream_bidi.rb +315 -0
- data/lib/httpx/pool.rb +58 -5
- data/lib/httpx/request/body.rb +1 -1
- data/lib/httpx/request.rb +6 -2
- data/lib/httpx/resolver/https.rb +10 -4
- data/lib/httpx/resolver/native.rb +13 -13
- data/lib/httpx/resolver/resolver.rb +4 -0
- data/lib/httpx/resolver/system.rb +37 -14
- data/lib/httpx/resolver.rb +2 -2
- data/lib/httpx/response/body.rb +10 -21
- data/lib/httpx/response/buffer.rb +36 -12
- data/lib/httpx/response.rb +11 -1
- data/lib/httpx/selector.rb +16 -12
- data/lib/httpx/session.rb +79 -19
- data/lib/httpx/timers.rb +24 -16
- data/lib/httpx/transcoder/multipart/decoder.rb +4 -2
- data/lib/httpx/transcoder/multipart/encoder.rb +2 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +1 -1
- data/sig/chainable.rbs +5 -2
- data/sig/connection/http2.rbs +11 -2
- data/sig/connection.rbs +4 -4
- data/sig/errors.rbs +0 -3
- data/sig/headers.rbs +15 -10
- data/sig/httpx.rbs +5 -1
- data/sig/io/tcp.rbs +6 -0
- data/sig/loggable.rbs +2 -0
- data/sig/options.rbs +7 -1
- data/sig/plugins/cookies/cookie.rbs +1 -3
- data/sig/plugins/cookies/jar.rbs +4 -4
- data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/h2c.rbs +4 -0
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy.rbs +4 -0
- data/sig/plugins/query.rbs +18 -0
- data/sig/plugins/response_cache/file_store.rbs +19 -0
- data/sig/plugins/response_cache/store.rbs +13 -0
- data/sig/plugins/response_cache.rbs +41 -19
- data/sig/plugins/retries.rbs +4 -3
- data/sig/plugins/stream.rbs +5 -1
- data/sig/plugins/stream_bidi.rbs +68 -0
- data/sig/plugins/upgrade/h2.rbs +9 -0
- data/sig/plugins/upgrade.rbs +5 -0
- data/sig/pool.rbs +5 -0
- data/sig/punycode.rbs +5 -0
- data/sig/request.rbs +2 -0
- data/sig/resolver/https.rbs +3 -2
- data/sig/resolver/native.rbs +1 -2
- data/sig/resolver/resolver.rbs +11 -3
- data/sig/resolver/system.rbs +19 -2
- data/sig/resolver.rbs +11 -7
- data/sig/response/body.rbs +3 -4
- data/sig/response/buffer.rbs +2 -3
- data/sig/response.rbs +2 -2
- data/sig/selector.rbs +20 -10
- data/sig/session.rbs +14 -6
- data/sig/timers.rbs +5 -7
- data/sig/transcoder/multipart.rbs +4 -3
- metadata +13 -2
data/lib/httpx/headers.rb
CHANGED
@@ -11,20 +11,32 @@ module HTTPX
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(headers = nil)
|
14
|
+
if headers.nil? || headers.empty?
|
15
|
+
@headers = headers.to_h
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
14
19
|
@headers = {}
|
15
|
-
return unless headers
|
16
20
|
|
17
21
|
headers.each do |field, value|
|
18
|
-
|
19
|
-
|
22
|
+
field = downcased(field)
|
23
|
+
|
24
|
+
value = array_value(value)
|
25
|
+
|
26
|
+
current = @headers[field]
|
27
|
+
|
28
|
+
if current.nil?
|
29
|
+
@headers[field] = value
|
30
|
+
else
|
31
|
+
current.concat(value)
|
20
32
|
end
|
21
33
|
end
|
22
34
|
end
|
23
35
|
|
24
36
|
# cloned initialization
|
25
|
-
def initialize_clone(orig)
|
37
|
+
def initialize_clone(orig, **kwargs)
|
26
38
|
super
|
27
|
-
@headers = orig.instance_variable_get(:@headers).clone
|
39
|
+
@headers = orig.instance_variable_get(:@headers).clone(**kwargs)
|
28
40
|
end
|
29
41
|
|
30
42
|
# dupped initialization
|
@@ -39,17 +51,6 @@ module HTTPX
|
|
39
51
|
super
|
40
52
|
end
|
41
53
|
|
42
|
-
def same_headers?(headers)
|
43
|
-
@headers.empty? || begin
|
44
|
-
headers.each do |k, v|
|
45
|
-
next unless key?(k)
|
46
|
-
|
47
|
-
return false unless v == self[k]
|
48
|
-
end
|
49
|
-
true
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
54
|
# merges headers with another header-quack.
|
54
55
|
# the merge rule is, if the header already exists,
|
55
56
|
# ignore what the +other+ headers has. Otherwise, set
|
@@ -119,6 +120,10 @@ module HTTPX
|
|
119
120
|
other == to_hash
|
120
121
|
end
|
121
122
|
|
123
|
+
def empty?
|
124
|
+
@headers.empty?
|
125
|
+
end
|
126
|
+
|
122
127
|
# the headers store in Hash format
|
123
128
|
def to_hash
|
124
129
|
Hash[to_a]
|
@@ -137,7 +142,8 @@ module HTTPX
|
|
137
142
|
|
138
143
|
# :nocov:
|
139
144
|
def inspect
|
140
|
-
|
145
|
+
"#<#{self.class}:#{object_id} " \
|
146
|
+
"#{to_hash.inspect}>"
|
141
147
|
end
|
142
148
|
# :nocov:
|
143
149
|
|
@@ -160,12 +166,7 @@ module HTTPX
|
|
160
166
|
private
|
161
167
|
|
162
168
|
def array_value(value)
|
163
|
-
|
164
|
-
when Array
|
165
|
-
value.map { |val| String(val).strip }
|
166
|
-
else
|
167
|
-
[String(value).strip]
|
168
|
-
end
|
169
|
+
Array(value)
|
169
170
|
end
|
170
171
|
|
171
172
|
def downcased(field)
|
data/lib/httpx/io/ssl.rb
CHANGED
@@ -9,7 +9,8 @@ module HTTPX
|
|
9
9
|
# rubocop:disable Style/MutableConstant
|
10
10
|
TLS_OPTIONS = { alpn_protocols: %w[h2 http/1.1].freeze }
|
11
11
|
# https://github.com/jruby/jruby-openssl/issues/284
|
12
|
-
|
12
|
+
# TODO: remove when dropping support for jruby-openssl < 0.15.4
|
13
|
+
TLS_OPTIONS[:verify_hostname] = true if RUBY_ENGINE == "jruby" && JOpenSSL::VERSION < "0.15.4"
|
13
14
|
# rubocop:enable Style/MutableConstant
|
14
15
|
TLS_OPTIONS.freeze
|
15
16
|
|
data/lib/httpx/io/tcp.rb
CHANGED
@@ -167,7 +167,12 @@ module HTTPX
|
|
167
167
|
|
168
168
|
# :nocov:
|
169
169
|
def inspect
|
170
|
-
"#<#{self.class}
|
170
|
+
"#<#{self.class}:#{object_id} " \
|
171
|
+
"#{@ip}:#{@port} " \
|
172
|
+
"@state=#{@state} " \
|
173
|
+
"@hostname=#{@hostname} " \
|
174
|
+
"@addresses=#{@addresses} " \
|
175
|
+
"@state=#{@state}>"
|
171
176
|
end
|
172
177
|
# :nocov:
|
173
178
|
|
@@ -195,12 +200,9 @@ module HTTPX
|
|
195
200
|
end
|
196
201
|
|
197
202
|
def log_transition_state(nextstate)
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
else
|
202
|
-
"#{host} #{@state} -> #{nextstate}"
|
203
|
-
end
|
203
|
+
label = host
|
204
|
+
label = "#{label}(##{@io.fileno})" if nextstate == :connected
|
205
|
+
"#{label} #{@state} -> #{nextstate}"
|
204
206
|
end
|
205
207
|
end
|
206
208
|
end
|
data/lib/httpx/io/unix.rb
CHANGED
data/lib/httpx/loggable.rb
CHANGED
@@ -15,7 +15,13 @@ module HTTPX
|
|
15
15
|
|
16
16
|
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
17
17
|
|
18
|
-
def log(
|
18
|
+
def log(
|
19
|
+
level: @options.debug_level,
|
20
|
+
color: nil,
|
21
|
+
debug_level: @options.debug_level,
|
22
|
+
debug: @options.debug,
|
23
|
+
&msg
|
24
|
+
)
|
19
25
|
return unless debug_level >= level
|
20
26
|
|
21
27
|
debug_stream = debug || ($stderr if USE_DEBUG_LOG)
|
@@ -37,5 +43,11 @@ module HTTPX
|
|
37
43
|
def log_exception(ex, level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug)
|
38
44
|
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
39
45
|
end
|
46
|
+
|
47
|
+
def log_redact(text, should_redact = @options.debug_redact)
|
48
|
+
return text.to_s unless should_redact
|
49
|
+
|
50
|
+
"[REDACTED]"
|
51
|
+
end
|
40
52
|
end
|
41
53
|
end
|
data/lib/httpx/options.rb
CHANGED
@@ -27,10 +27,19 @@ module HTTPX
|
|
27
27
|
[Socket::AF_INET]
|
28
28
|
end.freeze
|
29
29
|
|
30
|
+
SET_TEMPORARY_NAME = ->(mod, pl = nil) do
|
31
|
+
if mod.respond_to?(:set_temporary_name) # ruby 3.4 only
|
32
|
+
name = mod.name || "#{mod.superclass.name}(plugin)"
|
33
|
+
name = "#{name}/#{pl}" if pl
|
34
|
+
mod.set_temporary_name(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
30
38
|
DEFAULT_OPTIONS = {
|
31
39
|
:max_requests => Float::INFINITY,
|
32
40
|
:debug => nil,
|
33
41
|
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
42
|
+
:debug_redact => ENV.key?("HTTPX_DEBUG_REDACT"),
|
34
43
|
:ssl => EMPTY_HASH,
|
35
44
|
:http2_settings => { settings_enable_push: 0 }.freeze,
|
36
45
|
:fallback_protocol => "http/1.1",
|
@@ -47,18 +56,18 @@ module HTTPX
|
|
47
56
|
write_timeout: WRITE_TIMEOUT,
|
48
57
|
request_timeout: REQUEST_TIMEOUT,
|
49
58
|
},
|
50
|
-
:headers_class => Class.new(Headers),
|
59
|
+
:headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
|
51
60
|
:headers => {},
|
52
61
|
:window_size => WINDOW_SIZE,
|
53
62
|
:buffer_size => BUFFER_SIZE,
|
54
63
|
:body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
|
55
|
-
:request_class => Class.new(Request),
|
56
|
-
:response_class => Class.new(Response),
|
57
|
-
:request_body_class => Class.new(Request::Body),
|
58
|
-
:response_body_class => Class.new(Response::Body),
|
59
|
-
:pool_class => Class.new(Pool),
|
60
|
-
:connection_class => Class.new(Connection),
|
61
|
-
:options_class => Class.new(self),
|
64
|
+
:request_class => Class.new(Request, &SET_TEMPORARY_NAME),
|
65
|
+
:response_class => Class.new(Response, &SET_TEMPORARY_NAME),
|
66
|
+
:request_body_class => Class.new(Request::Body, &SET_TEMPORARY_NAME),
|
67
|
+
:response_body_class => Class.new(Response::Body, &SET_TEMPORARY_NAME),
|
68
|
+
:pool_class => Class.new(Pool, &SET_TEMPORARY_NAME),
|
69
|
+
:connection_class => Class.new(Connection, &SET_TEMPORARY_NAME),
|
70
|
+
:options_class => Class.new(self, &SET_TEMPORARY_NAME),
|
62
71
|
:transport => nil,
|
63
72
|
:addresses => nil,
|
64
73
|
:persistent => false,
|
@@ -66,6 +75,7 @@ module HTTPX
|
|
66
75
|
:resolver_options => { cache: true }.freeze,
|
67
76
|
:pool_options => EMPTY_HASH,
|
68
77
|
:ip_families => ip_address_families,
|
78
|
+
:close_on_fork => false,
|
69
79
|
}.freeze
|
70
80
|
|
71
81
|
class << self
|
@@ -92,6 +102,7 @@ module HTTPX
|
|
92
102
|
#
|
93
103
|
# :debug :: an object which log messages are written to (must respond to <tt><<</tt>)
|
94
104
|
# :debug_level :: the log level of messages (can be 1, 2, or 3).
|
105
|
+
# :debug_redact :: whether header/body payload should be redacted (defaults to <tt>false</tt>).
|
95
106
|
# :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::IO::SSL)
|
96
107
|
# :http2_settings :: a hash of options to be passed to a HTTP2::Connection (ex: <tt>{ max_concurrent_streams: 2 }</tt>)
|
97
108
|
# :fallback_protocol :: version of HTTP protocol to use by default in the absence of protocol negotiation
|
@@ -128,6 +139,8 @@ module HTTPX
|
|
128
139
|
# :base_path :: path to prefix given relative paths with (ex: "/v2")
|
129
140
|
# :max_concurrent_requests :: max number of requests which can be set concurrently
|
130
141
|
# :max_requests :: max number of requests which can be made on socket before it reconnects.
|
142
|
+
# :close_on_fork :: whether the session automatically closes when the process is fork (defaults to <tt>false</tt>).
|
143
|
+
# it only works if the session is persistent (and ruby 3.1 or higher is used).
|
131
144
|
#
|
132
145
|
# This list of options are enhanced with each loaded plugin, see the plugin docs for details.
|
133
146
|
def initialize(options = {})
|
@@ -136,13 +149,18 @@ module HTTPX
|
|
136
149
|
end
|
137
150
|
|
138
151
|
def freeze
|
139
|
-
super
|
140
152
|
@origin.freeze
|
141
153
|
@base_path.freeze
|
142
154
|
@timeout.freeze
|
143
155
|
@headers.freeze
|
144
156
|
@addresses.freeze
|
145
157
|
@supported_compression_formats.freeze
|
158
|
+
@ssl.freeze
|
159
|
+
@http2_settings.freeze
|
160
|
+
@pool_options.freeze
|
161
|
+
@resolver_options.freeze
|
162
|
+
@ip_families.freeze
|
163
|
+
super
|
146
164
|
end
|
147
165
|
|
148
166
|
def option_origin(value)
|
@@ -165,41 +183,6 @@ module HTTPX
|
|
165
183
|
Array(value).map(&:to_s)
|
166
184
|
end
|
167
185
|
|
168
|
-
def option_max_concurrent_requests(value)
|
169
|
-
raise TypeError, ":max_concurrent_requests must be positive" unless value.positive?
|
170
|
-
|
171
|
-
value
|
172
|
-
end
|
173
|
-
|
174
|
-
def option_max_requests(value)
|
175
|
-
raise TypeError, ":max_requests must be positive" unless value.positive?
|
176
|
-
|
177
|
-
value
|
178
|
-
end
|
179
|
-
|
180
|
-
def option_window_size(value)
|
181
|
-
value = Integer(value)
|
182
|
-
|
183
|
-
raise TypeError, ":window_size must be positive" unless value.positive?
|
184
|
-
|
185
|
-
value
|
186
|
-
end
|
187
|
-
|
188
|
-
def option_buffer_size(value)
|
189
|
-
value = Integer(value)
|
190
|
-
|
191
|
-
raise TypeError, ":buffer_size must be positive" unless value.positive?
|
192
|
-
|
193
|
-
value
|
194
|
-
end
|
195
|
-
|
196
|
-
def option_body_threshold_size(value)
|
197
|
-
bytes = Integer(value)
|
198
|
-
raise TypeError, ":body_threshold_size must be positive" unless bytes.positive?
|
199
|
-
|
200
|
-
bytes
|
201
|
-
end
|
202
|
-
|
203
186
|
def option_transport(value)
|
204
187
|
transport = value.to_s
|
205
188
|
raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)
|
@@ -215,17 +198,42 @@ module HTTPX
|
|
215
198
|
Array(value)
|
216
199
|
end
|
217
200
|
|
201
|
+
# number options
|
202
|
+
%i[
|
203
|
+
max_concurrent_requests max_requests window_size buffer_size
|
204
|
+
body_threshold_size debug_level
|
205
|
+
].each do |option|
|
206
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
207
|
+
# converts +v+ into an Integer before setting the +#{option}+ option.
|
208
|
+
def option_#{option}(value) # def option_max_requests(v)
|
209
|
+
value = Integer(value) unless value.infinite?
|
210
|
+
raise TypeError, ":#{option} must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive?
|
211
|
+
|
212
|
+
value
|
213
|
+
end
|
214
|
+
OUT
|
215
|
+
end
|
216
|
+
|
217
|
+
# hashable options
|
218
|
+
%i[ssl http2_settings resolver_options pool_options].each do |option|
|
219
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
220
|
+
# converts +v+ into an Hash before setting the +#{option}+ option.
|
221
|
+
def option_#{option}(value) # def option_ssl(v)
|
222
|
+
Hash[value]
|
223
|
+
end
|
224
|
+
OUT
|
225
|
+
end
|
226
|
+
|
218
227
|
%i[
|
219
|
-
ssl http2_settings
|
220
228
|
request_class response_class headers_class request_body_class
|
221
229
|
response_body_class connection_class options_class
|
222
230
|
pool_class pool_options
|
223
|
-
io fallback_protocol debug
|
231
|
+
io fallback_protocol debug debug_redact resolver_class
|
224
232
|
compress_request_body decompress_response_body
|
225
|
-
persistent
|
233
|
+
persistent close_on_fork
|
226
234
|
].each do |method_name|
|
227
235
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
228
|
-
# sets +v+ as the value of
|
236
|
+
# sets +v+ as the value of the +#{method_name}+ option
|
229
237
|
def option_#{method_name}(v); v; end # def option_smth(v); v; end
|
230
238
|
OUT
|
231
239
|
end
|
@@ -296,35 +304,42 @@ module HTTPX
|
|
296
304
|
def extend_with_plugin_classes(pl)
|
297
305
|
if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
|
298
306
|
@request_class = @request_class.dup
|
307
|
+
SET_TEMPORARY_NAME[@request_class, pl]
|
299
308
|
@request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
|
300
309
|
@request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
301
310
|
end
|
302
311
|
if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
|
303
312
|
@response_class = @response_class.dup
|
313
|
+
SET_TEMPORARY_NAME[@response_class, pl]
|
304
314
|
@response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
305
315
|
@response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
306
316
|
end
|
307
317
|
if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
|
308
318
|
@headers_class = @headers_class.dup
|
319
|
+
SET_TEMPORARY_NAME[@headers_class, pl]
|
309
320
|
@headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
310
321
|
@headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
311
322
|
end
|
312
323
|
if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
|
313
324
|
@request_body_class = @request_body_class.dup
|
325
|
+
SET_TEMPORARY_NAME[@request_body_class, pl]
|
314
326
|
@request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
315
327
|
@request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
316
328
|
end
|
317
329
|
if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
|
318
330
|
@response_body_class = @response_body_class.dup
|
331
|
+
SET_TEMPORARY_NAME[@response_body_class, pl]
|
319
332
|
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
320
333
|
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
321
334
|
end
|
322
335
|
if defined?(pl::PoolMethods)
|
323
336
|
@pool_class = @pool_class.dup
|
337
|
+
SET_TEMPORARY_NAME[@pool_class, pl]
|
324
338
|
@pool_class.__send__(:include, pl::PoolMethods)
|
325
339
|
end
|
326
340
|
if defined?(pl::ConnectionMethods)
|
327
341
|
@connection_class = @connection_class.dup
|
342
|
+
SET_TEMPORARY_NAME[@connection_class, pl]
|
328
343
|
@connection_class.__send__(:include, pl::ConnectionMethods)
|
329
344
|
end
|
330
345
|
return unless defined?(pl::OptionsMethods)
|
data/lib/httpx/parser/http1.rb
CHANGED
@@ -8,6 +8,13 @@ module HTTPX
|
|
8
8
|
# https://gitlab.com/os85/httpx/-/wikis/Events
|
9
9
|
#
|
10
10
|
module Callbacks
|
11
|
+
CALLBACKS = %i[
|
12
|
+
connection_opened connection_closed
|
13
|
+
request_error
|
14
|
+
request_started request_body_chunk request_completed
|
15
|
+
response_started response_body_chunk response_completed
|
16
|
+
].freeze
|
17
|
+
|
11
18
|
# connection closed user-space errors happen after errors can be surfaced to requests,
|
12
19
|
# so they need to pierce through the scheduler, which is only possible by simulating an
|
13
20
|
# interrupt.
|
@@ -16,12 +23,7 @@ module HTTPX
|
|
16
23
|
module InstanceMethods
|
17
24
|
include HTTPX::Callbacks
|
18
25
|
|
19
|
-
|
20
|
-
connection_opened connection_closed
|
21
|
-
request_error
|
22
|
-
request_started request_body_chunk request_completed
|
23
|
-
response_started response_body_chunk response_completed
|
24
|
-
].each do |meth|
|
26
|
+
CALLBACKS.each do |meth|
|
25
27
|
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
26
28
|
def on_#{meth}(&blk) # def on_connection_opened(&blk)
|
27
29
|
on(:#{meth}, &blk) # on(:connection_opened, &blk)
|
@@ -32,6 +34,17 @@ module HTTPX
|
|
32
34
|
|
33
35
|
private
|
34
36
|
|
37
|
+
def branch(options, &blk)
|
38
|
+
super(options).tap do |sess|
|
39
|
+
CALLBACKS.each do |cb|
|
40
|
+
next unless callbacks_for?(cb)
|
41
|
+
|
42
|
+
sess.callbacks(cb).concat(callbacks(cb))
|
43
|
+
end
|
44
|
+
sess.wrap(&blk) if blk
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
35
48
|
def do_init_connection(connection, selector)
|
36
49
|
super
|
37
50
|
connection.on(:open) do
|
@@ -70,10 +70,11 @@ module HTTPX
|
|
70
70
|
short_circuit_responses
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
74
|
-
emit(:circuit_open, request) if try_circuit_open(request, response)
|
75
|
-
|
73
|
+
def set_request_callbacks(request)
|
76
74
|
super
|
75
|
+
request.on(:response) do |response|
|
76
|
+
emit(:circuit_open, request) if try_circuit_open(request, response)
|
77
|
+
end
|
77
78
|
end
|
78
79
|
|
79
80
|
def try_circuit_open(request, response)
|
@@ -83,7 +83,7 @@ module HTTPX
|
|
83
83
|
scanner.skip(RE_WSP)
|
84
84
|
|
85
85
|
name, value = scan_name_value(scanner, true)
|
86
|
-
value = nil if name.empty?
|
86
|
+
value = nil if name && name.empty?
|
87
87
|
|
88
88
|
attrs = {}
|
89
89
|
|
@@ -98,15 +98,18 @@ module HTTPX
|
|
98
98
|
|
99
99
|
aname, avalue = scan_name_value(scanner, true)
|
100
100
|
|
101
|
-
next if aname.empty? || value.nil?
|
101
|
+
next if (aname.nil? || aname.empty?) || value.nil?
|
102
102
|
|
103
103
|
aname.downcase!
|
104
104
|
|
105
105
|
case aname
|
106
106
|
when "expires"
|
107
|
+
next unless avalue
|
108
|
+
|
107
109
|
# RFC 6265 5.2.1
|
108
|
-
(avalue
|
110
|
+
(avalue = Time.parse(avalue)) || next
|
109
111
|
when "max-age"
|
112
|
+
next unless avalue
|
110
113
|
# RFC 6265 5.2.2
|
111
114
|
next unless /\A-?\d+\z/.match?(avalue)
|
112
115
|
|
@@ -119,7 +122,7 @@ module HTTPX
|
|
119
122
|
# RFC 6265 5.2.4
|
120
123
|
# A relative path must be ignored rather than normalizing it
|
121
124
|
# to "/".
|
122
|
-
next unless avalue.start_with?("/")
|
125
|
+
next unless avalue && avalue.start_with?("/")
|
123
126
|
when "secure", "httponly"
|
124
127
|
# RFC 6265 5.2.5, 5.2.6
|
125
128
|
avalue = true
|
@@ -48,15 +48,15 @@ module HTTPX
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
-
def
|
52
|
-
|
51
|
+
def set_request_callbacks(request)
|
52
|
+
super
|
53
|
+
request.on(:response) do |response|
|
54
|
+
next unless response && response.respond_to?(:headers) && (set_cookie = response.headers["set-cookie"])
|
53
55
|
|
54
56
|
log { "cookies: set-cookie is over #{Cookie::MAX_LENGTH}" } if set_cookie.bytesize > Cookie::MAX_LENGTH
|
55
57
|
|
56
58
|
@options.cookies.parse(set_cookie)
|
57
59
|
end
|
58
|
-
|
59
|
-
super
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -149,9 +149,11 @@ module HTTPX
|
|
149
149
|
retry_start = Utils.now
|
150
150
|
log { "redirecting after #{redirect_after} secs..." }
|
151
151
|
selector.after(redirect_after) do
|
152
|
-
if request.response
|
152
|
+
if (response = request.response)
|
153
|
+
response.finish!
|
154
|
+
retry_request.response = response
|
153
155
|
# request has terminated abruptly meanwhile
|
154
|
-
retry_request.emit(:response,
|
156
|
+
retry_request.emit(:response, response)
|
155
157
|
else
|
156
158
|
log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
|
157
159
|
send_request(retry_request, selector, options)
|
data/lib/httpx/plugins/h2c.rb
CHANGED
@@ -42,6 +42,12 @@ module HTTPX
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
module RequestMethods
|
46
|
+
def valid_h2c_verb?
|
47
|
+
VALID_H2C_VERBS.include?(@verb)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
45
51
|
module ConnectionMethods
|
46
52
|
using URIExtensions
|
47
53
|
|
@@ -53,7 +59,7 @@ module HTTPX
|
|
53
59
|
def send(request)
|
54
60
|
return super if @h2c_handshake
|
55
61
|
|
56
|
-
return super unless
|
62
|
+
return super unless request.valid_h2c_verb? && request.scheme == "http"
|
57
63
|
|
58
64
|
return super if @upgrade_protocol == "h2c"
|
59
65
|
|
@@ -24,7 +24,7 @@ module HTTPX
|
|
24
24
|
else
|
25
25
|
1
|
26
26
|
end
|
27
|
-
klass.plugin(:retries, max_retries: max_retries
|
27
|
+
klass.plugin(:retries, max_retries: max_retries)
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.extra_options(options)
|
@@ -34,6 +34,27 @@ module HTTPX
|
|
34
34
|
module InstanceMethods
|
35
35
|
private
|
36
36
|
|
37
|
+
def repeatable_request?(request, _)
|
38
|
+
super || begin
|
39
|
+
response = request.response
|
40
|
+
|
41
|
+
return false unless response && response.is_a?(ErrorResponse)
|
42
|
+
|
43
|
+
error = response.error
|
44
|
+
|
45
|
+
Retries::RECONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def retryable_error?(ex)
|
50
|
+
super &&
|
51
|
+
# under the persistent plugin rules, requests are only retried for connection related errors,
|
52
|
+
# which do not include request timeout related errors. This only gets overriden if the end user
|
53
|
+
# manually changed +:max_retries+ to something else, which means it is aware of the
|
54
|
+
# consequences.
|
55
|
+
(!ex.is_a?(RequestTimeoutError) || @options.max_retries != 1)
|
56
|
+
end
|
57
|
+
|
37
58
|
def get_current_selector
|
38
59
|
super(&nil) || begin
|
39
60
|
return unless block_given?
|
@@ -60,7 +60,7 @@ module HTTPX
|
|
60
60
|
return unless @io.connected?
|
61
61
|
|
62
62
|
@parser || begin
|
63
|
-
@parser =
|
63
|
+
@parser = parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
64
64
|
parser = @parser
|
65
65
|
parser.extend(ProxyParser)
|
66
66
|
parser.on(:response, &method(:__http_on_connect))
|
@@ -138,6 +138,8 @@ module HTTPX
|
|
138
138
|
else
|
139
139
|
pending = @pending + @parser.pending
|
140
140
|
while (req = pending.shift)
|
141
|
+
response.finish!
|
142
|
+
req.response = response
|
141
143
|
req.emit(:response, response)
|
142
144
|
end
|
143
145
|
reset
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin adds support for using the experimental QUERY HTTP method
|
7
|
+
#
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Query
|
9
|
+
module Query
|
10
|
+
def self.subplugins
|
11
|
+
{
|
12
|
+
retries: QueryRetries,
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def query(*uri, **options)
|
18
|
+
request("QUERY", uri, **options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module QueryRetries
|
23
|
+
module InstanceMethods
|
24
|
+
private
|
25
|
+
|
26
|
+
def repeatable_request?(request, options)
|
27
|
+
super || request.verb == "QUERY"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
register_plugin :query, Query
|
34
|
+
end
|
35
|
+
end
|