httpx 1.8.4 → 1.9.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_9_0.md +75 -0
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/altsvc.rb +29 -15
- data/lib/httpx/chainable.rb +4 -1
- data/lib/httpx/connection/http1.rb +11 -19
- data/lib/httpx/connection/http2.rb +48 -14
- data/lib/httpx/connection.rb +171 -47
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/tcp.rb +18 -14
- data/lib/httpx/options.rb +86 -35
- data/lib/httpx/parser/http1.rb +3 -6
- data/lib/httpx/plugins/auth/digest.rb +1 -2
- data/lib/httpx/plugins/auth.rb +11 -2
- data/lib/httpx/plugins/aws_sdk_authentication.rb +7 -3
- data/lib/httpx/plugins/aws_sigv4.rb +1 -0
- data/lib/httpx/plugins/basic_auth.rb +2 -0
- data/lib/httpx/plugins/callbacks.rb +7 -6
- data/lib/httpx/plugins/circuit_breaker.rb +1 -0
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +2 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +6 -0
- data/lib/httpx/plugins/follow_redirects.rb +1 -1
- data/lib/httpx/plugins/grpc.rb +21 -0
- data/lib/httpx/plugins/hedging.rb +284 -0
- data/lib/httpx/plugins/ntlm_auth.rb +1 -0
- data/lib/httpx/plugins/ntlm_v2_auth.rb +1 -0
- data/lib/httpx/plugins/oauth.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +44 -10
- data/lib/httpx/plugins/proxy/ssh.rb +30 -11
- data/lib/httpx/plugins/proxy.rb +62 -41
- data/lib/httpx/plugins/push_promise.rb +4 -2
- data/lib/httpx/plugins/retries.rb +2 -2
- data/lib/httpx/plugins/server_sent_events.rb +1 -0
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/tracing.rb +58 -11
- data/lib/httpx/plugins/upgrade/h2.rb +1 -1
- data/lib/httpx/plugins/upgrade.rb +2 -0
- data/lib/httpx/plugins/webdav.rb +8 -0
- data/lib/httpx/request/body.rb +2 -1
- data/lib/httpx/request.rb +5 -5
- data/lib/httpx/resolver.rb +5 -1
- data/lib/httpx/response.rb +15 -7
- data/lib/httpx/selector.rb +3 -3
- data/lib/httpx/session.rb +36 -10
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +3 -3
- data/sig/callbacks.rbs +1 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +1 -1
- data/sig/connection/http2.rbs +15 -15
- data/sig/connection.rbs +18 -1
- data/sig/headers.rbs +1 -1
- data/sig/httpx.rbs +2 -0
- data/sig/options.rbs +43 -12
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/auth/digest.rbs +0 -1
- data/sig/plugins/auth.rbs +12 -5
- data/sig/plugins/hedging.rbs +44 -0
- data/sig/plugins/proxy/http.rbs +6 -1
- data/sig/plugins/proxy.rbs +3 -2
- data/sig/plugins/retries.rbs +2 -2
- data/sig/plugins/tracing.rbs +14 -5
- data/sig/request/body.rbs +1 -1
- data/sig/resolver/entry.rbs +2 -2
- data/sig/response.rbs +1 -2
- data/sig/selector.rbs +2 -2
- data/sig/session.rbs +7 -5
- metadata +5 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins
|
|
5
|
+
#
|
|
6
|
+
# This plugin adds support for request hedging, i.e. a latency-mitigation strategy that involves
|
|
7
|
+
# sending requests through multiple connections and returning the fastest response.
|
|
8
|
+
#
|
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Hedging#hedging
|
|
10
|
+
#
|
|
11
|
+
module Hedging
|
|
12
|
+
# :hedge_after:: seconds after which, if the request didn't complete yet, it'll be sent on a different connection
|
|
13
|
+
module OptionsMethods
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def option_hedge_after(value)
|
|
17
|
+
if value.respond_to?(:call)
|
|
18
|
+
value1 = value
|
|
19
|
+
value1 = value1.method(:call) unless value1.respond_to?(:arity)
|
|
20
|
+
|
|
21
|
+
# allow ->(*) arity as well, which is < 0
|
|
22
|
+
raise TypeError, "`:hedge_after` proc has invalid number of parameters" unless value1.arity.negative? || value1.arity.between?(
|
|
23
|
+
1, 2
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
else
|
|
27
|
+
value = Float(value)
|
|
28
|
+
raise TypeError, "`:hedge_after` must be positive" unless value.positive?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module InstanceMethods
|
|
36
|
+
def select_connection(connection, selector)
|
|
37
|
+
connection.try_yield_hedge_connection do |hedge_conn|
|
|
38
|
+
super.tap do
|
|
39
|
+
select_connection(hedge_conn, selector) if hedge_conn
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def do_request(request, connection, selector)
|
|
47
|
+
super.tap do
|
|
48
|
+
# forego hedging if the connection is already open, or if
|
|
49
|
+
# the connection is scheduled for DNS resolution, as pending requests
|
|
50
|
+
# will be hedged afterwards.
|
|
51
|
+
next if connection.open? || !connection.addresses?
|
|
52
|
+
|
|
53
|
+
hedge_request!(request, connection, selector)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# creates the hedge connection before a connection initiates the connection handshake.
|
|
58
|
+
# This excludes connections which are already open.
|
|
59
|
+
def on_resolver_connection(connection, selector)
|
|
60
|
+
super.tap do
|
|
61
|
+
next if connection.open? || connection.hedge_connection
|
|
62
|
+
|
|
63
|
+
connection.pending.each do |request|
|
|
64
|
+
hedge_request!(request, connection, selector)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
on_resolver_connection(connection.hedge_connection, selector) if connection.hedge_connection
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def hedge_connection(connection)
|
|
72
|
+
connection.hedge_connection || begin
|
|
73
|
+
new_connection = connection.dup
|
|
74
|
+
new_connection.log { "cloning hedge connection of connection##{connection.object_id}..." }
|
|
75
|
+
connection.hedge_connection = new_connection
|
|
76
|
+
new_connection
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def hedge_request!(request, connection, selector)
|
|
81
|
+
hedge_after = when_to_hedge(request)
|
|
82
|
+
|
|
83
|
+
if hedge_after&.positive?
|
|
84
|
+
# hedge after interval
|
|
85
|
+
cb = selector.after(hedge_after) do
|
|
86
|
+
next if request.response
|
|
87
|
+
|
|
88
|
+
request.hedge_request = hedge_request = request.dup
|
|
89
|
+
hedge_conn = hedge_connection(connection)
|
|
90
|
+
hedge_conn.send(hedge_request)
|
|
91
|
+
select_connection(hedge_conn, selector)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# forego hedging if request is over
|
|
95
|
+
request.once(:response) do
|
|
96
|
+
cb.cancel
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
request.hedge_request = hedge_request = request.dup
|
|
100
|
+
hedge_connection(connection).send(hedge_request)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def when_to_hedge(request)
|
|
105
|
+
hedge_after = request.options.hedge_after
|
|
106
|
+
|
|
107
|
+
return unless hedge_after
|
|
108
|
+
|
|
109
|
+
hedge_after = hedge_after.call(request) if hedge_after.respond_to?(:call)
|
|
110
|
+
|
|
111
|
+
hedge_after
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# hedge connections should not deactivate, instead they should terminate.
|
|
115
|
+
# this ensures that persistent hedge connections aren't treated as persistent,
|
|
116
|
+
# and close instead of deactivating.
|
|
117
|
+
def deactivate(selector, *)
|
|
118
|
+
hedge_connections = selector.each_connection.select(&:is_hedge_connection)
|
|
119
|
+
|
|
120
|
+
return super unless hedge_connections.any?
|
|
121
|
+
|
|
122
|
+
selector_close(selector, hedge_connections)
|
|
123
|
+
|
|
124
|
+
super
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# do not check hedge connections back in
|
|
128
|
+
def can_checkin?(connection)
|
|
129
|
+
super && !connection.is_hedge_connection
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
module RequestMethods
|
|
134
|
+
# TODO: would be nice to get rid of this
|
|
135
|
+
attr_reader :connection
|
|
136
|
+
|
|
137
|
+
attr_reader :hedge_request
|
|
138
|
+
|
|
139
|
+
def initialize(*)
|
|
140
|
+
super
|
|
141
|
+
@is_hedge_request = false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def hedge_request=(request)
|
|
145
|
+
@hedge_request = request
|
|
146
|
+
|
|
147
|
+
return unless request
|
|
148
|
+
|
|
149
|
+
@is_hedge_request = !request.hedge_request.nil?
|
|
150
|
+
|
|
151
|
+
return if @is_hedge_request
|
|
152
|
+
|
|
153
|
+
request.hedge_request = self
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def response=(response)
|
|
157
|
+
return super unless @hedge_request # rubocop:disable Lint/ReturnInVoidContext
|
|
158
|
+
|
|
159
|
+
prev_response = @response
|
|
160
|
+
|
|
161
|
+
begin
|
|
162
|
+
super
|
|
163
|
+
ensure
|
|
164
|
+
@response = prev_response if prev_response
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def emit_response(response)
|
|
169
|
+
hedge_request = @hedge_request
|
|
170
|
+
|
|
171
|
+
return super unless hedge_request
|
|
172
|
+
|
|
173
|
+
return super unless hedge_request &&
|
|
174
|
+
(
|
|
175
|
+
hedge_request.response.nil? ||
|
|
176
|
+
!hedge_request.response.finished?
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# if this response is for a hedge request, and the main request
|
|
180
|
+
# didn't complete yet, then we replace it.
|
|
181
|
+
|
|
182
|
+
super.tap do
|
|
183
|
+
hedge_request.response = response
|
|
184
|
+
|
|
185
|
+
# should cancel any pending HTTP/2 stream; there's no way to
|
|
186
|
+
# cancel an HTTP/1 request, so that one will run to completion.
|
|
187
|
+
hedge_request.emit(:refuse)
|
|
188
|
+
end
|
|
189
|
+
ensure
|
|
190
|
+
self.hedge_request = nil
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
module ResponseMethods
|
|
195
|
+
def finish!
|
|
196
|
+
connection = @request.connection
|
|
197
|
+
|
|
198
|
+
super
|
|
199
|
+
|
|
200
|
+
return unless connection && (hedge_connection = connection.hedge_connection)
|
|
201
|
+
|
|
202
|
+
# disconnect connection with its hedge, so that objects do not leak after
|
|
203
|
+
# they're not needed.
|
|
204
|
+
connection.hedge_connection = hedge_connection.hedge_connection = nil
|
|
205
|
+
|
|
206
|
+
# remarking itself as the main connection, so that it's not discarded when
|
|
207
|
+
# going back to the pool.
|
|
208
|
+
connection.is_hedge_connection = false
|
|
209
|
+
hedge_connection.is_hedge_connection = true
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
module ConnectionMethods
|
|
214
|
+
attr_reader :hedge_connection
|
|
215
|
+
|
|
216
|
+
attr_accessor :is_hedge_connection
|
|
217
|
+
|
|
218
|
+
def initialize(*)
|
|
219
|
+
super
|
|
220
|
+
@is_hedge_connection = false
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def addresses=(addrs)
|
|
224
|
+
try_yield_hedge_connection do |hedge_conn|
|
|
225
|
+
super.tap do
|
|
226
|
+
hedge_conn.addresses = addrs if hedge_conn
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def hedge_connection=(connection)
|
|
232
|
+
@hedge_connection = connection
|
|
233
|
+
|
|
234
|
+
return unless connection
|
|
235
|
+
|
|
236
|
+
@is_hedge_connection = !connection.hedge_connection.nil?
|
|
237
|
+
|
|
238
|
+
return if @is_hedge_connection
|
|
239
|
+
|
|
240
|
+
connection.hedge_connection = self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def try_yield_hedge_connection
|
|
244
|
+
return yield(nil) unless (hedge_conn = @hedge_connection)
|
|
245
|
+
|
|
246
|
+
@hedge_connection = nil
|
|
247
|
+
|
|
248
|
+
begin
|
|
249
|
+
hedge_conn.try_yield_hedge_connection do
|
|
250
|
+
yield(hedge_conn)
|
|
251
|
+
end
|
|
252
|
+
ensure
|
|
253
|
+
@hedge_connection = hedge_conn
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# connections should not merge with their hedge connections
|
|
258
|
+
def mergeable?(connection)
|
|
259
|
+
super && connection.hedge_connection != self
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def send(request)
|
|
263
|
+
super.tap do
|
|
264
|
+
if @hedge_connection && !request.hedge_request
|
|
265
|
+
request.hedge_request = hedge_request = request.dup
|
|
266
|
+
@hedge_connection.send(hedge_request)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
private
|
|
272
|
+
|
|
273
|
+
def send_request_to_parser(request)
|
|
274
|
+
# if original request was in the queue, but has since been finished
|
|
275
|
+
# in the hedge connection.
|
|
276
|
+
return if request.response&.finished?
|
|
277
|
+
|
|
278
|
+
super
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
register_plugin(:hedging, Hedging)
|
|
283
|
+
end
|
|
284
|
+
end
|
data/lib/httpx/plugins/oauth.rb
CHANGED
|
@@ -10,19 +10,44 @@ module HTTPX
|
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# adds support for the following options:
|
|
14
|
+
#
|
|
15
|
+
# :proxy_headers :: hash of HTTP headers to send while connecting to the proxy.
|
|
16
|
+
# :enable_proxy_tunnel :: when <tt>true</tt>, it forces the establishment of the CONNECT proxy
|
|
17
|
+
# tunnel, in cases where it wouldn't otherwise normally establish it
|
|
18
|
+
# (i.e. if the request is an http:// request).
|
|
19
|
+
module OptionsMethods
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def option_proxy_headers(value)
|
|
23
|
+
value = value.dup if value.frozen?
|
|
24
|
+
|
|
25
|
+
headers_class.new(value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def option_enable_proxy_tunnel(value) # rubocop:disable Naming/PredicateMethod
|
|
29
|
+
!!value
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
13
33
|
module InstanceMethods
|
|
34
|
+
# returns a new session with the proxy authentication scheme set to <tt>"basic"</tt>, for HTTP Basic Auth.
|
|
14
35
|
def with_proxy_basic_auth(opts)
|
|
15
36
|
with(proxy: opts.merge(scheme: "basic"))
|
|
16
37
|
end
|
|
17
38
|
|
|
39
|
+
# returns a new session with the proxy authentication scheme set to <tt>"digest"</tt>, for HTTP Digest Auth.
|
|
18
40
|
def with_proxy_digest_auth(opts)
|
|
19
41
|
with(proxy: opts.merge(scheme: "digest"))
|
|
20
42
|
end
|
|
21
43
|
|
|
44
|
+
# returns a new session with the proxy authentication scheme set to <tt>"ntlm"</tt>, for NTLM Auth.
|
|
22
45
|
def with_proxy_ntlm_auth(opts)
|
|
23
46
|
with(proxy: opts.merge(scheme: "ntlm"))
|
|
24
47
|
end
|
|
25
48
|
|
|
49
|
+
private
|
|
50
|
+
|
|
26
51
|
def fetch_response(request, selector, options)
|
|
27
52
|
response = super
|
|
28
53
|
|
|
@@ -51,7 +76,7 @@ module HTTPX
|
|
|
51
76
|
super
|
|
52
77
|
end
|
|
53
78
|
|
|
54
|
-
def
|
|
79
|
+
def handle_connect_error(*)
|
|
55
80
|
if @state == :connecting
|
|
56
81
|
# proxy connect related requests should not be reenqueed
|
|
57
82
|
@parser.reset
|
|
@@ -62,6 +87,12 @@ module HTTPX
|
|
|
62
87
|
super
|
|
63
88
|
end
|
|
64
89
|
|
|
90
|
+
def on_error(error, *)
|
|
91
|
+
error = error.to_connection_error if error.is_a?(RequestTimeoutError) && connecting?
|
|
92
|
+
|
|
93
|
+
super
|
|
94
|
+
end
|
|
95
|
+
|
|
65
96
|
private
|
|
66
97
|
|
|
67
98
|
def purge_after_closed
|
|
@@ -136,7 +167,7 @@ module HTTPX
|
|
|
136
167
|
transition(:connecting)
|
|
137
168
|
end
|
|
138
169
|
end
|
|
139
|
-
__http_proxy_connect
|
|
170
|
+
__http_proxy_connect
|
|
140
171
|
end
|
|
141
172
|
return if @state == :connected
|
|
142
173
|
when :connected
|
|
@@ -155,16 +186,19 @@ module HTTPX
|
|
|
155
186
|
super
|
|
156
187
|
end
|
|
157
188
|
|
|
158
|
-
def __http_proxy_connect
|
|
189
|
+
def __http_proxy_connect
|
|
159
190
|
req = @pending.first
|
|
160
|
-
|
|
191
|
+
enable_proxy_tunnel = (
|
|
192
|
+
req.options.enable_proxy_tunnel.nil? && req && req.uri.scheme == "https"
|
|
193
|
+
) || req.options.enable_proxy_tunnel
|
|
194
|
+
|
|
195
|
+
if enable_proxy_tunnel
|
|
161
196
|
# if the first request after CONNECT is to an https address, it is assumed that
|
|
162
197
|
# all requests in the queue are not only ALL HTTPS, but they also share the certificate,
|
|
163
198
|
# and therefore, will share the connection.
|
|
164
199
|
#
|
|
165
200
|
connect_request = ConnectRequest.new(req.uri, @options)
|
|
166
|
-
|
|
167
|
-
parser.send(connect_request)
|
|
201
|
+
send(connect_request)
|
|
168
202
|
else
|
|
169
203
|
handle_transition(:connected)
|
|
170
204
|
end
|
|
@@ -175,7 +209,7 @@ module HTTPX
|
|
|
175
209
|
if response.is_a?(Response) && response.status == 200
|
|
176
210
|
req = @pending.first
|
|
177
211
|
request_uri = req.uri
|
|
178
|
-
@io = ProxySSL.new(@io, request_uri, @options)
|
|
212
|
+
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
|
179
213
|
transition(:connected)
|
|
180
214
|
throw(:called)
|
|
181
215
|
elsif response.is_a?(Response) &&
|
|
@@ -185,8 +219,7 @@ module HTTPX
|
|
|
185
219
|
|
|
186
220
|
request.transition(:idle)
|
|
187
221
|
request.headers["proxy-authorization"] = @options.proxy.authenticate(request, response.headers["proxy-authenticate"])
|
|
188
|
-
|
|
189
|
-
@inflight += 1
|
|
222
|
+
send(request)
|
|
190
223
|
else
|
|
191
224
|
pending = @pending + @parser.pending
|
|
192
225
|
while (req = pending.shift)
|
|
@@ -222,7 +255,8 @@ module HTTPX
|
|
|
222
255
|
class ConnectRequest < Request
|
|
223
256
|
def initialize(uri, options)
|
|
224
257
|
super("CONNECT", uri, options)
|
|
225
|
-
@headers.
|
|
258
|
+
@headers = options.headers_class.new
|
|
259
|
+
merge_headers(options.proxy_headers) if options.proxy_headers
|
|
226
260
|
end
|
|
227
261
|
|
|
228
262
|
def path
|
|
@@ -21,31 +21,50 @@ module HTTPX
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
module InstanceMethods
|
|
24
|
+
def initialize(*)
|
|
25
|
+
super
|
|
26
|
+
@_gateway = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
def request(*args, **options)
|
|
25
30
|
raise ArgumentError, "must perform at least one request" if args.empty?
|
|
26
31
|
|
|
27
32
|
requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
|
|
28
33
|
|
|
29
|
-
request = requests.first
|
|
34
|
+
request = requests.first
|
|
35
|
+
|
|
36
|
+
return super unless request
|
|
30
37
|
|
|
31
38
|
request_options = request.options
|
|
32
39
|
|
|
33
40
|
return super unless request_options.proxy
|
|
34
41
|
|
|
35
|
-
ssh_options = request_options.proxy
|
|
36
|
-
ssh_uris = ssh_options.delete(:uri)
|
|
37
|
-
|
|
42
|
+
ssh_options = request_options.proxy.dup
|
|
43
|
+
ssh_uris = Array(ssh_options.delete(:uri))
|
|
44
|
+
|
|
45
|
+
proxy_ns = 0
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
ssh_uri = URI(ssh_uris[proxy_ns])
|
|
49
|
+
|
|
50
|
+
return super unless ssh_uri.scheme == "ssh"
|
|
51
|
+
|
|
52
|
+
ssh_username = ssh_options.delete(:username)
|
|
53
|
+
ssh_options[:port] ||= ssh_uri.port || 22
|
|
54
|
+
if request_options.debug
|
|
55
|
+
ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
|
|
56
|
+
end
|
|
38
57
|
|
|
39
|
-
|
|
58
|
+
request_uri = URI(requests.first.uri)
|
|
59
|
+
@_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
|
|
60
|
+
rescue StandardError => e
|
|
61
|
+
log { "failed connecting to ssh proxy, trying next..." }
|
|
62
|
+
proxy_ns += 1
|
|
63
|
+
raise e if proxy_ns >= ssh_uris.size
|
|
40
64
|
|
|
41
|
-
|
|
42
|
-
ssh_options[:port] ||= ssh_uri.port || 22
|
|
43
|
-
if request_options.debug
|
|
44
|
-
ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
|
|
65
|
+
retry
|
|
45
66
|
end
|
|
46
67
|
|
|
47
|
-
request_uri = URI(requests.first.uri)
|
|
48
|
-
@_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
|
|
49
68
|
begin
|
|
50
69
|
@_gateway.open(request_uri.host, request_uri.port) do |local_port|
|
|
51
70
|
io = build_gateway_socket(local_port, request_uri, request_options)
|