httpx 1.1.5 → 1.2.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 +5 -5
- data/doc/release_notes/1_2_0.md +49 -0
- data/lib/httpx/adapters/webmock.rb +25 -3
- data/lib/httpx/altsvc.rb +57 -2
- data/lib/httpx/chainable.rb +40 -29
- data/lib/httpx/connection/http1.rb +27 -22
- data/lib/httpx/connection/http2.rb +7 -3
- data/lib/httpx/connection.rb +45 -60
- data/lib/httpx/extensions.rb +0 -15
- data/lib/httpx/options.rb +84 -27
- data/lib/httpx/plugins/aws_sigv4.rb +2 -2
- data/lib/httpx/plugins/basic_auth.rb +1 -1
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +19 -9
- data/lib/httpx/plugins/digest_auth.rb +1 -1
- data/lib/httpx/plugins/follow_redirects.rb +11 -0
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/h2c.rb +20 -8
- data/lib/httpx/plugins/proxy/socks4.rb +2 -2
- data/lib/httpx/plugins/proxy/socks5.rb +2 -2
- data/lib/httpx/plugins/proxy.rb +14 -32
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +4 -0
- data/lib/httpx/plugins/ssrf_filter.rb +142 -0
- data/lib/httpx/plugins/stream.rb +1 -1
- data/lib/httpx/plugins/upgrade/h2.rb +1 -1
- data/lib/httpx/plugins/upgrade.rb +1 -1
- data/lib/httpx/plugins/webdav.rb +1 -1
- data/lib/httpx/pool.rb +32 -28
- data/lib/httpx/request/body.rb +3 -3
- data/lib/httpx/request.rb +3 -5
- data/lib/httpx/resolver/https.rb +3 -2
- data/lib/httpx/resolver/native.rb +1 -0
- data/lib/httpx/resolver/resolver.rb +17 -6
- data/lib/httpx/session.rb +13 -82
- data/lib/httpx/timers.rb +3 -10
- data/lib/httpx/transcoder.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +33 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -1
- data/sig/connection.rbs +16 -16
- data/sig/options.rbs +10 -2
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/follow_redirects.rbs +2 -0
- data/sig/plugins/proxy/socks4.rbs +2 -1
- data/sig/plugins/proxy/socks5.rbs +2 -1
- data/sig/plugins/proxy.rbs +11 -1
- data/sig/pool.rbs +1 -3
- data/sig/resolver/resolver.rbs +3 -1
- data/sig/session.rbs +4 -4
- metadata +10 -4
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
class ServerSideRequestForgeryError < Error; end
|
5
|
+
|
6
|
+
module Plugins
|
7
|
+
#
|
8
|
+
# This plugin adds support for preventing Server-Side Request Forgery attacks.
|
9
|
+
#
|
10
|
+
# https://gitlab.com/os85/httpx/wikis/Server-Side-Request-Forgery-Filter
|
11
|
+
#
|
12
|
+
module SsrfFilter
|
13
|
+
module IPAddrExtensions
|
14
|
+
refine IPAddr do
|
15
|
+
def prefixlen
|
16
|
+
mask_addr = @mask_addr
|
17
|
+
raise "Invalid mask" if mask_addr.zero?
|
18
|
+
|
19
|
+
mask_addr >>= 1 while (mask_addr & 0x1).zero?
|
20
|
+
|
21
|
+
length = 0
|
22
|
+
while mask_addr & 0x1 == 0x1
|
23
|
+
length += 1
|
24
|
+
mask_addr >>= 1
|
25
|
+
end
|
26
|
+
|
27
|
+
length
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
using IPAddrExtensions
|
33
|
+
|
34
|
+
# https://en.wikipedia.org/wiki/Reserved_IP_addresses
|
35
|
+
IPV4_BLACKLIST = [
|
36
|
+
IPAddr.new("0.0.0.0/8"), # Current network (only valid as source address)
|
37
|
+
IPAddr.new("10.0.0.0/8"), # Private network
|
38
|
+
IPAddr.new("100.64.0.0/10"), # Shared Address Space
|
39
|
+
IPAddr.new("127.0.0.0/8"), # Loopback
|
40
|
+
IPAddr.new("169.254.0.0/16"), # Link-local
|
41
|
+
IPAddr.new("172.16.0.0/12"), # Private network
|
42
|
+
IPAddr.new("192.0.0.0/24"), # IETF Protocol Assignments
|
43
|
+
IPAddr.new("192.0.2.0/24"), # TEST-NET-1, documentation and examples
|
44
|
+
IPAddr.new("192.88.99.0/24"), # IPv6 to IPv4 relay (includes 2002::/16)
|
45
|
+
IPAddr.new("192.168.0.0/16"), # Private network
|
46
|
+
IPAddr.new("198.18.0.0/15"), # Network benchmark tests
|
47
|
+
IPAddr.new("198.51.100.0/24"), # TEST-NET-2, documentation and examples
|
48
|
+
IPAddr.new("203.0.113.0/24"), # TEST-NET-3, documentation and examples
|
49
|
+
IPAddr.new("224.0.0.0/4"), # IP multicast (former Class D network)
|
50
|
+
IPAddr.new("240.0.0.0/4"), # Reserved (former Class E network)
|
51
|
+
IPAddr.new("255.255.255.255"), # Broadcast
|
52
|
+
].freeze
|
53
|
+
|
54
|
+
IPV6_BLACKLIST = ([
|
55
|
+
IPAddr.new("::1/128"), # Loopback
|
56
|
+
IPAddr.new("64:ff9b::/96"), # IPv4/IPv6 translation (RFC 6052)
|
57
|
+
IPAddr.new("100::/64"), # Discard prefix (RFC 6666)
|
58
|
+
IPAddr.new("2001::/32"), # Teredo tunneling
|
59
|
+
IPAddr.new("2001:10::/28"), # Deprecated (previously ORCHID)
|
60
|
+
IPAddr.new("2001:20::/28"), # ORCHIDv2
|
61
|
+
IPAddr.new("2001:db8::/32"), # Addresses used in documentation and example source code
|
62
|
+
IPAddr.new("2002::/16"), # 6to4
|
63
|
+
IPAddr.new("fc00::/7"), # Unique local address
|
64
|
+
IPAddr.new("fe80::/10"), # Link-local address
|
65
|
+
IPAddr.new("ff00::/8"), # Multicast
|
66
|
+
] + IPV4_BLACKLIST.flat_map do |ipaddr|
|
67
|
+
prefixlen = ipaddr.prefixlen
|
68
|
+
|
69
|
+
ipv4_compatible = ipaddr.ipv4_compat.mask(96 + prefixlen)
|
70
|
+
ipv4_mapped = ipaddr.ipv4_mapped.mask(80 + prefixlen)
|
71
|
+
|
72
|
+
[ipv4_compatible, ipv4_mapped]
|
73
|
+
end).freeze
|
74
|
+
|
75
|
+
class << self
|
76
|
+
def extra_options(options)
|
77
|
+
options.merge(allowed_schemes: %w[https http])
|
78
|
+
end
|
79
|
+
|
80
|
+
def unsafe_ip_address?(ipaddr)
|
81
|
+
range = ipaddr.to_range
|
82
|
+
return true if range.first != range.last
|
83
|
+
|
84
|
+
return IPV6_BLACKLIST.any? { |r| r.include?(ipaddr) } if ipaddr.ipv6?
|
85
|
+
|
86
|
+
IPV4_BLACKLIST.any? { |r| r.include?(ipaddr) } # then it's IPv4
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module OptionsMethods
|
91
|
+
def option_allowed_schemes(value)
|
92
|
+
Array(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module InstanceMethods
|
97
|
+
def send_requests(*requests)
|
98
|
+
responses = requests.map do |request|
|
99
|
+
next if @options.allowed_schemes.include?(request.uri.scheme)
|
100
|
+
|
101
|
+
error = ServerSideRequestForgeryError.new("#{request.uri} URI scheme not allowed")
|
102
|
+
error.set_backtrace(caller)
|
103
|
+
response = ErrorResponse.new(request, error, request.options)
|
104
|
+
request.emit(:response, response)
|
105
|
+
response
|
106
|
+
end
|
107
|
+
allowed_requests = requests.select { |req| responses[requests.index(req)].nil? }
|
108
|
+
allowed_responses = super(*allowed_requests)
|
109
|
+
allowed_responses.each_with_index do |res, idx|
|
110
|
+
req = allowed_requests[idx]
|
111
|
+
responses[requests.index(req)] = res
|
112
|
+
end
|
113
|
+
|
114
|
+
responses
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module ConnectionMethods
|
119
|
+
def initialize(*)
|
120
|
+
begin
|
121
|
+
super
|
122
|
+
rescue ServerSideRequestForgeryError => e
|
123
|
+
# may raise when IPs are passed as options via :addresses
|
124
|
+
throw(:resolve_error, e)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def addresses=(addrs)
|
129
|
+
addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
|
130
|
+
|
131
|
+
addrs.reject!(&SsrfFilter.method(:unsafe_ip_address?))
|
132
|
+
|
133
|
+
raise ServerSideRequestForgeryError, "#{@origin.host} has no public IP addresses" if addrs.empty?
|
134
|
+
|
135
|
+
super
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
register_plugin :ssrf_filter, SsrfFilter
|
141
|
+
end
|
142
|
+
end
|
data/lib/httpx/plugins/stream.rb
CHANGED
@@ -6,7 +6,7 @@ module HTTPX
|
|
6
6
|
# This plugin adds support for upgrading an HTTP/1.1 connection to HTTP/2
|
7
7
|
# via an Upgrade: h2 response declaration
|
8
8
|
#
|
9
|
-
# https://gitlab.com/
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2
|
10
10
|
#
|
11
11
|
module H2
|
12
12
|
class << self
|
@@ -6,7 +6,7 @@ module HTTPX
|
|
6
6
|
# This plugin helps negotiating a new protocol from an HTTP/1.1 connection, via the
|
7
7
|
# Upgrade header.
|
8
8
|
#
|
9
|
-
# https://gitlab.com/
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Upgrade
|
10
10
|
#
|
11
11
|
module Upgrade
|
12
12
|
class << self
|
data/lib/httpx/plugins/webdav.rb
CHANGED
data/lib/httpx/pool.rb
CHANGED
@@ -17,8 +17,6 @@ module HTTPX
|
|
17
17
|
@timers = Timers.new
|
18
18
|
@selector = Selector.new
|
19
19
|
@connections = []
|
20
|
-
@eden_connections = []
|
21
|
-
@connected_connections = 0
|
22
20
|
end
|
23
21
|
|
24
22
|
def empty?
|
@@ -45,16 +43,15 @@ module HTTPX
|
|
45
43
|
connection.emit(:error, e)
|
46
44
|
end
|
47
45
|
rescue Exception # rubocop:disable Lint/RescueException
|
48
|
-
@connections.each(&:
|
46
|
+
@connections.each(&:force_reset)
|
49
47
|
raise
|
50
48
|
end
|
51
49
|
|
52
50
|
def close(connections = @connections)
|
53
51
|
return if connections.empty?
|
54
52
|
|
55
|
-
@eden_connections.clear
|
56
53
|
connections = connections.reject(&:inflight?)
|
57
|
-
connections.each(&:
|
54
|
+
connections.each(&:terminate)
|
58
55
|
next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
|
59
56
|
|
60
57
|
# close resolvers
|
@@ -68,22 +65,36 @@ module HTTPX
|
|
68
65
|
resolver.close unless resolver.closed?
|
69
66
|
end
|
70
67
|
# for https resolver
|
71
|
-
resolver_connections.each(&:
|
68
|
+
resolver_connections.each(&:terminate)
|
72
69
|
next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
|
73
70
|
end
|
74
71
|
|
75
72
|
def init_connection(connection, _options)
|
76
|
-
resolve_connection(connection) unless connection.family
|
77
73
|
connection.timers = @timers
|
78
|
-
connection.on(:open) do
|
79
|
-
@connected_connections += 1
|
80
|
-
end
|
81
74
|
connection.on(:activate) do
|
82
75
|
select_connection(connection)
|
83
76
|
end
|
77
|
+
connection.on(:exhausted) do
|
78
|
+
case connection.state
|
79
|
+
when :closed
|
80
|
+
connection.idling
|
81
|
+
@connections << connection
|
82
|
+
select_connection(connection)
|
83
|
+
when :closing
|
84
|
+
connection.once(:close) do
|
85
|
+
connection.idling
|
86
|
+
@connections << connection
|
87
|
+
select_connection(connection)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
84
91
|
connection.on(:close) do
|
85
92
|
unregister_connection(connection)
|
86
93
|
end
|
94
|
+
connection.on(:terminate) do
|
95
|
+
unregister_connection(connection, true)
|
96
|
+
end
|
97
|
+
resolve_connection(connection) unless connection.family
|
87
98
|
end
|
88
99
|
|
89
100
|
def deactivate(connections)
|
@@ -102,16 +113,15 @@ module HTTPX
|
|
102
113
|
connection.match?(uri, options)
|
103
114
|
end
|
104
115
|
|
105
|
-
unless conn
|
106
|
-
@eden_connections.delete_if do |connection|
|
107
|
-
is_expired = connection.expired?
|
108
|
-
conn = connection if conn.nil? && !is_expired && connection.match?(uri, options)
|
109
|
-
is_expired
|
110
|
-
end
|
116
|
+
return unless conn
|
111
117
|
|
112
|
-
|
118
|
+
case conn.state
|
119
|
+
when :closed
|
120
|
+
conn.idling
|
121
|
+
select_connection(conn)
|
122
|
+
when :closing
|
123
|
+
conn.once(:close) do
|
113
124
|
conn.idling
|
114
|
-
@connections << conn
|
115
125
|
select_connection(conn)
|
116
126
|
end
|
117
127
|
end
|
@@ -151,7 +161,7 @@ module HTTPX
|
|
151
161
|
|
152
162
|
return connection if connection.family == family
|
153
163
|
|
154
|
-
new_connection = connection.class.new(connection.
|
164
|
+
new_connection = connection.class.new(connection.origin, connection.options)
|
155
165
|
new_connection.family = family
|
156
166
|
|
157
167
|
connection.once(:tcp_open) { new_connection.force_reset }
|
@@ -218,18 +228,12 @@ module HTTPX
|
|
218
228
|
end
|
219
229
|
|
220
230
|
def register_connection(connection)
|
221
|
-
if connection.state == :open
|
222
|
-
# if open, an IO was passed upstream, therefore
|
223
|
-
# consider it connected already.
|
224
|
-
@connected_connections += 1
|
225
|
-
end
|
226
231
|
select_connection(connection)
|
227
232
|
end
|
228
233
|
|
229
|
-
def unregister_connection(connection)
|
230
|
-
@connections.delete(connection)
|
231
|
-
|
232
|
-
@connected_connections -= 1 if deselect_connection(connection)
|
234
|
+
def unregister_connection(connection, cleanup = !connection.used?)
|
235
|
+
@connections.delete(connection) if cleanup
|
236
|
+
deselect_connection(connection)
|
233
237
|
end
|
234
238
|
|
235
239
|
def select_connection(connection)
|
data/lib/httpx/request/body.rb
CHANGED
@@ -70,9 +70,9 @@ module HTTPX
|
|
70
70
|
|
71
71
|
# sets the body to yield using chunked trannsfer encoding format.
|
72
72
|
def stream(body)
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
return body unless chunked?
|
74
|
+
|
75
|
+
Transcoder::Chunker.encode(body.enum_for(:each))
|
76
76
|
end
|
77
77
|
|
78
78
|
# returns whether the body yields infinitely.
|
data/lib/httpx/request.rb
CHANGED
@@ -127,11 +127,9 @@ module HTTPX
|
|
127
127
|
return
|
128
128
|
end
|
129
129
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
return
|
134
|
-
end
|
130
|
+
# 103 Early Hints advertises resources in document to browsers.
|
131
|
+
# not very relevant for an HTTP client, discard.
|
132
|
+
return if response.status >= 103
|
135
133
|
end
|
136
134
|
|
137
135
|
@response = response
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -27,7 +27,7 @@ module HTTPX
|
|
27
27
|
use_get: false,
|
28
28
|
}.freeze
|
29
29
|
|
30
|
-
def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close
|
30
|
+
def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate
|
31
31
|
|
32
32
|
def initialize(_, options)
|
33
33
|
super
|
@@ -50,6 +50,7 @@ module HTTPX
|
|
50
50
|
if @uri_addresses.empty?
|
51
51
|
ex = ResolveError.new("Can't resolve DNS server #{@uri.host}")
|
52
52
|
ex.set_backtrace(caller)
|
53
|
+
connection.force_reset
|
53
54
|
throw(:resolve_error, ex)
|
54
55
|
end
|
55
56
|
|
@@ -67,7 +68,7 @@ module HTTPX
|
|
67
68
|
def resolver_connection
|
68
69
|
@resolver_connection ||= @pool.find_connection(@uri, @options) || begin
|
69
70
|
@building_connection = true
|
70
|
-
connection = @options.connection_class.new(
|
71
|
+
connection = @options.connection_class.new(@uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
|
71
72
|
@pool.init_connection(connection, @options)
|
72
73
|
# only explicity emit addresses if connection didn't pre-resolve, i.e. it's not an IP.
|
73
74
|
emit_addresses(connection, @family, @uri_addresses) unless connection.addresses
|
@@ -38,6 +38,8 @@ module HTTPX
|
|
38
38
|
|
39
39
|
def close; end
|
40
40
|
|
41
|
+
alias_method :terminate, :close
|
42
|
+
|
41
43
|
def closed?
|
42
44
|
true
|
43
45
|
end
|
@@ -65,20 +67,29 @@ module HTTPX
|
|
65
67
|
unless connection.state == :closed ||
|
66
68
|
# double emission check
|
67
69
|
(connection.addresses && addresses.intersect?(connection.addresses))
|
68
|
-
emit_resolved_connection(connection, addresses)
|
70
|
+
emit_resolved_connection(connection, addresses, early_resolve)
|
69
71
|
end
|
70
72
|
end
|
71
73
|
else
|
72
|
-
emit_resolved_connection(connection, addresses)
|
74
|
+
emit_resolved_connection(connection, addresses, early_resolve)
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
76
78
|
private
|
77
79
|
|
78
|
-
def emit_resolved_connection(connection, addresses)
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def emit_resolved_connection(connection, addresses, early_resolve)
|
81
|
+
begin
|
82
|
+
connection.addresses = addresses
|
83
|
+
|
84
|
+
emit(:resolve, connection)
|
85
|
+
rescue StandardError => e
|
86
|
+
if early_resolve
|
87
|
+
connection.force_reset
|
88
|
+
throw(:resolve_error, e)
|
89
|
+
else
|
90
|
+
emit(:error, connection, e)
|
91
|
+
end
|
92
|
+
end
|
82
93
|
end
|
83
94
|
|
84
95
|
def early_resolve(connection, hostname: connection.origin.host)
|
data/lib/httpx/session.rb
CHANGED
@@ -8,7 +8,6 @@ module HTTPX
|
|
8
8
|
class Session
|
9
9
|
include Loggable
|
10
10
|
include Chainable
|
11
|
-
include Callbacks
|
12
11
|
|
13
12
|
EMPTY_HASH = {}.freeze
|
14
13
|
|
@@ -85,33 +84,7 @@ module HTTPX
|
|
85
84
|
options = @options.merge(options) unless options.is_a?(Options)
|
86
85
|
request = rklass.new(verb, uri, options)
|
87
86
|
request.persistent = @persistent
|
88
|
-
|
89
|
-
request.on(:promise, &method(:on_promise))
|
90
|
-
|
91
|
-
request.on(:headers) do
|
92
|
-
emit(:request_started, request)
|
93
|
-
end
|
94
|
-
request.on(:body_chunk) do |chunk|
|
95
|
-
emit(:request_body_chunk, request, chunk)
|
96
|
-
end
|
97
|
-
request.on(:done) do
|
98
|
-
emit(:request_completed, request)
|
99
|
-
end
|
100
|
-
|
101
|
-
request.on(:response_started) do |res|
|
102
|
-
if res.is_a?(Response)
|
103
|
-
emit(:response_started, request, res)
|
104
|
-
res.on(:chunk_received) do |chunk|
|
105
|
-
emit(:response_body_chunk, request, res, chunk)
|
106
|
-
end
|
107
|
-
else
|
108
|
-
emit(:request_error, request, res.error)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
request.on(:response) do |res|
|
112
|
-
emit(:response_completed, request, res)
|
113
|
-
end
|
114
|
-
|
87
|
+
set_request_callbacks(request)
|
115
88
|
request
|
116
89
|
end
|
117
90
|
|
@@ -143,7 +116,7 @@ module HTTPX
|
|
143
116
|
def find_connection(request, connections, options)
|
144
117
|
uri = request.uri
|
145
118
|
|
146
|
-
connection = pool.find_connection(uri, options) ||
|
119
|
+
connection = pool.find_connection(uri, options) || init_connection(uri, options)
|
147
120
|
unless connections.nil? || connections.include?(connection)
|
148
121
|
connections << connection
|
149
122
|
set_connection_callbacks(connection, connections, options)
|
@@ -179,15 +152,6 @@ module HTTPX
|
|
179
152
|
other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
|
180
153
|
connections << other_connection if other_connection
|
181
154
|
end
|
182
|
-
connection.only(:exhausted) do
|
183
|
-
other_connection = connection.create_idle
|
184
|
-
other_connection.merge(connection)
|
185
|
-
catch(:coalesced) do
|
186
|
-
pool.init_connection(other_connection, options)
|
187
|
-
end
|
188
|
-
set_connection_callbacks(other_connection, connections, options)
|
189
|
-
connections << other_connection
|
190
|
-
end
|
191
155
|
end
|
192
156
|
|
193
157
|
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
@@ -202,24 +166,19 @@ module HTTPX
|
|
202
166
|
|
203
167
|
alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
|
204
168
|
|
205
|
-
connection = pool.find_connection(alt_origin, alt_options) ||
|
169
|
+
connection = pool.find_connection(alt_origin, alt_options) || init_connection(alt_origin, alt_options)
|
170
|
+
|
206
171
|
# advertised altsvc is the same origin being used, ignore
|
207
172
|
return if connection == existing_connection
|
208
173
|
|
174
|
+
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
175
|
+
|
209
176
|
set_connection_callbacks(connection, connections, alt_options)
|
210
177
|
|
211
178
|
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
212
179
|
|
213
|
-
# get uninitialized requests
|
214
|
-
# incidentally, all requests will be re-routed to the first
|
215
|
-
# advertised alt-svc, which incidentally follows the spec.
|
216
|
-
existing_connection.purge_pending do |request|
|
217
|
-
request.origin == origin &&
|
218
|
-
request.state == :idle &&
|
219
|
-
!request.headers.key?("alt-used")
|
220
|
-
end
|
221
|
-
|
222
180
|
connection.merge(existing_connection)
|
181
|
+
existing_connection.terminate
|
223
182
|
connection
|
224
183
|
rescue UnsupportedSchemeError
|
225
184
|
altsvc["noop"] = true
|
@@ -250,30 +209,13 @@ module HTTPX
|
|
250
209
|
requests
|
251
210
|
end
|
252
211
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
case uri.scheme
|
257
|
-
when "http"
|
258
|
-
"tcp"
|
259
|
-
when "https"
|
260
|
-
"ssl"
|
261
|
-
else
|
262
|
-
raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
|
263
|
-
end
|
264
|
-
end
|
265
|
-
init_connection(type, uri, options)
|
212
|
+
def set_request_callbacks(request)
|
213
|
+
request.on(:response, &method(:on_response).curry(2)[request])
|
214
|
+
request.on(:promise, &method(:on_promise))
|
266
215
|
end
|
267
216
|
|
268
|
-
def init_connection(
|
269
|
-
connection = options.connection_class.new(
|
270
|
-
connection.on(:open) do
|
271
|
-
emit(:connection_opened, connection.origin, connection.io.socket)
|
272
|
-
# only run close callback if it opened
|
273
|
-
end
|
274
|
-
connection.on(:close) do
|
275
|
-
emit(:connection_closed, connection.origin, connection.io.socket) if connection.used?
|
276
|
-
end
|
217
|
+
def init_connection(uri, options)
|
218
|
+
connection = options.connection_class.new(uri, options)
|
277
219
|
catch(:coalesced) do
|
278
220
|
pool.init_connection(connection, options)
|
279
221
|
connection
|
@@ -369,19 +311,8 @@ module HTTPX
|
|
369
311
|
extend(pl::ClassMethods) if defined?(pl::ClassMethods)
|
370
312
|
|
371
313
|
opts = @default_options
|
372
|
-
opts.
|
373
|
-
opts.request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
374
|
-
opts.response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
375
|
-
opts.response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
376
|
-
opts.headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
377
|
-
opts.headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
378
|
-
opts.request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
379
|
-
opts.request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
380
|
-
opts.response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
381
|
-
opts.response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
382
|
-
opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
|
314
|
+
opts.extend_with_plugin_classes(pl)
|
383
315
|
if defined?(pl::OptionsMethods)
|
384
|
-
opts.options_class.__send__(:include, pl::OptionsMethods)
|
385
316
|
|
386
317
|
(pl::OptionsMethods.instance_methods - Object.instance_methods).each do |meth|
|
387
318
|
opts.options_class.method_added(meth)
|
data/lib/httpx/timers.rb
CHANGED
@@ -81,16 +81,9 @@ module HTTPX
|
|
81
81
|
@callbacks << callback
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
@callbacks.delete(callback)
|
88
|
-
end
|
89
|
-
else
|
90
|
-
def delete(callback)
|
91
|
-
@callbacks.delete(callback)
|
92
|
-
@on_empty.call if @callbacks.empty?
|
93
|
-
end
|
84
|
+
def delete(callback)
|
85
|
+
@callbacks.delete(callback)
|
86
|
+
@on_empty.call if @callbacks.empty?
|
94
87
|
end
|
95
88
|
|
96
89
|
def no_callbacks?
|
data/lib/httpx/transcoder.rb
CHANGED
data/lib/httpx/version.rb
CHANGED
data/sig/altsvc.rbs
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module HTTPX
|
2
|
+
module AltSvc
|
3
|
+
module ConnectionMixin
|
4
|
+
|
5
|
+
def send: (Request request) -> void
|
6
|
+
|
7
|
+
def match?: (URI::Generic uri, Options options) -> bool
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def match_altsvcs?: (URI::Generic uri) -> bool
|
12
|
+
|
13
|
+
def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
|
14
|
+
end
|
15
|
+
|
16
|
+
type altsvc_params = Hash[String, untyped]
|
17
|
+
|
18
|
+
def self?.cached_altsvc: (String origin) -> Array[altsvc_params]
|
19
|
+
|
20
|
+
def self?.cached_altsvc_set: (String origin, altsvc_params) -> void
|
21
|
+
|
22
|
+
def self?.lookup: (String origin, Integer | Float ttl) -> Array[altsvc_params]
|
23
|
+
|
24
|
+
def self?.emit: (Request request, response response) { (http_uri alt_origin, String origin, altsvc_params alt_params) -> void } -> void
|
25
|
+
|
26
|
+
def self?.parse: (String altsvc) { (http_uri alt_origin, altsvc_params alt_params) -> void } -> void
|
27
|
+
| (String altsvc) -> Enumerable[[http_uri, altsvc_params]]
|
28
|
+
|
29
|
+
def self?.parse_altsvc_scheme: (String alt_proto) -> String?
|
30
|
+
|
31
|
+
def self.parse_altsvc_origin: (string alt_proto, String alt_origin) -> http_uri?
|
32
|
+
end
|
33
|
+
end
|
data/sig/chainable.rbs
CHANGED
@@ -34,6 +34,7 @@ module HTTPX
|
|
34
34
|
| (:response_cache, ?options) -> Plugins::sessionResponseCache
|
35
35
|
| (:circuit_breaker, ?options) -> Plugins::sessionCircuitBreaker
|
36
36
|
| (:oauth, ?options) -> Plugins::sessionOAuth
|
37
|
+
| (:callbacks, ?options) -> Plugins::sessionCallbacks
|
37
38
|
| (Symbol | Module, ?options) { (Class) -> void } -> Session
|
38
39
|
| (Symbol | Module, ?options) -> Session
|
39
40
|
|
data/sig/connection/http1.rbs
CHANGED
@@ -10,8 +10,9 @@ module HTTPX
|
|
10
10
|
attr_reader pending: Array[Request]
|
11
11
|
attr_reader requests: Array[Request]
|
12
12
|
|
13
|
+
attr_accessor max_concurrent_requests: Integer
|
14
|
+
|
13
15
|
@options: Options
|
14
|
-
@max_concurrent_requests: Integer
|
15
16
|
@max_requests: Integer
|
16
17
|
@parser: Parser::HTTP1
|
17
18
|
@buffer: Buffer
|