httpx 1.8.4 → 1.9.1
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_8_1.md +1 -1
- data/doc/release_notes/1_8_2.md +4 -4
- data/doc/release_notes/1_8_3.md +3 -5
- data/doc/release_notes/1_8_4.md +1 -1
- data/doc/release_notes/1_9_0.md +75 -0
- data/doc/release_notes/1_9_1.md +32 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/webmock.rb +15 -2
- data/lib/httpx/altsvc.rb +29 -15
- data/lib/httpx/chainable.rb +4 -1
- data/lib/httpx/connection/http1.rb +34 -35
- data/lib/httpx/connection/http2.rb +146 -33
- data/lib/httpx/connection.rb +282 -162
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/tcp.rb +42 -21
- 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 -1
- data/lib/httpx/plugins/basic_auth.rb +2 -0
- data/lib/httpx/plugins/cache.rb +1 -1
- data/lib/httpx/plugins/callbacks.rb +8 -6
- data/lib/httpx/plugins/circuit_breaker.rb +1 -0
- data/lib/httpx/plugins/content_digest.rb +3 -5
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +10 -1
- data/lib/httpx/plugins/fiber_concurrency.rb +59 -16
- data/lib/httpx/plugins/follow_redirects.rb +1 -1
- data/lib/httpx/plugins/grpc.rb +21 -0
- data/lib/httpx/plugins/h2c.rb +12 -11
- data/lib/httpx/plugins/hedging.rb +284 -0
- data/lib/httpx/plugins/ntlm_auth.rb +5 -3
- data/lib/httpx/plugins/ntlm_v2_auth.rb +5 -3
- data/lib/httpx/plugins/oauth.rb +2 -2
- data/lib/httpx/plugins/proxy/http.rb +67 -26
- data/lib/httpx/plugins/proxy/socks5.rb +4 -4
- data/lib/httpx/plugins/proxy/ssh.rb +30 -11
- data/lib/httpx/plugins/proxy.rb +68 -54
- data/lib/httpx/plugins/push_promise.rb +4 -2
- data/lib/httpx/plugins/query.rb +1 -0
- 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/stream.rb +12 -10
- data/lib/httpx/plugins/stream_bidi.rb +4 -0
- data/lib/httpx/plugins/tracing.rb +99 -11
- data/lib/httpx/plugins/upgrade/h2.rb +3 -3
- 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 +6 -6
- data/lib/httpx/resolver/https.rb +3 -2
- data/lib/httpx/resolver/native.rb +19 -9
- data/lib/httpx/resolver/resolver.rb +2 -2
- data/lib/httpx/resolver/system.rb +4 -0
- data/lib/httpx/resolver.rb +10 -6
- data/lib/httpx/response.rb +19 -7
- data/lib/httpx/selector.rb +111 -10
- data/lib/httpx/session.rb +36 -10
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -0
- data/sig/altsvc.rbs +3 -3
- data/sig/callbacks.rbs +1 -0
- data/sig/chainable.rbs +3 -1
- data/sig/connection/http1.rbs +4 -2
- data/sig/connection/http2.rbs +23 -17
- data/sig/connection.rbs +29 -7
- data/sig/headers.rbs +1 -1
- data/sig/httpx.rbs +2 -0
- data/sig/io/tcp.rbs +5 -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/expect.rbs +6 -0
- data/sig/plugins/fiber_concurrency.rbs +10 -1
- data/sig/plugins/hedging.rbs +44 -0
- data/sig/plugins/proxy/http.rbs +9 -2
- data/sig/plugins/proxy.rbs +3 -2
- data/sig/plugins/retries.rbs +2 -2
- data/sig/plugins/stream_bidi.rbs +0 -2
- data/sig/plugins/tracing.rbs +22 -5
- data/sig/request/body.rbs +1 -1
- data/sig/resolver/entry.rbs +2 -2
- data/sig/resolver/https.rbs +2 -0
- data/sig/resolver/native.rbs +3 -0
- data/sig/resolver/resolver.rbs +1 -1
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +1 -1
- data/sig/response.rbs +1 -2
- data/sig/selector.rbs +17 -3
- data/sig/session.rbs +8 -6
- metadata +7 -1
data/lib/httpx/headers.rb
CHANGED
|
@@ -159,8 +159,8 @@ module HTTPX
|
|
|
159
159
|
# This method is more internal, and for this reason doesn't try
|
|
160
160
|
# to "correct" the user input, i.e. it doesn't downcase the key.
|
|
161
161
|
#
|
|
162
|
-
def get(
|
|
163
|
-
@headers[
|
|
162
|
+
def get(downcased_key)
|
|
163
|
+
@headers[downcased_key] || EMPTY
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
private
|
data/lib/httpx/io/tcp.rb
CHANGED
|
@@ -17,7 +17,7 @@ module HTTPX
|
|
|
17
17
|
@keep_open = false
|
|
18
18
|
@addresses = []
|
|
19
19
|
@ip_index = -1
|
|
20
|
-
@ip = nil
|
|
20
|
+
@ip = @attempt_started_at = nil
|
|
21
21
|
@hostname = origin.host
|
|
22
22
|
@options = options
|
|
23
23
|
@fallback_protocol = @options.fallback_protocol
|
|
@@ -80,6 +80,27 @@ module HTTPX
|
|
|
80
80
|
@addresses.any?
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# time left until the attempt to connect to the current address should be abandoned.
|
|
84
|
+
def connect_attempt_time_left
|
|
85
|
+
return unless @state == :idle && @ip_index.positive? && (attempt_started_at = @attempt_started_at)
|
|
86
|
+
|
|
87
|
+
timeouts = @options.timeout
|
|
88
|
+
timeout = timeouts.connect_timeout || timeouts.request_timeout || timeouts.total_request_timeout
|
|
89
|
+
|
|
90
|
+
return unless timeout && timeout.finite?
|
|
91
|
+
|
|
92
|
+
timeout.fdiv(@addresses.size) - Utils.elapsed_time(attempt_started_at)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# abandons the connection attempt to the current address.
|
|
96
|
+
def next_address!(e)
|
|
97
|
+
@io&.close
|
|
98
|
+
@ip_index -= 1
|
|
99
|
+
raise e if @ip_index.negative?
|
|
100
|
+
|
|
101
|
+
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
|
102
|
+
end
|
|
103
|
+
|
|
83
104
|
def to_io
|
|
84
105
|
@io.to_io
|
|
85
106
|
end
|
|
@@ -109,13 +130,8 @@ module HTTPX
|
|
|
109
130
|
try_connect
|
|
110
131
|
rescue Errno::EHOSTUNREACH,
|
|
111
132
|
Errno::ENETUNREACH => e
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
raise e if @ip_index.negative?
|
|
115
|
-
|
|
116
|
-
log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
|
|
133
|
+
next_address!(e)
|
|
117
134
|
@options.resolver_cache.evict(@hostname, @ip)
|
|
118
|
-
|
|
119
135
|
@io = build_socket
|
|
120
136
|
retry
|
|
121
137
|
rescue Errno::ECONNREFUSED,
|
|
@@ -132,7 +148,7 @@ module HTTPX
|
|
|
132
148
|
rescue Errno::ETIMEDOUT => e
|
|
133
149
|
@ip_index -= 1
|
|
134
150
|
|
|
135
|
-
raise ConnectTimeoutError.new(@options.timeout
|
|
151
|
+
raise ConnectTimeoutError.new(@options.timeout.connect_timeout, e.message) if @ip_index.negative?
|
|
136
152
|
|
|
137
153
|
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
|
138
154
|
|
|
@@ -159,26 +175,29 @@ module HTTPX
|
|
|
159
175
|
private :try_connect
|
|
160
176
|
|
|
161
177
|
def read(size, buffer)
|
|
162
|
-
|
|
163
|
-
|
|
178
|
+
siz = @io.read_nonblock(size, buffer, exception: false)
|
|
179
|
+
|
|
180
|
+
case siz
|
|
181
|
+
when String
|
|
182
|
+
log { "READ: #{buffer.bytesize} bytes..." }
|
|
183
|
+
buffer.bytesize
|
|
184
|
+
when :wait_readable, :wait_writable
|
|
164
185
|
buffer.clear
|
|
165
|
-
|
|
186
|
+
0
|
|
166
187
|
end
|
|
167
|
-
return if ret.nil?
|
|
168
|
-
|
|
169
|
-
log { "READ: #{buffer.bytesize} bytes..." }
|
|
170
|
-
buffer.bytesize
|
|
171
188
|
end
|
|
172
189
|
|
|
173
190
|
def write(buffer)
|
|
174
191
|
siz = @io.write_nonblock(buffer, exception: false)
|
|
175
|
-
return 0 if siz == :wait_writable
|
|
176
|
-
return if siz.nil?
|
|
177
192
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
193
|
+
case siz
|
|
194
|
+
when Integer
|
|
195
|
+
log { "WRITE: #{siz} bytes..." }
|
|
196
|
+
buffer.shift!(siz)
|
|
197
|
+
siz
|
|
198
|
+
when :wait_readable, :wait_writable
|
|
199
|
+
0
|
|
200
|
+
end
|
|
182
201
|
end
|
|
183
202
|
|
|
184
203
|
def close
|
|
@@ -234,6 +253,7 @@ module HTTPX
|
|
|
234
253
|
|
|
235
254
|
def build_socket
|
|
236
255
|
@ip = @addresses[@ip_index]
|
|
256
|
+
@attempt_started_at = Utils.now
|
|
237
257
|
Socket.new(@ip.family, :STREAM, 0)
|
|
238
258
|
end
|
|
239
259
|
|
|
@@ -255,6 +275,7 @@ module HTTPX
|
|
|
255
275
|
|
|
256
276
|
def log_transition_state(nextstate)
|
|
257
277
|
label = host
|
|
278
|
+
label = "#{label}:#{@port}" if label
|
|
258
279
|
label = "#{label}(##{@io.fileno})" if nextstate == :connected
|
|
259
280
|
"#{label} #{@state} -> #{nextstate}"
|
|
260
281
|
end
|
data/lib/httpx/options.rb
CHANGED
|
@@ -12,12 +12,11 @@ module HTTPX
|
|
|
12
12
|
SETTINGS_TIMEOUT = 10
|
|
13
13
|
CLOSE_HANDSHAKE_TIMEOUT = 10
|
|
14
14
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
|
15
|
-
REQUEST_TIMEOUT = OPERATION_TIMEOUT = TOTAL_REQUEST_TIMEOUT = nil
|
|
16
15
|
RESOLVER_TYPES = %i[memory file].freeze
|
|
17
16
|
# default value used for "user-agent" header, when not overridden.
|
|
18
17
|
USER_AGENT = "httpx.rb/#{VERSION}".freeze # rubocop:disable Style/RedundantFreeze
|
|
19
18
|
|
|
20
|
-
@options_names = []
|
|
19
|
+
@options_names = Set[]
|
|
21
20
|
|
|
22
21
|
class << self
|
|
23
22
|
attr_reader :options_names
|
|
@@ -199,14 +198,14 @@ module HTTPX
|
|
|
199
198
|
# here, which is to make option objects shareable across ractors,
|
|
200
199
|
# and in most cases debug should be nil, or one of the objects
|
|
201
200
|
# which will eventually be shareable, like STDOUT or STDERR.
|
|
202
|
-
next if ivar
|
|
201
|
+
next if %i[debug io].include?(ivar)
|
|
203
202
|
|
|
204
|
-
|
|
203
|
+
send(ivar).freeze
|
|
205
204
|
end
|
|
206
205
|
super
|
|
207
206
|
end
|
|
208
207
|
|
|
209
|
-
REQUEST_BODY_IVARS = %i[
|
|
208
|
+
REQUEST_BODY_IVARS = %i[headers].freeze
|
|
210
209
|
|
|
211
210
|
# checks whether +other+ matches the same connection-level options
|
|
212
211
|
def connection_options_match?(other, ignore_ivars = nil)
|
|
@@ -214,20 +213,18 @@ module HTTPX
|
|
|
214
213
|
|
|
215
214
|
# headers and other request options do not play a role, as they are
|
|
216
215
|
# relevant only for the request.
|
|
217
|
-
ivars =
|
|
218
|
-
ivars.reject
|
|
219
|
-
ivars.reject! { |iv| ignore_ivars.include?(iv) } if ignore_ivars
|
|
216
|
+
ivars = self.class.options_names.reject { |iv| REQUEST_BODY_IVARS.include?(iv) }
|
|
217
|
+
ivars = ivars.reject { |iv| ignore_ivars.include?(iv) } if ignore_ivars
|
|
220
218
|
|
|
221
|
-
other_ivars = other.
|
|
222
|
-
other_ivars.reject
|
|
223
|
-
other_ivars.reject! { |iv| ignore_ivars.include?(iv) } if ignore_ivars
|
|
219
|
+
other_ivars = other.class.options_names.reject { |iv| REQUEST_BODY_IVARS.include?(iv) }
|
|
220
|
+
other_ivars = other_ivars.reject { |iv| ignore_ivars.include?(iv) } if ignore_ivars
|
|
224
221
|
|
|
225
222
|
return false if ivars.size != other_ivars.size
|
|
226
223
|
|
|
227
|
-
return false if ivars
|
|
224
|
+
return false if ivars != other_ivars
|
|
228
225
|
|
|
229
226
|
ivars.all? do |ivar|
|
|
230
|
-
|
|
227
|
+
send(ivar) == other.send(ivar)
|
|
231
228
|
end
|
|
232
229
|
end
|
|
233
230
|
|
|
@@ -296,6 +293,7 @@ module HTTPX
|
|
|
296
293
|
opts.instance_variable_set(ivar, v)
|
|
297
294
|
end
|
|
298
295
|
|
|
296
|
+
opts.freeze
|
|
299
297
|
opts
|
|
300
298
|
end
|
|
301
299
|
|
|
@@ -455,20 +453,22 @@ module HTTPX
|
|
|
455
453
|
end
|
|
456
454
|
|
|
457
455
|
def option_timeout(value)
|
|
456
|
+
return value if value.is_a?(Timeouts)
|
|
457
|
+
|
|
458
458
|
timeout_hash = Hash[value]
|
|
459
459
|
|
|
460
|
-
default_timeouts =
|
|
460
|
+
default_timeouts = Timeouts.members
|
|
461
461
|
|
|
462
462
|
# Validate keys and values
|
|
463
463
|
timeout_hash.each do |key, val|
|
|
464
|
-
raise TypeError, "invalid timeout: `:#{key}`" unless default_timeouts.
|
|
464
|
+
raise TypeError, "invalid timeout: `:#{key}`" unless default_timeouts.include?(key)
|
|
465
465
|
|
|
466
466
|
next if val.nil?
|
|
467
467
|
|
|
468
468
|
raise TypeError, "`:#{key}` must be numeric" unless val.is_a?(Numeric)
|
|
469
469
|
end
|
|
470
470
|
|
|
471
|
-
timeout_hash
|
|
471
|
+
Timeouts.new(**timeout_hash)
|
|
472
472
|
end
|
|
473
473
|
|
|
474
474
|
def option_supported_compression_formats(value)
|
|
@@ -483,7 +483,14 @@ module HTTPX
|
|
|
483
483
|
end
|
|
484
484
|
|
|
485
485
|
def option_addresses(value)
|
|
486
|
-
|
|
486
|
+
case value
|
|
487
|
+
when Hash
|
|
488
|
+
value.transform_values do |value|
|
|
489
|
+
Array(value).map { |entry| Resolver::Entry.convert(entry) }
|
|
490
|
+
end
|
|
491
|
+
else
|
|
492
|
+
Array(value).map { |entry| Resolver::Entry.convert(entry) }
|
|
493
|
+
end
|
|
487
494
|
end
|
|
488
495
|
|
|
489
496
|
def option_ip_families(value)
|
|
@@ -539,12 +546,67 @@ module HTTPX
|
|
|
539
546
|
end
|
|
540
547
|
end
|
|
541
548
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
549
|
+
class Timeouts < Struct.new(
|
|
550
|
+
:connect_timeout, :settings_timeout,
|
|
551
|
+
:read_timeout, :write_timeout,
|
|
552
|
+
:request_timeout, :total_request_timeout,
|
|
553
|
+
:operation_timeout, :keep_alive_timeout, :ping_timeout, :close_handshake_timeout,
|
|
554
|
+
keyword_init: true
|
|
555
|
+
)
|
|
556
|
+
def initialize(
|
|
557
|
+
connect_timeout: nil,
|
|
558
|
+
settings_timeout: nil,
|
|
559
|
+
read_timeout: nil,
|
|
560
|
+
write_timeout: nil,
|
|
561
|
+
request_timeout: nil,
|
|
562
|
+
total_request_timeout: nil,
|
|
563
|
+
operation_timeout: nil,
|
|
564
|
+
close_handshake_timeout: nil,
|
|
565
|
+
keep_alive_timeout: nil,
|
|
566
|
+
ping_timeout: nil
|
|
567
|
+
)
|
|
568
|
+
super
|
|
569
|
+
freeze
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def connect_timeout
|
|
573
|
+
self[:connect_timeout] || (CONNECT_TIMEOUT unless deadline_timeouts?)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def settings_timeout
|
|
577
|
+
self[:settings_timeout] || (SETTINGS_TIMEOUT unless deadline_timeouts?)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def read_timeout
|
|
581
|
+
self[:read_timeout] || (READ_TIMEOUT unless deadline_timeouts?)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def write_timeout
|
|
585
|
+
self[:write_timeout] || (WRITE_TIMEOUT unless deadline_timeouts?)
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def close_handshake_timeout
|
|
589
|
+
self[:close_handshake_timeout] || CLOSE_HANDSHAKE_TIMEOUT
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def keep_alive_timeout
|
|
593
|
+
self[:keep_alive_timeout] || KEEP_ALIVE_TIMEOUT
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def ping_timeout
|
|
597
|
+
self[:ping_timeout] || PING_TIMEOUT
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def merge(other)
|
|
601
|
+
other_h = other.to_h
|
|
602
|
+
other_h.compact!
|
|
603
|
+
Timeouts.new(**to_h, **other_h)
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
private
|
|
607
|
+
|
|
608
|
+
def deadline_timeouts?
|
|
609
|
+
request_timeout || total_request_timeout || operation_timeout
|
|
548
610
|
end
|
|
549
611
|
end
|
|
550
612
|
|
|
@@ -572,18 +634,7 @@ module HTTPX
|
|
|
572
634
|
:max_response_headers => 1000,
|
|
573
635
|
:max_response_header_value_size => nil,
|
|
574
636
|
:max_response_body_size => Float::INFINITY,
|
|
575
|
-
:timeout =>
|
|
576
|
-
connect_timeout: CONNECT_TIMEOUT,
|
|
577
|
-
settings_timeout: SETTINGS_TIMEOUT,
|
|
578
|
-
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
|
579
|
-
operation_timeout: OPERATION_TIMEOUT,
|
|
580
|
-
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
|
581
|
-
ping_timeout: PING_TIMEOUT,
|
|
582
|
-
read_timeout: READ_TIMEOUT,
|
|
583
|
-
write_timeout: WRITE_TIMEOUT,
|
|
584
|
-
request_timeout: REQUEST_TIMEOUT,
|
|
585
|
-
total_request_timeout: TOTAL_REQUEST_TIMEOUT,
|
|
586
|
-
}.freeze,
|
|
637
|
+
:timeout => Timeouts.new,
|
|
587
638
|
:headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
|
|
588
639
|
:headers => EMPTY_HASH,
|
|
589
640
|
:window_size => WINDOW_SIZE,
|
data/lib/httpx/parser/http1.rb
CHANGED
|
@@ -68,7 +68,7 @@ module HTTPX
|
|
|
68
68
|
version, code, _ = m.captures
|
|
69
69
|
raise(Error, "unsupported HTTP version (HTTP/#{version})") unless version && VERSIONS.include?(version)
|
|
70
70
|
|
|
71
|
-
@http_version = version
|
|
71
|
+
@http_version = version
|
|
72
72
|
@status_code = code.to_i
|
|
73
73
|
raise(Error, "wrong status code (#{@status_code})") unless (100..599).cover?(@status_code)
|
|
74
74
|
|
|
@@ -78,9 +78,7 @@ module HTTPX
|
|
|
78
78
|
|
|
79
79
|
def parse_headers
|
|
80
80
|
headers = @headers
|
|
81
|
-
buffer = @buffer
|
|
82
|
-
|
|
83
|
-
#: @type var buffer: String
|
|
81
|
+
buffer = @buffer #: String
|
|
84
82
|
|
|
85
83
|
while (idx = buffer.index("\n"))
|
|
86
84
|
# @type var line: String
|
|
@@ -114,8 +112,7 @@ module HTTPX
|
|
|
114
112
|
key = line.byteslice(0..(separator_index - 1))
|
|
115
113
|
|
|
116
114
|
key.rstrip! # was lstripped previously!
|
|
117
|
-
|
|
118
|
-
value = line.byteslice((separator_index + 1)..-1)
|
|
115
|
+
value = line.byteslice((separator_index + 1)..-1) #: String
|
|
119
116
|
value.strip!
|
|
120
117
|
raise Error, "wrong header format" if value.nil?
|
|
121
118
|
|
|
@@ -14,7 +14,6 @@ module HTTPX
|
|
|
14
14
|
def initialize(user, password, hashed: false, **)
|
|
15
15
|
@user = user
|
|
16
16
|
@password = password
|
|
17
|
-
@nonce = 0
|
|
18
17
|
@hashed = hashed
|
|
19
18
|
end
|
|
20
19
|
|
|
@@ -137,7 +136,7 @@ module HTTPX
|
|
|
137
136
|
end
|
|
138
137
|
|
|
139
138
|
def next_nonce
|
|
140
|
-
|
|
139
|
+
SecureRandom.random_number(2**32)
|
|
141
140
|
end
|
|
142
141
|
|
|
143
142
|
def raise_format_error
|
data/lib/httpx/plugins/auth.rb
CHANGED
|
@@ -69,14 +69,18 @@ module HTTPX
|
|
|
69
69
|
@skip_auth_header_value = false
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# returns a new session where requests will be authorized using +token+ (or what the +blk+ yields),
|
|
73
|
+
# using +auth_header_type+ as header value prefix.
|
|
72
74
|
def authorization(token = nil, auth_header_type: nil, &blk)
|
|
73
75
|
with(auth_header_type: auth_header_type, auth_header_value: token || blk)
|
|
74
76
|
end
|
|
75
77
|
|
|
78
|
+
# returns a new session which call #authorize while setting <tt>"Bearer"</tt> as the +auth_header_type+.
|
|
76
79
|
def bearer_auth(token = nil, &blk)
|
|
77
80
|
authorization(token, auth_header_type: "Bearer", &blk)
|
|
78
81
|
end
|
|
79
82
|
|
|
83
|
+
# yields a block where authorization (if available) will be bypassed.
|
|
80
84
|
def skip_auth_header
|
|
81
85
|
@skip_auth_header_value = true
|
|
82
86
|
yield
|
|
@@ -84,6 +88,7 @@ module HTTPX
|
|
|
84
88
|
@skip_auth_header_value = false
|
|
85
89
|
end
|
|
86
90
|
|
|
91
|
+
# resets authorization information.
|
|
87
92
|
def reset_auth_header_value!
|
|
88
93
|
@auth_header_value_mtx.synchronize do
|
|
89
94
|
@auth_header_value = @auth_header_expires_at = nil
|
|
@@ -186,11 +191,15 @@ module HTTPX
|
|
|
186
191
|
auth_error?(response, options) || super
|
|
187
192
|
end
|
|
188
193
|
|
|
189
|
-
def prepare_to_retry(request, response)
|
|
194
|
+
def prepare_to_retry(request, response, retry_after)
|
|
190
195
|
super
|
|
191
196
|
|
|
192
197
|
return unless auth_error?(response, request.options) ||
|
|
193
|
-
(@options.generate_auth_value_on_retry && @options.generate_auth_value_on_retry.call(response))
|
|
198
|
+
(@options.generate_auth_value_on_retry && @options.generate_auth_value_on_retry.call(response)) ||
|
|
199
|
+
(
|
|
200
|
+
(expires_at = @auth_header_expires_at) &&
|
|
201
|
+
expires_at < (Time.now.utc.to_i + (retry_after || 0))
|
|
202
|
+
)
|
|
194
203
|
|
|
195
204
|
# regenerate token before retry, but only if it's the first request from batch failing.
|
|
196
205
|
# otherwise, it means that the first request already passed here, so this request should
|
|
@@ -85,9 +85,13 @@ module HTTPX
|
|
|
85
85
|
|
|
86
86
|
module InstanceMethods
|
|
87
87
|
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
88
|
+
# returns a session which will use the AWS credentials provided by your AWS system configuration.
|
|
89
|
+
#
|
|
90
|
+
# session = HTTPX.plugin(:aws_sdk_authentication)
|
|
91
|
+
# auth_session = session.aws_authentication
|
|
92
|
+
# # OR
|
|
93
|
+
# auth_session = session.aws_authentication(credentials: Aws::Credentials.new('akid', 'secret'))
|
|
94
|
+
# auth_session.get("http://...")
|
|
91
95
|
#
|
|
92
96
|
def aws_sdk_authentication(
|
|
93
97
|
credentials: AwsSdkAuthentication.credentials(@options.aws_profile),
|
|
@@ -156,7 +156,6 @@ module HTTPX
|
|
|
156
156
|
|
|
157
157
|
class << self
|
|
158
158
|
def load_dependencies(*)
|
|
159
|
-
require "set"
|
|
160
159
|
require "digest/sha2"
|
|
161
160
|
require "cgi/escape"
|
|
162
161
|
end
|
|
@@ -178,6 +177,7 @@ module HTTPX
|
|
|
178
177
|
end
|
|
179
178
|
|
|
180
179
|
module InstanceMethods
|
|
180
|
+
# returns a session which will authenticate requests using the AWS Sigv4 protocol.
|
|
181
181
|
def aws_sigv4_authentication(**options)
|
|
182
182
|
with(sigv4_signer: Signer.new(**options))
|
|
183
183
|
end
|
|
@@ -19,6 +19,8 @@ module HTTPX
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
module InstanceMethods
|
|
22
|
+
# returns a new session which will authorize requests using HTTP Basic Auth, with
|
|
23
|
+
# +user+ and +password+ as credentials.
|
|
22
24
|
def basic_auth(user, password)
|
|
23
25
|
authorization(Authentication::Basic.new(user, password).authenticate)
|
|
24
26
|
end
|
data/lib/httpx/plugins/cache.rb
CHANGED
|
@@ -106,7 +106,7 @@ module HTTPX
|
|
|
106
106
|
def fetch_response(request, *)
|
|
107
107
|
response = super
|
|
108
108
|
|
|
109
|
-
return unless response
|
|
109
|
+
return unless response && response.is_a?(Response)
|
|
110
110
|
|
|
111
111
|
if cacheable_request?(request) && cacheable_response?(request, response) && !response.cached?
|
|
112
112
|
log { "caching response for #{request.uri}..." }
|
|
@@ -25,6 +25,7 @@ module HTTPX
|
|
|
25
25
|
|
|
26
26
|
CALLBACKS.each do |meth|
|
|
27
27
|
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
|
28
|
+
# sets a <tt>:#{meth}</tt> callback on the session
|
|
28
29
|
def on_#{meth}(&blk) # def on_connection_opened(&blk)
|
|
29
30
|
on(:#{meth}, &blk) # on(:connection_opened, &blk)
|
|
30
31
|
self # self
|
|
@@ -33,6 +34,7 @@ module HTTPX
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def plugin(*args, &blk)
|
|
37
|
+
# ruby 2.7 requires explicit named splat
|
|
36
38
|
super(*args).tap do |sess|
|
|
37
39
|
CALLBACKS.each do |cb|
|
|
38
40
|
next unless callbacks_for?(cb)
|
|
@@ -44,6 +46,12 @@ module HTTPX
|
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
|
|
49
|
+
def close(*)
|
|
50
|
+
super
|
|
51
|
+
rescue CallbackError => e
|
|
52
|
+
raise e.cause
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
private
|
|
48
56
|
|
|
49
57
|
def branch(options, &blk)
|
|
@@ -114,12 +122,6 @@ module HTTPX
|
|
|
114
122
|
rescue CallbackError => e
|
|
115
123
|
raise e.cause
|
|
116
124
|
end
|
|
117
|
-
|
|
118
|
-
def close(*)
|
|
119
|
-
super
|
|
120
|
-
rescue CallbackError => e
|
|
121
|
-
raise e.cause
|
|
122
|
-
end
|
|
123
125
|
end
|
|
124
126
|
|
|
125
127
|
module RequestMethods
|
|
@@ -34,6 +34,7 @@ module HTTPX
|
|
|
34
34
|
|
|
35
35
|
%i[circuit_open].each do |meth|
|
|
36
36
|
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
|
37
|
+
# sets a <tt>:#{meth}</tt> callback on the session.
|
|
37
38
|
def on_#{meth}(&blk) # def on_circuit_open(&blk)
|
|
38
39
|
on(:#{meth}, &blk) # on(:circuit_open, &blk)
|
|
39
40
|
self # self
|
|
@@ -81,15 +81,13 @@ module HTTPX
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def write(chunk)
|
|
84
|
-
@content_digest_buffer
|
|
84
|
+
@content_digest_buffer&.write(chunk)
|
|
85
85
|
super
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def close
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@content_digest_buffer = nil
|
|
92
|
-
end
|
|
89
|
+
@content_digest_buffer&.close
|
|
90
|
+
@content_digest_buffer = nil
|
|
93
91
|
super
|
|
94
92
|
end
|
|
95
93
|
end
|
|
@@ -33,6 +33,8 @@ module HTTPX
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
module InstanceMethods
|
|
36
|
+
# returns a session which will authenticate requests using HTTP Digest Auth, with
|
|
37
|
+
# +user+ and +password+ as credentials.
|
|
36
38
|
def digest_auth(user, password, hashed: false)
|
|
37
39
|
with(digest: Authentication::Digest.new(user, password, hashed: hashed))
|
|
38
40
|
end
|
data/lib/httpx/plugins/expect.rb
CHANGED
|
@@ -104,9 +104,15 @@ module HTTPX
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
module ConnectionMethods
|
|
107
|
-
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def set_request_timeouts(request)
|
|
108
110
|
super
|
|
109
111
|
|
|
112
|
+
set_request_expect_timeout(request)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def set_request_expect_timeout(request)
|
|
110
116
|
return unless request.headers["expect"] == "100-continue"
|
|
111
117
|
|
|
112
118
|
expect_timeout = request.options.expect_timeout
|
|
@@ -118,6 +124,7 @@ module HTTPX
|
|
|
118
124
|
if request.state == :expect && !request.expects?
|
|
119
125
|
Expect.no_expect_store.add(request.origin)
|
|
120
126
|
request.headers.delete("expect")
|
|
127
|
+
request.transition(:body)
|
|
121
128
|
consume
|
|
122
129
|
end
|
|
123
130
|
end
|
|
@@ -125,6 +132,8 @@ module HTTPX
|
|
|
125
132
|
end
|
|
126
133
|
|
|
127
134
|
module InstanceMethods
|
|
135
|
+
private
|
|
136
|
+
|
|
128
137
|
def fetch_response(request, selector, options)
|
|
129
138
|
response = super
|
|
130
139
|
|