httpx 0.11.1 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/doc/release_notes/0_11_1.md +5 -1
- 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/doc/release_notes/0_13_1.md +5 -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 +50 -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/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 +20 -8
- 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 +24 -2
@@ -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
|
|