httpx 0.11.0 → 0.13.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 +2 -2
- data/doc/release_notes/0_11_1.md +5 -0
- data/doc/release_notes/0_11_2.md +5 -0
- data/doc/release_notes/0_11_3.md +5 -0
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- data/lib/httpx.rb +2 -1
- data/lib/httpx/adapters/faraday.rb +4 -6
- data/lib/httpx/altsvc.rb +1 -0
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection.rb +80 -28
- data/lib/httpx/connection/http1.rb +19 -6
- data/lib/httpx/connection/http2.rb +32 -25
- data/lib/httpx/io.rb +16 -3
- data/lib/httpx/io/ssl.rb +35 -24
- data/lib/httpx/io/tcp.rb +48 -28
- data/lib/httpx/io/tls.rb +218 -0
- data/lib/httpx/io/tls/box.rb +365 -0
- data/lib/httpx/io/tls/context.rb +199 -0
- data/lib/httpx/io/tls/ffi.rb +390 -0
- data/lib/httpx/io/udp.rb +3 -2
- data/lib/httpx/io/unix.rb +27 -12
- data/lib/httpx/options.rb +11 -23
- data/lib/httpx/parser/http1.rb +4 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +81 -0
- data/lib/httpx/plugins/aws_sigv4.rb +218 -0
- data/lib/httpx/plugins/compression.rb +21 -9
- data/lib/httpx/plugins/compression/brotli.rb +8 -6
- data/lib/httpx/plugins/compression/deflate.rb +4 -7
- data/lib/httpx/plugins/compression/gzip.rb +2 -2
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +1 -1
- data/lib/httpx/plugins/digest_authentication.rb +1 -1
- data/lib/httpx/plugins/follow_redirects.rb +1 -1
- data/lib/httpx/plugins/h2c.rb +43 -58
- data/lib/httpx/plugins/internal_telemetry.rb +93 -0
- data/lib/httpx/plugins/multipart.rb +2 -0
- data/lib/httpx/plugins/multipart/encoder.rb +4 -9
- data/lib/httpx/plugins/proxy.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +1 -1
- data/lib/httpx/plugins/proxy/socks4.rb +8 -0
- data/lib/httpx/plugins/proxy/socks5.rb +8 -0
- data/lib/httpx/plugins/push_promise.rb +3 -2
- data/lib/httpx/plugins/retries.rb +2 -2
- data/lib/httpx/plugins/stream.rb +6 -6
- data/lib/httpx/plugins/upgrade.rb +83 -0
- data/lib/httpx/plugins/upgrade/h2.rb +54 -0
- data/lib/httpx/pool.rb +14 -6
- data/lib/httpx/registry.rb +1 -7
- data/lib/httpx/request.rb +11 -1
- data/lib/httpx/resolver/https.rb +3 -11
- data/lib/httpx/response.rb +14 -7
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +25 -2
- data/lib/httpx/transcoder/body.rb +3 -5
- data/lib/httpx/version.rb +1 -1
- data/sig/chainable.rbs +2 -1
- data/sig/connection/http1.rbs +3 -2
- data/sig/connection/http2.rbs +5 -3
- data/sig/options.rbs +7 -20
- data/sig/plugins/aws_sdk_authentication.rbs +17 -0
- data/sig/plugins/aws_sigv4.rbs +64 -0
- data/sig/plugins/compression.rbs +5 -3
- data/sig/plugins/compression/brotli.rbs +1 -1
- data/sig/plugins/compression/deflate.rbs +1 -1
- data/sig/plugins/compression/gzip.rbs +1 -1
- data/sig/plugins/cookies.rbs +0 -1
- data/sig/plugins/digest_authentication.rbs +0 -1
- data/sig/plugins/expect.rbs +0 -2
- data/sig/plugins/follow_redirects.rbs +0 -2
- data/sig/plugins/h2c.rbs +5 -10
- data/sig/plugins/persistent.rbs +0 -1
- data/sig/plugins/proxy.rbs +0 -1
- data/sig/plugins/push_promise.rbs +1 -1
- data/sig/plugins/retries.rbs +0 -4
- data/sig/plugins/upgrade.rbs +23 -0
- data/sig/response.rbs +3 -1
- metadata +48 -26
@@ -17,7 +17,7 @@ module HTTPX
|
|
17
17
|
Errno::ECONNRESET,
|
18
18
|
Errno::ECONNABORTED,
|
19
19
|
Errno::EPIPE,
|
20
|
-
(
|
20
|
+
(TLSError if defined?(TLSError)),
|
21
21
|
TimeoutError,
|
22
22
|
Parser::Error,
|
23
23
|
Errno::EINVAL,
|
@@ -55,7 +55,7 @@ module HTTPX
|
|
55
55
|
|
56
56
|
module InstanceMethods
|
57
57
|
def max_retries(n)
|
58
|
-
|
58
|
+
with(max_retries: n.to_i)
|
59
59
|
end
|
60
60
|
|
61
61
|
private
|
data/lib/httpx/plugins/stream.rb
CHANGED
@@ -5,6 +5,8 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin adds support for stream response (text/event-stream).
|
7
7
|
#
|
8
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/Stream
|
9
|
+
#
|
8
10
|
module Stream
|
9
11
|
module InstanceMethods
|
10
12
|
private
|
@@ -31,7 +33,7 @@ module HTTPX
|
|
31
33
|
end
|
32
34
|
|
33
35
|
module ResponseBodyMethods
|
34
|
-
def initialize(
|
36
|
+
def initialize(*)
|
35
37
|
super
|
36
38
|
@stream = @response.stream
|
37
39
|
end
|
@@ -119,11 +121,9 @@ module HTTPX
|
|
119
121
|
end
|
120
122
|
|
121
123
|
def method_missing(meth, *args, &block)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
super
|
126
|
-
end
|
124
|
+
return super unless @options.response_class.public_method_defined?(meth)
|
125
|
+
|
126
|
+
response.__send__(meth, *args, &block)
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin helps negotiating a new protocol from an HTTP/1.1 connection, via the
|
7
|
+
# Upgrade header.
|
8
|
+
#
|
9
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/Upgrade
|
10
|
+
#
|
11
|
+
module Upgrade
|
12
|
+
class << self
|
13
|
+
def configure(klass)
|
14
|
+
klass.plugin(:"upgrade/h2")
|
15
|
+
end
|
16
|
+
|
17
|
+
def extra_options(options)
|
18
|
+
upgrade_handlers = Module.new do
|
19
|
+
extend Registry
|
20
|
+
end
|
21
|
+
|
22
|
+
Class.new(options.class) do
|
23
|
+
def_option(:upgrade_handlers) do |encs|
|
24
|
+
raise Error, ":upgrade_handlers must be a registry" unless encs.respond_to?(:registry)
|
25
|
+
|
26
|
+
encs
|
27
|
+
end
|
28
|
+
end.new(options).merge(upgrade_handlers: upgrade_handlers)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
def fetch_response(request, connections, options)
|
34
|
+
response = super
|
35
|
+
|
36
|
+
if response && response.headers.key?("upgrade")
|
37
|
+
|
38
|
+
upgrade_protocol = response.headers["upgrade"].split(/ *, */).first
|
39
|
+
|
40
|
+
return response unless upgrade_protocol && options.upgrade_handlers.registry.key?(upgrade_protocol)
|
41
|
+
|
42
|
+
protocol_handler = options.upgrade_handlers.registry(upgrade_protocol)
|
43
|
+
|
44
|
+
return response unless protocol_handler
|
45
|
+
|
46
|
+
log { "upgrading to #{upgrade_protocol}..." }
|
47
|
+
connection = find_connection(request, connections, options)
|
48
|
+
connections << connection unless connections.include?(connection)
|
49
|
+
|
50
|
+
# do not upgrade already upgraded connections
|
51
|
+
return if connection.upgrade_protocol == upgrade_protocol
|
52
|
+
|
53
|
+
protocol_handler.call(connection, request, response)
|
54
|
+
|
55
|
+
# keep in the loop if the server is switching, unless
|
56
|
+
# the connection has been hijacked, in which case you want
|
57
|
+
# to terminante immediately
|
58
|
+
return if response.status == 101 && !connection.hijacked
|
59
|
+
end
|
60
|
+
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
64
|
+
def close(*args)
|
65
|
+
return super if args.empty?
|
66
|
+
|
67
|
+
connections, = args
|
68
|
+
|
69
|
+
pool.close(connections.reject(&:hijacked))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module ConnectionMethods
|
74
|
+
attr_reader :upgrade_protocol, :hijacked
|
75
|
+
|
76
|
+
def hijack_io
|
77
|
+
@hijacked = true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
register_plugin(:upgrade, Upgrade)
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin adds support for upgrading an HTTP/1.1 connection to HTTP/2
|
7
|
+
# via an Upgrade: h2 response declaration
|
8
|
+
#
|
9
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/Upgrade#h2
|
10
|
+
#
|
11
|
+
module H2
|
12
|
+
class << self
|
13
|
+
def configure(klass)
|
14
|
+
klass.default_options.upgrade_handlers.register "h2", self
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(connection, _request, _response)
|
18
|
+
connection.upgrade_to_h2
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ConnectionMethods
|
23
|
+
using URIExtensions
|
24
|
+
|
25
|
+
def upgrade_to_h2
|
26
|
+
prev_parser = @parser
|
27
|
+
|
28
|
+
if prev_parser
|
29
|
+
prev_parser.reset
|
30
|
+
@inflight -= prev_parser.requests.size
|
31
|
+
end
|
32
|
+
|
33
|
+
@parser = Connection::HTTP2.new(@write_buffer, @options)
|
34
|
+
set_parser_callbacks(@parser)
|
35
|
+
@upgrade_protocol = :h2
|
36
|
+
|
37
|
+
# what's happening here:
|
38
|
+
# a deviation from the state machine is done to perform the actions when a
|
39
|
+
# connection is closed, without transitioning, so the connection is kept in the pool.
|
40
|
+
# the state is reset to initial, so that the socket reconnect works out of the box,
|
41
|
+
# while the parser is already here.
|
42
|
+
purge_after_closed
|
43
|
+
transition(:idle)
|
44
|
+
|
45
|
+
prev_parser.requests.each do |req|
|
46
|
+
req.transition(:idle)
|
47
|
+
send(req)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
register_plugin(:"upgrade/h2", H2)
|
53
|
+
end
|
54
|
+
end
|
data/lib/httpx/pool.rb
CHANGED
@@ -64,11 +64,6 @@ module HTTPX
|
|
64
64
|
connection.on(:open) do
|
65
65
|
@connected_connections += 1
|
66
66
|
end
|
67
|
-
connection.on(:unreachable) do
|
68
|
-
resolver = find_resolver_for(connection)
|
69
|
-
resolver.uncache(connection) if resolver
|
70
|
-
resolve_connection(connection)
|
71
|
-
end
|
72
67
|
end
|
73
68
|
|
74
69
|
# opens a connection to the IP reachable through +uri+.
|
@@ -85,6 +80,20 @@ module HTTPX
|
|
85
80
|
|
86
81
|
def resolve_connection(connection)
|
87
82
|
@connections << connection unless @connections.include?(connection)
|
83
|
+
|
84
|
+
if connection.addresses || connection.state == :open
|
85
|
+
#
|
86
|
+
# there are two cases in which we want to activate initialization of
|
87
|
+
# connection immediately:
|
88
|
+
#
|
89
|
+
# 1. when the connection already has addresses, i.e. it doesn't need to
|
90
|
+
# resolve a name (not the same as name being an IP, yet)
|
91
|
+
# 2. when the connection is initialized with an external already open IO.
|
92
|
+
#
|
93
|
+
on_resolver_connection(connection)
|
94
|
+
return
|
95
|
+
end
|
96
|
+
|
88
97
|
resolver = find_resolver_for(connection)
|
89
98
|
resolver << connection
|
90
99
|
return if resolver.empty?
|
@@ -135,7 +144,6 @@ module HTTPX
|
|
135
144
|
connection.on(:close) do
|
136
145
|
unregister_connection(connection)
|
137
146
|
end
|
138
|
-
return if connection.state == :open
|
139
147
|
end
|
140
148
|
|
141
149
|
def unregister_connection(connection)
|
data/lib/httpx/registry.rb
CHANGED
@@ -62,13 +62,7 @@ module HTTPX
|
|
62
62
|
handler = @registry.fetch(tag)
|
63
63
|
raise(Error, "#{tag} is not registered in #{self}") unless handler
|
64
64
|
|
65
|
-
|
66
|
-
when Symbol, String
|
67
|
-
obj = const_get(handler)
|
68
|
-
@registry[tag] = obj
|
69
|
-
else
|
70
|
-
handler
|
71
|
-
end
|
65
|
+
handler
|
72
66
|
end
|
73
67
|
|
74
68
|
# @param [Object] tag the identifier for the handler in the registry
|
data/lib/httpx/request.rb
CHANGED
@@ -217,6 +217,16 @@ module HTTPX
|
|
217
217
|
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
218
218
|
end
|
219
219
|
# :nocov:
|
220
|
+
|
221
|
+
def respond_to_missing?(meth, *args)
|
222
|
+
@body.respond_to?(meth, *args) || super
|
223
|
+
end
|
224
|
+
|
225
|
+
def method_missing(meth, *args, &block)
|
226
|
+
return super unless @body.respond_to?(meth)
|
227
|
+
|
228
|
+
@body.__send__(meth, *args, &block)
|
229
|
+
end
|
220
230
|
end
|
221
231
|
|
222
232
|
def transition(nextstate)
|
@@ -247,7 +257,7 @@ module HTTPX
|
|
247
257
|
return if @state == :expect
|
248
258
|
end
|
249
259
|
@state = nextstate
|
250
|
-
emit(@state)
|
260
|
+
emit(@state, self)
|
251
261
|
nil
|
252
262
|
end
|
253
263
|
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -55,20 +55,12 @@ module HTTPX
|
|
55
55
|
early_resolve(connection) || resolve(connection)
|
56
56
|
end
|
57
57
|
|
58
|
-
def timeout
|
59
|
-
@connections.map(&:timeout).min
|
60
|
-
end
|
61
|
-
|
62
58
|
def closed?
|
63
|
-
|
64
|
-
|
65
|
-
resolver_connection.closed?
|
59
|
+
true
|
66
60
|
end
|
67
61
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
resolver_connection.__send__(__method__)
|
62
|
+
def empty?
|
63
|
+
true
|
72
64
|
end
|
73
65
|
|
74
66
|
private
|
data/lib/httpx/response.rb
CHANGED
@@ -27,8 +27,7 @@ module HTTPX
|
|
27
27
|
@version = version
|
28
28
|
@status = Integer(status)
|
29
29
|
@headers = @options.headers_class.new(headers)
|
30
|
-
@body = @options.response_body_class.new(self,
|
31
|
-
window_size: @options.window_size)
|
30
|
+
@body = @options.response_body_class.new(self, @options)
|
32
31
|
end
|
33
32
|
|
34
33
|
def merge_headers(h)
|
@@ -83,11 +82,12 @@ module HTTPX
|
|
83
82
|
end
|
84
83
|
|
85
84
|
class Body
|
86
|
-
def initialize(response,
|
85
|
+
def initialize(response, options)
|
87
86
|
@response = response
|
88
87
|
@headers = response.headers
|
89
|
-
@
|
90
|
-
@
|
88
|
+
@options = options
|
89
|
+
@threshold_size = options.body_threshold_size
|
90
|
+
@window_size = options.window_size
|
91
91
|
@encoding = response.content_type.charset || Encoding::BINARY
|
92
92
|
@length = 0
|
93
93
|
@buffer = nil
|
@@ -268,8 +268,15 @@ module HTTPX
|
|
268
268
|
@error.message
|
269
269
|
end
|
270
270
|
|
271
|
-
|
272
|
-
|
271
|
+
if Exception.method_defined?(:full_message)
|
272
|
+
def to_s
|
273
|
+
@error.full_message
|
274
|
+
end
|
275
|
+
else
|
276
|
+
def to_s
|
277
|
+
"#{@error.message} (#{@error.class})\n" \
|
278
|
+
"#{@error.backtrace.join("\n") if @error.backtrace}"
|
279
|
+
end
|
273
280
|
end
|
274
281
|
|
275
282
|
def raise_for_status
|
data/lib/httpx/selector.rb
CHANGED
@@ -69,6 +69,11 @@ class HTTPX::Selector
|
|
69
69
|
|
70
70
|
if @selectables.empty?
|
71
71
|
@selectables = selectables
|
72
|
+
|
73
|
+
# do not run event loop if there's nothing to wait on.
|
74
|
+
# this might happen if connect failed and connection was unregistered.
|
75
|
+
return if (!r || r.empty?) && (!w || w.empty?)
|
76
|
+
|
72
77
|
break
|
73
78
|
else
|
74
79
|
@selectables = [*selectables, @selectables]
|
data/lib/httpx/session.rb
CHANGED
@@ -199,7 +199,18 @@ module HTTPX
|
|
199
199
|
responses << response
|
200
200
|
requests.shift
|
201
201
|
|
202
|
-
break if requests.empty?
|
202
|
+
break if requests.empty?
|
203
|
+
|
204
|
+
next unless pool.empty?
|
205
|
+
|
206
|
+
# in some cases, the pool of connections might have been drained because there was some
|
207
|
+
# handshake error, and the error responses have already been emitted, but there was no
|
208
|
+
# opportunity to traverse the requests, hence we're returning only a fraction of the errors
|
209
|
+
# we were supposed to. This effectively fetches the existing responses and return them.
|
210
|
+
while (request = requests.shift)
|
211
|
+
responses << fetch_response(request, connections, request_options)
|
212
|
+
end
|
213
|
+
break
|
203
214
|
end
|
204
215
|
responses
|
205
216
|
ensure
|
@@ -269,7 +280,19 @@ module HTTPX
|
|
269
280
|
end
|
270
281
|
# :nocov:
|
271
282
|
end
|
283
|
+
end
|
284
|
+
|
285
|
+
unless ENV.grep(/https?_proxy$/i).empty?
|
286
|
+
proxy_session = plugin(:proxy)
|
287
|
+
::HTTPX.send(:remove_const, :Session)
|
288
|
+
::HTTPX.send(:const_set, :Session, proxy_session.class)
|
289
|
+
end
|
272
290
|
|
273
|
-
|
291
|
+
# :nocov:
|
292
|
+
if Session.default_options.debug_level > 2
|
293
|
+
proxy_session = plugin(:internal_telemetry)
|
294
|
+
::HTTPX.send(:remove_const, :Session)
|
295
|
+
::HTTPX.send(:const_set, :Session, proxy_session.class)
|
274
296
|
end
|
297
|
+
# :nocov:
|
275
298
|
end
|
@@ -44,11 +44,9 @@ module HTTPX::Transcoder
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def method_missing(meth, *args, &block)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
super
|
51
|
-
end
|
47
|
+
return super unless @raw.respond_to?(meth)
|
48
|
+
|
49
|
+
@raw.__send__(meth, *args, &block)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
data/lib/httpx/version.rb
CHANGED
data/sig/chainable.rbs
CHANGED
@@ -18,7 +18,8 @@ module HTTPX
|
|
18
18
|
| (:cookies) -> Plugins::sessionCookies
|
19
19
|
| (:expect) -> Session
|
20
20
|
| (:follow_redirects) -> Plugins::sessionFollowRedirects
|
21
|
-
| (:
|
21
|
+
| (:upgrade) -> Session
|
22
|
+
| (:h2c) -> Session
|
22
23
|
| (:multipart) -> Session
|
23
24
|
| (:persistent) -> Plugins::sessionPersistent
|
24
25
|
| (:proxy) -> Plugins::sessionProxy
|
data/sig/connection/http1.rbs
CHANGED
@@ -4,6 +4,7 @@ module HTTPX
|
|
4
4
|
include Loggable
|
5
5
|
|
6
6
|
attr_reader pending: Array[Request]
|
7
|
+
attr_reader requests: Array[Request]
|
7
8
|
|
8
9
|
@options: Options
|
9
10
|
@max_concurrent_requests: Integer
|
@@ -31,7 +32,7 @@ module HTTPX
|
|
31
32
|
|
32
33
|
def on_headers: (Hash[String, Array[String]] headers) -> void
|
33
34
|
|
34
|
-
def on_trailers: (
|
35
|
+
def on_trailers: (Hash[String, Array[String]] headers) -> void
|
35
36
|
|
36
37
|
def on_data: (string chunk) -> void
|
37
38
|
|
@@ -51,7 +52,7 @@ module HTTPX
|
|
51
52
|
|
52
53
|
def disable_pipelining: () -> void
|
53
54
|
|
54
|
-
def
|
55
|
+
def set_protocol_headers: (Request) -> void
|
55
56
|
|
56
57
|
def headline_uri: (Request) -> String
|
57
58
|
|