httpx 1.1.4 → 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_1_4.md +1 -1
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/lib/httpx/adapters/webmock.rb +29 -8
- 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/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -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 +9 -4
- 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 +12 -3
- 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/response.rb +1 -1
- 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/plugins/stream.rbs +24 -22
- data/sig/pool.rbs +1 -3
- data/sig/resolver/resolver.rbs +3 -1
- data/sig/session.rbs +4 -4
- metadata +12 -4
data/lib/httpx/connection.rb
CHANGED
@@ -47,11 +47,11 @@ module HTTPX
|
|
47
47
|
|
48
48
|
attr_accessor :family
|
49
49
|
|
50
|
-
def initialize(
|
51
|
-
@type = type
|
50
|
+
def initialize(uri, options)
|
52
51
|
@origins = [uri.origin]
|
53
52
|
@origin = Utils.to_uri(uri.origin)
|
54
53
|
@options = Options.new(options)
|
54
|
+
@type = initialize_type(uri, @options)
|
55
55
|
@window_size = @options.window_size
|
56
56
|
@read_buffer = Buffer.new(@options.buffer_size)
|
57
57
|
@write_buffer = Buffer.new(@options.buffer_size)
|
@@ -92,18 +92,14 @@ module HTTPX
|
|
92
92
|
def match?(uri, options)
|
93
93
|
return false if !used? && (@state == :closing || @state == :closed)
|
94
94
|
|
95
|
-
return false if exhausted?
|
96
|
-
|
97
95
|
(
|
98
|
-
(
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
) && @options == options
|
106
|
-
) || (match_altsvcs?(uri) && match_altsvc_options?(uri, options))
|
96
|
+
@origins.include?(uri.origin) &&
|
97
|
+
# if there is more than one origin to match, it means that this connection
|
98
|
+
# was the result of coalescing. To prevent blind trust in the case where the
|
99
|
+
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
100
|
+
# SSL certificate
|
101
|
+
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
102
|
+
) && @options == options
|
107
103
|
end
|
108
104
|
|
109
105
|
def expired?
|
@@ -115,8 +111,6 @@ module HTTPX
|
|
115
111
|
def mergeable?(connection)
|
116
112
|
return false if @state == :closing || @state == :closed || !@io
|
117
113
|
|
118
|
-
return false if exhausted?
|
119
|
-
|
120
114
|
return false unless connection.addresses
|
121
115
|
|
122
116
|
(
|
@@ -139,7 +133,7 @@ module HTTPX
|
|
139
133
|
end
|
140
134
|
|
141
135
|
def create_idle(options = {})
|
142
|
-
self.class.new(@
|
136
|
+
self.class.new(@origin, @options.merge(options))
|
143
137
|
end
|
144
138
|
|
145
139
|
def merge(connection)
|
@@ -167,24 +161,6 @@ module HTTPX
|
|
167
161
|
end
|
168
162
|
end
|
169
163
|
|
170
|
-
# checks if this is connection is an alternative service of
|
171
|
-
# +uri+
|
172
|
-
def match_altsvcs?(uri)
|
173
|
-
@origins.any? { |origin| uri.altsvc_match?(origin) } ||
|
174
|
-
AltSvc.cached_altsvc(@origin).any? do |altsvc|
|
175
|
-
origin = altsvc["origin"]
|
176
|
-
origin.altsvc_match?(uri.origin)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def match_altsvc_options?(uri, options)
|
181
|
-
return @options == options unless @options.ssl[:hostname] == uri.host
|
182
|
-
|
183
|
-
dup_options = @options.merge(ssl: { hostname: nil })
|
184
|
-
dup_options.ssl.delete(:hostname)
|
185
|
-
dup_options == options
|
186
|
-
end
|
187
|
-
|
188
164
|
def connecting?
|
189
165
|
@state == :idle
|
190
166
|
end
|
@@ -223,7 +199,6 @@ module HTTPX
|
|
223
199
|
when :closing
|
224
200
|
consume
|
225
201
|
transition(:closed)
|
226
|
-
emit(:close)
|
227
202
|
when :open
|
228
203
|
consume
|
229
204
|
end
|
@@ -236,24 +211,33 @@ module HTTPX
|
|
236
211
|
@parser.close if @parser
|
237
212
|
end
|
238
213
|
|
214
|
+
def terminate
|
215
|
+
@connected_at = nil if @state == :closed
|
216
|
+
|
217
|
+
close
|
218
|
+
end
|
219
|
+
|
239
220
|
# bypasses the state machine to force closing of connections still connecting.
|
240
221
|
# **only** used for Happy Eyeballs v2.
|
241
222
|
def force_reset
|
242
223
|
@state = :closing
|
243
224
|
transition(:closed)
|
244
|
-
emit(:close)
|
245
225
|
end
|
246
226
|
|
247
227
|
def reset
|
228
|
+
return if @state == :closing || @state == :closed
|
229
|
+
|
248
230
|
transition(:closing)
|
231
|
+
unless @write_buffer.empty?
|
232
|
+
# handshakes, try sending
|
233
|
+
consume
|
234
|
+
@write_buffer.clear
|
235
|
+
end
|
249
236
|
transition(:closed)
|
250
|
-
emit(:close)
|
251
237
|
end
|
252
238
|
|
253
239
|
def send(request)
|
254
240
|
if @parser && !@write_buffer.full?
|
255
|
-
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
256
|
-
|
257
241
|
if @response_received_at && @keep_alive_timeout &&
|
258
242
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
259
243
|
# when pushing a request into an existing connection, we have to check whether there
|
@@ -319,10 +303,6 @@ module HTTPX
|
|
319
303
|
transition(:open)
|
320
304
|
end
|
321
305
|
|
322
|
-
def exhausted?
|
323
|
-
@parser && parser.exhausted?
|
324
|
-
end
|
325
|
-
|
326
306
|
def consume
|
327
307
|
return unless @io
|
328
308
|
|
@@ -497,34 +477,25 @@ module HTTPX
|
|
497
477
|
request.emit(:promise, parser, stream)
|
498
478
|
end
|
499
479
|
parser.on(:exhausted) do
|
480
|
+
@pending.concat(parser.pending)
|
500
481
|
emit(:exhausted)
|
501
482
|
end
|
502
483
|
parser.on(:origin) do |origin|
|
503
484
|
@origins |= [origin]
|
504
485
|
end
|
505
486
|
parser.on(:close) do |force|
|
506
|
-
if
|
507
|
-
|
508
|
-
|
509
|
-
transition(:closed)
|
510
|
-
emit(:close)
|
511
|
-
end
|
487
|
+
if force
|
488
|
+
reset
|
489
|
+
emit(:terminate)
|
512
490
|
end
|
513
491
|
end
|
514
492
|
parser.on(:close_handshake) do
|
515
493
|
consume
|
516
494
|
end
|
517
495
|
parser.on(:reset) do
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
transition(:closing)
|
522
|
-
transition(:closed)
|
523
|
-
|
524
|
-
@parser.reset if @parser
|
525
|
-
transition(:idle)
|
526
|
-
transition(:open)
|
527
|
-
end
|
496
|
+
@pending.concat(parser.pending) unless parser.empty?
|
497
|
+
reset
|
498
|
+
idling unless @pending.empty?
|
528
499
|
end
|
529
500
|
parser.on(:current_timeout) do
|
530
501
|
@current_timeout = @timeout = parser.timeout
|
@@ -593,13 +564,14 @@ module HTTPX
|
|
593
564
|
when :inactive
|
594
565
|
return unless @state == :open
|
595
566
|
when :closing
|
596
|
-
return unless @state == :open
|
567
|
+
return unless @state == :idle || @state == :open
|
597
568
|
|
598
569
|
when :closed
|
599
570
|
return unless @state == :closing
|
600
571
|
return unless @write_buffer.empty?
|
601
572
|
|
602
573
|
purge_after_closed
|
574
|
+
emit(:close) if @pending.empty?
|
603
575
|
when :already_open
|
604
576
|
nextstate = :open
|
605
577
|
# the first check for given io readiness must still use a timeout.
|
@@ -621,6 +593,19 @@ module HTTPX
|
|
621
593
|
@timeout = nil
|
622
594
|
end
|
623
595
|
|
596
|
+
def initialize_type(uri, options)
|
597
|
+
options.transport || begin
|
598
|
+
case uri.scheme
|
599
|
+
when "http"
|
600
|
+
"tcp"
|
601
|
+
when "https"
|
602
|
+
"ssl"
|
603
|
+
else
|
604
|
+
raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
624
609
|
def build_socket(addrs = nil)
|
625
610
|
case @type
|
626
611
|
when "tcp"
|
data/lib/httpx/extensions.rb
CHANGED
@@ -54,21 +54,6 @@ module HTTPX
|
|
54
54
|
def origin
|
55
55
|
"#{scheme}://#{authority}"
|
56
56
|
end unless URI::HTTP.method_defined?(:origin)
|
57
|
-
|
58
|
-
def altsvc_match?(uri)
|
59
|
-
uri = URI.parse(uri)
|
60
|
-
|
61
|
-
origin == uri.origin || begin
|
62
|
-
case scheme
|
63
|
-
when "h2"
|
64
|
-
(uri.scheme == "https" || uri.scheme == "h2") &&
|
65
|
-
host == uri.host &&
|
66
|
-
(port || default_port) == (uri.port || uri.default_port)
|
67
|
-
else
|
68
|
-
false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
57
|
end
|
73
58
|
end
|
74
59
|
end
|
data/lib/httpx/options.rb
CHANGED
@@ -11,6 +11,7 @@ module HTTPX
|
|
11
11
|
MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
|
12
12
|
KEEP_ALIVE_TIMEOUT = 20
|
13
13
|
SETTINGS_TIMEOUT = 10
|
14
|
+
CLOSE_HANDSHAKE_TIMEOUT = 10
|
14
15
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
15
16
|
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
16
17
|
|
@@ -39,6 +40,7 @@ module HTTPX
|
|
39
40
|
:timeout => {
|
40
41
|
connect_timeout: CONNECT_TIMEOUT,
|
41
42
|
settings_timeout: SETTINGS_TIMEOUT,
|
43
|
+
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
42
44
|
operation_timeout: OPERATION_TIMEOUT,
|
43
45
|
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
44
46
|
read_timeout: READ_TIMEOUT,
|
@@ -226,44 +228,69 @@ module HTTPX
|
|
226
228
|
OUT
|
227
229
|
end
|
228
230
|
|
229
|
-
|
230
|
-
private_constant :REQUEST_IVARS
|
231
|
+
REQUEST_BODY_IVARS = %i[@headers @params @form @xml @json @body].freeze
|
231
232
|
|
232
233
|
def ==(other)
|
233
|
-
|
234
|
+
super || options_equals?(other)
|
235
|
+
end
|
236
|
+
|
237
|
+
def options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS)
|
238
|
+
# headers and other request options do not play a role, as they are
|
239
|
+
# relevant only for the request.
|
240
|
+
ivars = instance_variables - ignore_ivars
|
241
|
+
other_ivars = other.instance_variables - ignore_ivars
|
242
|
+
|
243
|
+
return false if ivars.size != other_ivars.size
|
244
|
+
|
245
|
+
return false if ivars.sort != other_ivars.sort
|
246
|
+
|
234
247
|
ivars.all? do |ivar|
|
235
|
-
|
236
|
-
when :@headers
|
237
|
-
# currently, this is used to pick up an available matching connection.
|
238
|
-
# the headers do not play a role, as they are relevant only for the request.
|
239
|
-
true
|
240
|
-
when *REQUEST_IVARS
|
241
|
-
true
|
242
|
-
else
|
243
|
-
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
244
|
-
end
|
248
|
+
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
245
249
|
end
|
246
250
|
end
|
247
251
|
|
252
|
+
OTHER_LOOKUP = ->(obj, k, ivar_map) {
|
253
|
+
case obj
|
254
|
+
when Hash
|
255
|
+
obj[ivar_map[k]]
|
256
|
+
else
|
257
|
+
obj.instance_variable_get(k)
|
258
|
+
end
|
259
|
+
}
|
248
260
|
def merge(other)
|
249
|
-
|
261
|
+
ivar_map = nil
|
262
|
+
other_ivars = case other
|
263
|
+
when Hash
|
264
|
+
ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
|
265
|
+
ivar_map.keys
|
266
|
+
else
|
267
|
+
other.instance_variables
|
268
|
+
end
|
269
|
+
|
270
|
+
return self if other_ivars.empty?
|
250
271
|
|
251
|
-
|
252
|
-
return self if h2.empty?
|
272
|
+
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == OTHER_LOOKUP[other, ivar, ivar_map] }
|
253
273
|
|
254
|
-
|
274
|
+
opts = dup
|
255
275
|
|
256
|
-
|
276
|
+
other_ivars.each do |ivar|
|
277
|
+
v = OTHER_LOOKUP[other, ivar, ivar_map]
|
257
278
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
else
|
262
|
-
v2
|
279
|
+
unless v
|
280
|
+
opts.instance_variable_set(ivar, v)
|
281
|
+
next
|
263
282
|
end
|
283
|
+
|
284
|
+
v = opts.__send__(:"option_#{ivar[1..-1]}", v)
|
285
|
+
|
286
|
+
orig_v = instance_variable_get(ivar)
|
287
|
+
|
288
|
+
v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)
|
289
|
+
|
290
|
+
opts.instance_variable_set(ivar, v)
|
264
291
|
end
|
265
292
|
|
266
|
-
|
293
|
+
opts
|
267
294
|
end
|
268
295
|
|
269
296
|
def to_hash
|
@@ -272,10 +299,40 @@ module HTTPX
|
|
272
299
|
end
|
273
300
|
end
|
274
301
|
|
275
|
-
def
|
276
|
-
|
277
|
-
|
302
|
+
def extend_with_plugin_classes(pl)
|
303
|
+
if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
|
304
|
+
@request_class = @request_class.dup
|
305
|
+
@request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
|
306
|
+
@request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
307
|
+
end
|
308
|
+
if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
|
309
|
+
@response_class = @response_class.dup
|
310
|
+
@response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
311
|
+
@response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
278
312
|
end
|
313
|
+
if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
|
314
|
+
@headers_class = @headers_class.dup
|
315
|
+
@headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
316
|
+
@headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
317
|
+
end
|
318
|
+
if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
|
319
|
+
@request_body_class = @request_body_class.dup
|
320
|
+
@request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
321
|
+
@request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
322
|
+
end
|
323
|
+
if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
|
324
|
+
@response_body_class = @response_body_class.dup
|
325
|
+
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
326
|
+
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
327
|
+
end
|
328
|
+
if defined?(pl::ConnectionMethods)
|
329
|
+
@connection_class = @connection_class.dup
|
330
|
+
@connection_class.__send__(:include, pl::ConnectionMethods)
|
331
|
+
end
|
332
|
+
return unless defined?(pl::OptionsMethods)
|
333
|
+
|
334
|
+
@options_class = @options_class.dup
|
335
|
+
@options_class.__send__(:include, pl::OptionsMethods)
|
279
336
|
end
|
280
337
|
|
281
338
|
private
|
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin adds AWS Sigv4 authentication.
|
7
7
|
#
|
8
|
-
# https://docs.aws.amazon.com/
|
8
|
+
# https://docs.aws.amazon.com/IAM/latest/UserGuide/signing-elements.html
|
9
9
|
#
|
10
10
|
# https://gitlab.com/os85/httpx/wikis/AWS-SigV4
|
11
11
|
#
|
@@ -185,7 +185,7 @@ module HTTPX
|
|
185
185
|
def canonical_query
|
186
186
|
params = query.split("&")
|
187
187
|
# params = params.map { |p| p.match(/=/) ? p : p + '=' }
|
188
|
-
# From: https://docs.aws.amazon.com/
|
188
|
+
# From: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html#create-canonical-request
|
189
189
|
# Sort the parameter names by character code point in ascending order.
|
190
190
|
# Parameters with duplicate names should be sorted by value.
|
191
191
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds helper methods to implement HTTP Basic Auth (https://
|
6
|
+
# This plugin adds helper methods to implement HTTP Basic Auth (https://datatracker.ietf.org/doc/html/rfc7617)
|
7
7
|
#
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/Auth#basic-auth
|
9
9
|
#
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin adds suppoort for callbacks around the request/response lifecycle.
|
7
|
+
#
|
8
|
+
# https://gitlab.com/os85/httpx/-/wikis/Events
|
9
|
+
#
|
10
|
+
module Callbacks
|
11
|
+
# connection closed user-space errors happen after errors can be surfaced to requests,
|
12
|
+
# so they need to pierce through the scheduler, which is only possible by simulating an
|
13
|
+
# interrupt.
|
14
|
+
class CallbackError < Exception; end # rubocop:disable Lint/InheritException
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
include HTTPX::Callbacks
|
18
|
+
|
19
|
+
%i[
|
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|
|
25
|
+
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
26
|
+
def on_#{meth}(&blk) # def on_connection_opened(&blk)
|
27
|
+
on(:#{meth}, &blk) # on(:connection_opened, &blk)
|
28
|
+
end # end
|
29
|
+
MOD
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def init_connection(uri, options)
|
35
|
+
connection = super
|
36
|
+
connection.on(:open) do
|
37
|
+
emit_or_callback_error(:connection_opened, connection.origin, connection.io.socket)
|
38
|
+
end
|
39
|
+
connection.on(:close) do
|
40
|
+
emit_or_callback_error(:connection_closed, connection.origin) if connection.used?
|
41
|
+
end
|
42
|
+
|
43
|
+
connection
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_request_callbacks(request)
|
47
|
+
super
|
48
|
+
|
49
|
+
request.on(:headers) do
|
50
|
+
emit_or_callback_error(:request_started, request)
|
51
|
+
end
|
52
|
+
request.on(:body_chunk) do |chunk|
|
53
|
+
emit_or_callback_error(:request_body_chunk, request, chunk)
|
54
|
+
end
|
55
|
+
request.on(:done) do
|
56
|
+
emit_or_callback_error(:request_completed, request)
|
57
|
+
end
|
58
|
+
|
59
|
+
request.on(:response_started) do |res|
|
60
|
+
if res.is_a?(Response)
|
61
|
+
emit_or_callback_error(:response_started, request, res)
|
62
|
+
res.on(:chunk_received) do |chunk|
|
63
|
+
emit_or_callback_error(:response_body_chunk, request, res, chunk)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
emit_or_callback_error(:request_error, request, res.error)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
request.on(:response) do |res|
|
70
|
+
emit_or_callback_error(:response_completed, request, res)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def emit_or_callback_error(*args)
|
75
|
+
emit(*args)
|
76
|
+
rescue StandardError => e
|
77
|
+
ex = CallbackError.new(e.message)
|
78
|
+
ex.set_backtrace(e.backtrace)
|
79
|
+
raise ex
|
80
|
+
end
|
81
|
+
|
82
|
+
def receive_requests(*)
|
83
|
+
super
|
84
|
+
rescue CallbackError => e
|
85
|
+
raise e.cause
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
register_plugin :callbacks, Callbacks
|
90
|
+
end
|
91
|
+
end
|
@@ -71,22 +71,32 @@ module HTTPX
|
|
71
71
|
end
|
72
72
|
|
73
73
|
module OptionsMethods
|
74
|
-
def
|
75
|
-
super
|
74
|
+
def option_headers(*)
|
75
|
+
value = super
|
76
|
+
|
77
|
+
merge_cookie_in_jar(value.delete("cookie"), @cookies) if defined?(@cookies) && value.key?("cookie")
|
76
78
|
|
77
|
-
|
79
|
+
value
|
80
|
+
end
|
78
81
|
|
79
|
-
|
82
|
+
def option_cookies(value)
|
83
|
+
jar = value.is_a?(Jar) ? value : Jar.new(value)
|
84
|
+
|
85
|
+
merge_cookie_in_jar(@headers.delete("cookie"), jar) if defined?(@headers) && @headers.key?("cookie")
|
86
|
+
|
87
|
+
jar
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def merge_cookie_in_jar(cookies, jar)
|
93
|
+
cookies.each do |ck|
|
80
94
|
ck.split(/ *; */).each do |cookie|
|
81
95
|
name, value = cookie.split("=", 2)
|
82
|
-
|
96
|
+
jar.add(Cookie.new(name, value))
|
83
97
|
end
|
84
98
|
end
|
85
99
|
end
|
86
|
-
|
87
|
-
def option_cookies(value)
|
88
|
-
value.is_a?(Jar) ? value : Jar.new(value)
|
89
|
-
end
|
90
100
|
end
|
91
101
|
end
|
92
102
|
register_plugin :cookies, Cookies
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds helper methods to implement HTTP Digest Auth (https://
|
6
|
+
# This plugin adds helper methods to implement HTTP Digest Auth (https://datatracker.ietf.org/doc/html/rfc7616)
|
7
7
|
#
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/Auth#digest-auth
|
9
9
|
#
|
@@ -34,6 +34,12 @@ module HTTPX
|
|
34
34
|
def option_allow_auth_to_other_origins(value)
|
35
35
|
value
|
36
36
|
end
|
37
|
+
|
38
|
+
def option_redirect_on(value)
|
39
|
+
raise TypeError, ":redirect_on must be callable" unless value.respond_to?(:call)
|
40
|
+
|
41
|
+
value
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
39
45
|
module InstanceMethods
|
@@ -57,6 +63,11 @@ module HTTPX
|
|
57
63
|
# build redirect request
|
58
64
|
redirect_uri = __get_location_from_response(response)
|
59
65
|
|
66
|
+
if options.redirect_on
|
67
|
+
redirect_allowed = options.redirect_on.call(redirect_uri)
|
68
|
+
return response unless redirect_allowed
|
69
|
+
end
|
70
|
+
|
60
71
|
if response.status == 305 && options.respond_to?(:proxy)
|
61
72
|
# The requested resource MUST be accessed through the proxy given by
|
62
73
|
# the Location field. The Location field gives the URI of the proxy.
|
@@ -11,6 +11,7 @@ module HTTPX
|
|
11
11
|
@response = response
|
12
12
|
@decoder = ->(z) { z }
|
13
13
|
@consumed = false
|
14
|
+
@grpc_response = nil
|
14
15
|
end
|
15
16
|
|
16
17
|
def inspect
|
@@ -34,9 +35,7 @@ module HTTPX
|
|
34
35
|
private
|
35
36
|
|
36
37
|
def grpc_response
|
37
|
-
|
38
|
-
|
39
|
-
@grpc_response = if @response.respond_to?(:each)
|
38
|
+
@grpc_response ||= if @response.respond_to?(:each)
|
40
39
|
Enumerator.new do |y|
|
41
40
|
Message.stream(@response).each do |message|
|
42
41
|
y << @decoder.call(message)
|
data/lib/httpx/plugins/grpc.rb
CHANGED
@@ -261,14 +261,14 @@ module HTTPX
|
|
261
261
|
headers["grpc-timeout"] = "#{deadline}m"
|
262
262
|
end
|
263
263
|
|
264
|
-
headers = headers.merge(metadata) if metadata
|
264
|
+
headers = headers.merge(metadata.transform_keys(&:to_s)) if metadata
|
265
265
|
|
266
266
|
# prepare compressor
|
267
267
|
compression = @options.grpc_compression == true ? "gzip" : @options.grpc_compression
|
268
268
|
|
269
269
|
headers["grpc-encoding"] = compression if compression
|
270
270
|
|
271
|
-
headers.merge!(@options.call_credentials.call) if @options.call_credentials
|
271
|
+
headers.merge!(@options.call_credentials.call.transform_keys(&:to_s)) if @options.call_credentials
|
272
272
|
|
273
273
|
build_request("POST", uri, headers: headers, body: input)
|
274
274
|
end
|