httpx 1.7.2 → 1.7.3
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 +3 -1
- data/doc/release_notes/1_7_3.md +29 -0
- data/lib/httpx/adapters/webmock.rb +3 -4
- data/lib/httpx/connection/http1.rb +0 -1
- data/lib/httpx/connection/http2.rb +41 -30
- data/lib/httpx/connection.rb +18 -4
- data/lib/httpx/plugins/auth/digest.rb +2 -1
- data/lib/httpx/plugins/cookies/cookie.rb +34 -11
- data/lib/httpx/plugins/cookies/jar.rb +93 -18
- data/lib/httpx/plugins/cookies.rb +7 -3
- data/lib/httpx/plugins/expect.rb +26 -2
- data/lib/httpx/plugins/fiber_concurrency.rb +2 -4
- data/lib/httpx/plugins/follow_redirects.rb +3 -1
- data/lib/httpx/plugins/rate_limiter.rb +19 -19
- data/lib/httpx/plugins/retries.rb +11 -7
- data/lib/httpx/plugins/ssrf_filter.rb +1 -0
- data/lib/httpx/plugins/stream_bidi.rb +6 -0
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/resolver.rb +5 -0
- data/lib/httpx/selector.rb +4 -4
- data/lib/httpx/session.rb +6 -5
- data/lib/httpx/version.rb +1 -1
- data/sig/chainable.rbs +1 -1
- data/sig/connection/http2.rbs +8 -4
- data/sig/connection.rbs +3 -0
- data/sig/plugins/cookies/cookie.rbs +3 -2
- data/sig/plugins/cookies/jar.rbs +11 -0
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/expect.rbs +17 -2
- data/sig/plugins/proxy/socks4.rbs +4 -0
- data/sig/plugins/rate_limiter.rbs +2 -2
- data/sig/plugins/response_cache.rbs +3 -3
- data/sig/plugins/retries.rbs +17 -13
- data/sig/request.rbs +1 -0
- data/sig/resolver/native.rbs +2 -0
- data/sig/resolver/resolver.rbs +2 -2
- data/sig/selector.rbs +4 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54c87d9d8b2be0d12570204fd3c60d37f82127b624dde3a233085d5bcb43778c
|
|
4
|
+
data.tar.gz: 8bfec9fadfe697d083d37a9317e9ec9d6e23016f174bd62cabf24275f18fee79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c425e8714c36bdca0ab5d068157caa94ca591829466ccdb888e903487aaab50c9a8756eaa53f1cd692a109160deca10ec9ce4405a7690f38149a9e95ec9fb17
|
|
7
|
+
data.tar.gz: 20c6ae3067f1fe187402c743fb1741a131ba09e5039ed86e5c5d2eb8c94d590c84644ca3e6ab5fd3948b635d328bba4149df3a1d94dde3dfbf257bd694730275
|
data/README.md
CHANGED
|
@@ -46,7 +46,9 @@ And that's the simplest one there is. But you can also do:
|
|
|
46
46
|
HTTPX.post("http://example.com", form: { user: "john", password: "pass" })
|
|
47
47
|
|
|
48
48
|
http = HTTPX.with(headers: { "x-my-name" => "joe" })
|
|
49
|
-
|
|
49
|
+
File.open("path/to/file") do |file|
|
|
50
|
+
http.patch("http://example.com/file", body: file) # request body is streamed
|
|
51
|
+
end
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
If you want to do some more things with the response, you can get an `HTTPX::Response`:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 1.7.3
|
|
2
|
+
|
|
3
|
+
## Improvements
|
|
4
|
+
|
|
5
|
+
### cookies plugin: Jar as CookieStore
|
|
6
|
+
|
|
7
|
+
While previously an implementation detail, the cookie jar from a `:cookie` plugin-enabled session can now be manipulated by the end user:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
cookies_sess = HTTPX.plugin(:cookies)
|
|
11
|
+
|
|
12
|
+
jar = cookies.make_jar
|
|
13
|
+
|
|
14
|
+
sess = cookies_ses.with(cookies: jar)
|
|
15
|
+
|
|
16
|
+
# perform requests using sess, get/set/delete cookies in jar
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The jar API now closely follows the [Web Cookie Store API](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore), by providing the same set of functions.
|
|
20
|
+
|
|
21
|
+
Some API backwards compatibility is maintained, however since this was an internal implementation detail, this effort isn't meant to be thorough.
|
|
22
|
+
|
|
23
|
+
## Bugfixes
|
|
24
|
+
|
|
25
|
+
* `http-2`: clear buffered data chunks when receiving a `GOAWAY` stream frame; without this, the client kept sending the corresponding `DATA` frames, despite the peer server making it known that it wouldn't process it. While this is valid HTTP/2, this could increase the connection window until a point where it'd go over the max frame size. this issue was observed during large file uploads where the first request could fail and make the client renegotiate.
|
|
26
|
+
* `webmock` adapter: fixed response body length accounting which was making `response.body.empty?` return true for responses with payload.
|
|
27
|
+
* `:rate_limiter` plugin relies on an internal refactoring to be able to wait for the time suggested by the peer server instead of the potentially relying on custom user logic via own `:retry_after`.
|
|
28
|
+
* `:fiber_concurrency`: fix wrong names for native/system resolver overrides.
|
|
29
|
+
* connection: fix for race condition when closing the connection, where the state only transitions to `closed` after checking the connection back in to the pool, potentially corrupting it if another session meanwhile has picked it up and manipulated it.
|
|
@@ -82,6 +82,7 @@ module WebMock
|
|
|
82
82
|
|
|
83
83
|
def mock!
|
|
84
84
|
@mocked = true
|
|
85
|
+
@body.mock!
|
|
85
86
|
end
|
|
86
87
|
|
|
87
88
|
def mocked?
|
|
@@ -90,10 +91,8 @@ module WebMock
|
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
module ResponseBodyMethods
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
super
|
|
94
|
+
def mock!
|
|
95
|
+
@inflaters = nil
|
|
97
96
|
end
|
|
98
97
|
end
|
|
99
98
|
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require "securerandom"
|
|
4
4
|
require "http/2"
|
|
5
5
|
|
|
6
|
-
HTTP2::Connection.__send__(:public, :send_buffer) if HTTP2::VERSION < "1.1.1"
|
|
7
|
-
|
|
8
6
|
module HTTPX
|
|
9
7
|
class Connection::HTTP2
|
|
10
8
|
include Callbacks
|
|
@@ -215,9 +213,7 @@ module HTTPX
|
|
|
215
213
|
def handle_stream(stream, request)
|
|
216
214
|
request.on(:refuse, &method(:on_stream_refuse).curry(3)[stream, request])
|
|
217
215
|
stream.on(:close, &method(:on_stream_close).curry(3)[stream, request])
|
|
218
|
-
stream.on(:half_close)
|
|
219
|
-
log(level: 2) { "#{stream.id}: waiting for response..." }
|
|
220
|
-
end
|
|
216
|
+
stream.on(:half_close) { on_stream_half_close(stream, request) }
|
|
221
217
|
stream.on(:altsvc, &method(:on_altsvc).curry(2)[request.origin])
|
|
222
218
|
stream.on(:headers, &method(:on_stream_headers).curry(3)[stream, request])
|
|
223
219
|
stream.on(:data, &method(:on_stream_data).curry(3)[stream, request])
|
|
@@ -302,7 +298,7 @@ module HTTPX
|
|
|
302
298
|
end
|
|
303
299
|
|
|
304
300
|
log(color: :yellow) do
|
|
305
|
-
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
|
|
301
|
+
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{k == ":status" ? v : log_redact_headers(v)}" }.join("\n")
|
|
306
302
|
end
|
|
307
303
|
_, status = h.shift
|
|
308
304
|
headers = request.options.headers_class.new(h)
|
|
@@ -331,6 +327,16 @@ module HTTPX
|
|
|
331
327
|
stream.close
|
|
332
328
|
end
|
|
333
329
|
|
|
330
|
+
def on_stream_half_close(stream, _request)
|
|
331
|
+
unless stream.send_buffer.empty?
|
|
332
|
+
stream.send_buffer.clear
|
|
333
|
+
stream.data("", end_stream: true)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# TODO: omit log line if response already here
|
|
337
|
+
log(level: 2) { "#{stream.id}: waiting for response..." }
|
|
338
|
+
end
|
|
339
|
+
|
|
334
340
|
def on_stream_close(stream, request, error)
|
|
335
341
|
return if error == :stream_closed && !@streams.key?(request)
|
|
336
342
|
|
|
@@ -404,34 +410,39 @@ module HTTPX
|
|
|
404
410
|
|
|
405
411
|
def on_frame_sent(frame)
|
|
406
412
|
log(level: 2) { "#{frame[:stream]}: frame was sent!" }
|
|
407
|
-
log(level: 2, color: :blue)
|
|
408
|
-
payload =
|
|
409
|
-
case frame[:type]
|
|
410
|
-
when :data
|
|
411
|
-
frame.merge(payload: frame[:payload].bytesize)
|
|
412
|
-
when :headers, :ping
|
|
413
|
-
frame.merge(payload: log_redact_headers(frame[:payload]))
|
|
414
|
-
else
|
|
415
|
-
frame
|
|
416
|
-
end
|
|
417
|
-
"#{frame[:stream]}: #{payload}"
|
|
418
|
-
end
|
|
413
|
+
log(level: 2, color: :blue) { "#{frame[:stream]}: #{frame_with_extra_info(frame)}" }
|
|
419
414
|
end
|
|
420
415
|
|
|
421
416
|
def on_frame_received(frame)
|
|
422
417
|
log(level: 2) { "#{frame[:stream]}: frame was received!" }
|
|
423
|
-
log(level: 2, color: :magenta)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
418
|
+
log(level: 2, color: :magenta) { "#{frame[:stream]}: #{frame_with_extra_info(frame)}" }
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def frame_with_extra_info(frame)
|
|
422
|
+
case frame[:type]
|
|
423
|
+
when :data
|
|
424
|
+
frame.merge(payload: frame[:payload].bytesize)
|
|
425
|
+
when :headers, :ping
|
|
426
|
+
frame.merge(payload: log_redact_headers(frame[:payload]))
|
|
427
|
+
when :window_update
|
|
428
|
+
connection_or_stream = if (id = frame[:stream]).zero?
|
|
429
|
+
@connection
|
|
430
|
+
else
|
|
431
|
+
@streams.each_value.find { |s| s.id == id }
|
|
432
|
+
end
|
|
433
|
+
if connection_or_stream
|
|
434
|
+
frame.merge(
|
|
435
|
+
local_window: connection_or_stream.local_window,
|
|
436
|
+
remote_window: connection_or_stream.remote_window,
|
|
437
|
+
buffered_amount: connection_or_stream.buffered_amount,
|
|
438
|
+
stream_state: connection_or_stream.state,
|
|
439
|
+
)
|
|
440
|
+
else
|
|
441
|
+
frame
|
|
442
|
+
end
|
|
443
|
+
else
|
|
444
|
+
frame
|
|
445
|
+
end.merge(connection_state: @connection.state)
|
|
435
446
|
end
|
|
436
447
|
|
|
437
448
|
def on_altsvc(origin, frame)
|
data/lib/httpx/connection.rb
CHANGED
|
@@ -227,6 +227,9 @@ module HTTPX
|
|
|
227
227
|
consume
|
|
228
228
|
end
|
|
229
229
|
nil
|
|
230
|
+
rescue IOError => e
|
|
231
|
+
@write_buffer.clear
|
|
232
|
+
on_io_error(e)
|
|
230
233
|
rescue StandardError => e
|
|
231
234
|
@write_buffer.clear
|
|
232
235
|
on_error(e)
|
|
@@ -375,6 +378,11 @@ module HTTPX
|
|
|
375
378
|
current_session.deselect_connection(self, current_selector, @cloned)
|
|
376
379
|
end
|
|
377
380
|
|
|
381
|
+
def on_io_error(e)
|
|
382
|
+
on_error(e)
|
|
383
|
+
force_close(true)
|
|
384
|
+
end
|
|
385
|
+
|
|
378
386
|
def on_error(error, request = nil)
|
|
379
387
|
if error.is_a?(OperationTimeoutError)
|
|
380
388
|
|
|
@@ -493,7 +501,7 @@ module HTTPX
|
|
|
493
501
|
# flush as many bytes as the sockets allow.
|
|
494
502
|
#
|
|
495
503
|
loop do
|
|
496
|
-
# buffer has been
|
|
504
|
+
# buffer has been drained, mark and exit the write loop.
|
|
497
505
|
if @write_buffer.empty?
|
|
498
506
|
# we only mark as drained on the first loop
|
|
499
507
|
write_drained = write_drained.nil? && @inflight.positive?
|
|
@@ -586,6 +594,8 @@ module HTTPX
|
|
|
586
594
|
# back to the pending list before the parser is reset.
|
|
587
595
|
@inflight -= parser_pending_requests.size
|
|
588
596
|
@pending.unshift(*parser_pending_requests)
|
|
597
|
+
|
|
598
|
+
parser.pending.clear
|
|
589
599
|
end
|
|
590
600
|
|
|
591
601
|
def build_parser(protocol = @io.protocol)
|
|
@@ -721,8 +731,6 @@ module HTTPX
|
|
|
721
731
|
|
|
722
732
|
# do not deactivate connection in use
|
|
723
733
|
return if @inflight.positive? || @parser.waiting_for_ping?
|
|
724
|
-
|
|
725
|
-
disconnect
|
|
726
734
|
when :closing
|
|
727
735
|
return unless connecting? || @state == :open
|
|
728
736
|
|
|
@@ -740,8 +748,9 @@ module HTTPX
|
|
|
740
748
|
return unless @write_buffer.empty?
|
|
741
749
|
|
|
742
750
|
purge_after_closed
|
|
743
|
-
disconnect if @pending.empty?
|
|
744
751
|
|
|
752
|
+
# TODO: should this raise an error instead?
|
|
753
|
+
return unless @pending.empty?
|
|
745
754
|
when :already_open
|
|
746
755
|
nextstate = :open
|
|
747
756
|
# the first check for given io readiness must still use a timeout.
|
|
@@ -758,6 +767,11 @@ module HTTPX
|
|
|
758
767
|
end
|
|
759
768
|
log(level: 3) { "#{@state} -> #{nextstate}" }
|
|
760
769
|
@state = nextstate
|
|
770
|
+
# post state change
|
|
771
|
+
case nextstate
|
|
772
|
+
when :closed, :inactive
|
|
773
|
+
disconnect
|
|
774
|
+
end
|
|
761
775
|
end
|
|
762
776
|
|
|
763
777
|
def close_sibling
|
|
@@ -14,12 +14,14 @@ module HTTPX
|
|
|
14
14
|
|
|
15
15
|
attr_reader :domain, :path, :name, :value, :created_at
|
|
16
16
|
|
|
17
|
+
# assigns a new +path+ to this cookie.
|
|
17
18
|
def path=(path)
|
|
18
19
|
path = String(path)
|
|
20
|
+
@for_domain = false
|
|
19
21
|
@path = path.start_with?("/") ? path : "/"
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
#
|
|
24
|
+
# assigns a new +domain+ to this cookie.
|
|
23
25
|
def domain=(domain)
|
|
24
26
|
domain = String(domain)
|
|
25
27
|
|
|
@@ -37,6 +39,13 @@ module HTTPX
|
|
|
37
39
|
@domain = @domain_name.hostname
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
# checks whether +other+ is the same cookie, i.e. name, value, domain and path are
|
|
43
|
+
# the same.
|
|
44
|
+
def ==(other)
|
|
45
|
+
@name == other.name && @value == other.value &&
|
|
46
|
+
@path == other.path && @domain == other.domain
|
|
47
|
+
end
|
|
48
|
+
|
|
40
49
|
# Compares the cookie with another. When there are many cookies with
|
|
41
50
|
# the same name for a URL, the value of the smallest must be used.
|
|
42
51
|
def <=>(other)
|
|
@@ -47,11 +56,29 @@ module HTTPX
|
|
|
47
56
|
(@created_at <=> other.created_at).nonzero? || 0
|
|
48
57
|
end
|
|
49
58
|
|
|
59
|
+
def match?(name_or_options)
|
|
60
|
+
case name_or_options
|
|
61
|
+
when String
|
|
62
|
+
@name == name_or_options
|
|
63
|
+
when Hash, Array
|
|
64
|
+
name_or_options.all? { |k, v| respond_to?(k) && send(k) == v }
|
|
65
|
+
else
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
50
70
|
class << self
|
|
51
71
|
def new(cookie, *args)
|
|
52
|
-
|
|
72
|
+
case cookie
|
|
73
|
+
when self
|
|
74
|
+
cookie
|
|
75
|
+
when Array, Hash
|
|
76
|
+
options = Hash[cookie] #: cookie_attributes
|
|
77
|
+
super(options[:name], options[:value], options)
|
|
78
|
+
else
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
super
|
|
81
|
+
end
|
|
55
82
|
end
|
|
56
83
|
|
|
57
84
|
# Tests if +target_path+ is under +base_path+ as described in RFC
|
|
@@ -84,16 +111,12 @@ module HTTPX
|
|
|
84
111
|
end
|
|
85
112
|
end
|
|
86
113
|
|
|
87
|
-
def initialize(arg,
|
|
114
|
+
def initialize(arg, value, attrs = nil)
|
|
88
115
|
@created_at = Time.now
|
|
89
116
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@name = arg
|
|
94
|
-
@value, attr_hash = attrs
|
|
95
|
-
attr_hash = Hash.try_convert(attr_hash)
|
|
96
|
-
end
|
|
117
|
+
@name = arg
|
|
118
|
+
@value = value
|
|
119
|
+
attr_hash = Hash.try_convert(attrs)
|
|
97
120
|
|
|
98
121
|
attr_hash.each do |key, val|
|
|
99
122
|
key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol)
|
|
@@ -4,7 +4,12 @@ module HTTPX
|
|
|
4
4
|
module Plugins::Cookies
|
|
5
5
|
# The Cookie Jar
|
|
6
6
|
#
|
|
7
|
-
# It
|
|
7
|
+
# It stores and manages cookies for a session, such as i.e. evicting when expired, access methods, or
|
|
8
|
+
# initialization from parsing `Set-Cookie` HTTP header values.
|
|
9
|
+
#
|
|
10
|
+
# It closely follows the [CookieStore API](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore),
|
|
11
|
+
# by implementing the same methods, with a few specific conveniences for this non-browser manipulation use-case.
|
|
12
|
+
#
|
|
8
13
|
class Jar
|
|
9
14
|
using URIExtensions
|
|
10
15
|
|
|
@@ -12,10 +17,14 @@ module HTTPX
|
|
|
12
17
|
|
|
13
18
|
def initialize_dup(orig)
|
|
14
19
|
super
|
|
20
|
+
@mtx = orig.instance_variable_get(:@mtx).dup
|
|
15
21
|
@cookies = orig.instance_variable_get(:@cookies).dup
|
|
16
22
|
end
|
|
17
23
|
|
|
24
|
+
# initializes the cookie store, either empty, or with whatever is passed as +cookies+, which
|
|
25
|
+
# can be an array of HTTPX::Plugins::Cookies::Cookie objects or hashes-or-tuples of cookie attributes.
|
|
18
26
|
def initialize(cookies = nil)
|
|
27
|
+
@mtx = Thread::Mutex.new
|
|
19
28
|
@cookies = []
|
|
20
29
|
|
|
21
30
|
cookies.each do |elem|
|
|
@@ -32,48 +41,106 @@ module HTTPX
|
|
|
32
41
|
end if cookies
|
|
33
42
|
end
|
|
34
43
|
|
|
44
|
+
# parses the `Set-Cookie` header value as +set_cookie+ and does the corresponding updates.
|
|
35
45
|
def parse(set_cookie)
|
|
36
46
|
SetCookieParser.call(set_cookie) do |name, value, attrs|
|
|
37
|
-
|
|
47
|
+
set(Cookie.new(name, value, attrs))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# returns the first HTTPX::Plugins::Cookie::Cookie instance in the store which matches either the name
|
|
52
|
+
# (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+
|
|
53
|
+
def get(name_or_options)
|
|
54
|
+
each.find { |ck| ck.match?(name_or_options) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# returns all HTTPX::Plugins::Cookie::Cookie instances in the store which match either the name
|
|
58
|
+
# (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+
|
|
59
|
+
def get_all(name_or_options)
|
|
60
|
+
each.select { |ck| ck.match?(name_or_options) } # rubocop:disable Style/SelectByRegexp
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# when +name+ is a HTTPX::Plugins::Cookie::Cookie, it stores it internally; when +name+ is a String,
|
|
64
|
+
# it creates a cookie with it and the value-or-attributes passed to +value_or_options+.
|
|
65
|
+
|
|
66
|
+
# optionally, +name+ can also be the attributes hash-or-array as long it contains a <tt>:name</tt> field).
|
|
67
|
+
def set(name, value_or_options = nil)
|
|
68
|
+
cookie = case name
|
|
69
|
+
when Cookie
|
|
70
|
+
raise ArgumentError, "there should not be a second argument" if value_or_options
|
|
71
|
+
|
|
72
|
+
name
|
|
73
|
+
when Array, Hash
|
|
74
|
+
raise ArgumentError, "there should not be a second argument" if value_or_options
|
|
75
|
+
|
|
76
|
+
Cookie.new(name)
|
|
77
|
+
else
|
|
78
|
+
raise ArgumentError, "the second argument is required" unless value_or_options
|
|
79
|
+
|
|
80
|
+
Cookie.new(name, value_or_options)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
synchronize do
|
|
84
|
+
# If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value
|
|
85
|
+
# as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie.
|
|
86
|
+
@cookies.delete_if { |ck| ck.name == cookie.name && ck.domain == cookie.domain && ck.path == cookie.path }
|
|
87
|
+
|
|
88
|
+
@cookies << cookie
|
|
38
89
|
end
|
|
39
90
|
end
|
|
40
91
|
|
|
92
|
+
# @deprecated
|
|
41
93
|
def add(cookie, path = nil)
|
|
94
|
+
warn "DEPRECATION WARNING: calling `##{__method__}` is deprecated. Use `#set` instead."
|
|
42
95
|
c = cookie.dup
|
|
43
|
-
|
|
44
96
|
c.path = path if path && c.path == "/"
|
|
97
|
+
set(c)
|
|
98
|
+
end
|
|
45
99
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
100
|
+
# deletes all cookies in the store which match either the name (when String) or all attributes (when a Hash
|
|
101
|
+
# or array of tuples) passed to +name_or_options+.
|
|
102
|
+
#
|
|
103
|
+
# alternatively, of +name_or_options+ is an instance of HTTPX::Plugins::Cookies::Cookiem, it deletes it from the store.
|
|
104
|
+
def delete(name_or_options)
|
|
105
|
+
synchronize do
|
|
106
|
+
case name_or_options
|
|
107
|
+
when Cookie
|
|
108
|
+
@cookies.delete(name_or_options)
|
|
109
|
+
else
|
|
110
|
+
@cookies.delete_if { |ck| ck.match?(name_or_options) }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
51
113
|
end
|
|
52
114
|
|
|
115
|
+
# returns the list of valid cookies which matdh the domain and path from the URI object passed to +uri+.
|
|
53
116
|
def [](uri)
|
|
54
117
|
each(uri).sort
|
|
55
118
|
end
|
|
56
119
|
|
|
120
|
+
# enumerates over all stored cookies. if +uri+ is passed, it'll filter out expired cookies and
|
|
121
|
+
# only yield cookies which match its domain and path.
|
|
57
122
|
def each(uri = nil, &blk)
|
|
58
123
|
return enum_for(__method__, uri) unless blk
|
|
59
124
|
|
|
60
|
-
return @cookies.each(&blk) unless uri
|
|
125
|
+
return synchronize { @cookies.each(&blk) } unless uri
|
|
61
126
|
|
|
62
127
|
now = Time.now
|
|
63
128
|
tpath = uri.path
|
|
64
129
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
130
|
+
synchronize do
|
|
131
|
+
@cookies.delete_if do |cookie|
|
|
132
|
+
if cookie.expired?(now)
|
|
133
|
+
true
|
|
134
|
+
else
|
|
135
|
+
yield cookie if cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
|
|
136
|
+
false
|
|
137
|
+
end
|
|
71
138
|
end
|
|
72
139
|
end
|
|
73
140
|
end
|
|
74
141
|
|
|
75
142
|
def merge(other)
|
|
76
|
-
|
|
143
|
+
jar_dup = dup
|
|
77
144
|
|
|
78
145
|
other.each do |elem|
|
|
79
146
|
cookie = case elem
|
|
@@ -85,10 +152,18 @@ module HTTPX
|
|
|
85
152
|
Cookie.new(elem)
|
|
86
153
|
end
|
|
87
154
|
|
|
88
|
-
|
|
155
|
+
jar_dup.set(cookie)
|
|
89
156
|
end
|
|
90
157
|
|
|
91
|
-
|
|
158
|
+
jar_dup
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
def synchronize(&block)
|
|
164
|
+
return yield if @mtx.owned?
|
|
165
|
+
|
|
166
|
+
@mtx.synchronize(&block)
|
|
92
167
|
end
|
|
93
168
|
end
|
|
94
169
|
end
|
|
@@ -7,8 +7,6 @@ module HTTPX
|
|
|
7
7
|
#
|
|
8
8
|
# This plugin implements a persistent cookie jar for the duration of a session.
|
|
9
9
|
#
|
|
10
|
-
# It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session.
|
|
11
|
-
#
|
|
12
10
|
# https://gitlab.com/os85/httpx/wikis/Cookies
|
|
13
11
|
#
|
|
14
12
|
module Cookies
|
|
@@ -46,6 +44,12 @@ module HTTPX
|
|
|
46
44
|
request
|
|
47
45
|
end
|
|
48
46
|
|
|
47
|
+
# factory method to return a Jar to the user, which can then manipulate
|
|
48
|
+
# externally to the session.
|
|
49
|
+
def make_jar(*args)
|
|
50
|
+
Jar.new(*args)
|
|
51
|
+
end
|
|
52
|
+
|
|
49
53
|
private
|
|
50
54
|
|
|
51
55
|
def set_request_callbacks(request)
|
|
@@ -96,7 +100,7 @@ module HTTPX
|
|
|
96
100
|
cookies.each do |ck|
|
|
97
101
|
ck.split(/ *; */).each do |cookie|
|
|
98
102
|
name, value = cookie.split("=", 2)
|
|
99
|
-
jar.
|
|
103
|
+
jar.set(name, value)
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
end
|
data/lib/httpx/plugins/expect.rb
CHANGED
|
@@ -9,10 +9,34 @@ module HTTPX
|
|
|
9
9
|
#
|
|
10
10
|
module Expect
|
|
11
11
|
EXPECT_TIMEOUT = 2
|
|
12
|
+
NOEXPECT_STORE_MUTEX = Thread::Mutex.new
|
|
13
|
+
|
|
14
|
+
class Store
|
|
15
|
+
def initialize
|
|
16
|
+
@store = []
|
|
17
|
+
@mutex = Thread::Mutex.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def include?(host)
|
|
21
|
+
@mutex.synchronize { @store.include?(host) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add(host)
|
|
25
|
+
@mutex.synchronize { @store << host }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def delete(host)
|
|
29
|
+
@mutex.synchronize { @store.delete(host) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
12
32
|
|
|
13
33
|
class << self
|
|
14
34
|
def no_expect_store
|
|
15
|
-
|
|
35
|
+
return Ractor.store_if_absent(:httpx_no_expect_store) { Store.new } if Utils.in_ractor?
|
|
36
|
+
|
|
37
|
+
@no_expect_store ||= NOEXPECT_STORE_MUTEX.synchronize do
|
|
38
|
+
@no_expect_store || Store.new
|
|
39
|
+
end
|
|
16
40
|
end
|
|
17
41
|
|
|
18
42
|
def extra_options(options)
|
|
@@ -89,7 +113,7 @@ module HTTPX
|
|
|
89
113
|
set_request_timeout(:expect_timeout, request, expect_timeout, :expect, %i[body response]) do
|
|
90
114
|
# expect timeout expired
|
|
91
115
|
if request.state == :expect && !request.expects?
|
|
92
|
-
Expect.no_expect_store
|
|
116
|
+
Expect.no_expect_store.add(request.origin)
|
|
93
117
|
request.headers.delete("expect")
|
|
94
118
|
consume
|
|
95
119
|
end
|
|
@@ -160,9 +160,7 @@ module HTTPX
|
|
|
160
160
|
end
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
-
module
|
|
164
|
-
private
|
|
165
|
-
|
|
163
|
+
module ResolverNativeMethods
|
|
166
164
|
def calculate_interests
|
|
167
165
|
return if @queries.empty?
|
|
168
166
|
|
|
@@ -172,7 +170,7 @@ module HTTPX
|
|
|
172
170
|
end
|
|
173
171
|
end
|
|
174
172
|
|
|
175
|
-
module
|
|
173
|
+
module ResolverSystemMethods
|
|
176
174
|
def interests
|
|
177
175
|
return unless @queries.any? { |_, conn| conn.current_context? }
|
|
178
176
|
|
|
@@ -16,25 +16,7 @@ module HTTPX
|
|
|
16
16
|
|
|
17
17
|
class << self
|
|
18
18
|
def load_dependencies(klass)
|
|
19
|
-
klass.plugin(:retries
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Servers send the "Retry-After" header field to indicate how long the
|
|
23
|
-
# user agent ought to wait before making a follow-up request. When
|
|
24
|
-
# sent with a 503 (Service Unavailable) response, Retry-After indicates
|
|
25
|
-
# how long the service is expected to be unavailable to the client.
|
|
26
|
-
# When sent with any 3xx (Redirection) response, Retry-After indicates
|
|
27
|
-
# the minimum time that the user agent is asked to wait before issuing
|
|
28
|
-
# the redirected request.
|
|
29
|
-
#
|
|
30
|
-
def retry_after_rate_limit(_, response)
|
|
31
|
-
return unless response.is_a?(Response)
|
|
32
|
-
|
|
33
|
-
retry_after = response.headers["retry-after"]
|
|
34
|
-
|
|
35
|
-
return unless retry_after
|
|
36
|
-
|
|
37
|
-
Utils.parse_retry_after(retry_after)
|
|
19
|
+
klass.plugin(:retries)
|
|
38
20
|
end
|
|
39
21
|
end
|
|
40
22
|
|
|
@@ -52,6 +34,24 @@ module HTTPX
|
|
|
52
34
|
def rate_limit_error?(response)
|
|
53
35
|
response.is_a?(Response) && RATE_LIMIT_CODES.include?(response.status)
|
|
54
36
|
end
|
|
37
|
+
|
|
38
|
+
# Servers send the "Retry-After" header field to indicate how long the
|
|
39
|
+
# user agent ought to wait before making a follow-up request. When
|
|
40
|
+
# sent with a 503 (Service Unavailable) response, Retry-After indicates
|
|
41
|
+
# how long the service is expected to be unavailable to the client.
|
|
42
|
+
# When sent with any 3xx (Redirection) response, Retry-After indicates
|
|
43
|
+
# the minimum time that the user agent is asked to wait before issuing
|
|
44
|
+
# the redirected request.
|
|
45
|
+
#
|
|
46
|
+
def when_to_retry(_, response, options)
|
|
47
|
+
return super unless response.is_a?(Response)
|
|
48
|
+
|
|
49
|
+
retry_after = response.headers["retry-after"]
|
|
50
|
+
|
|
51
|
+
return super unless retry_after
|
|
52
|
+
|
|
53
|
+
Utils.parse_retry_after(retry_after)
|
|
54
|
+
end
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -148,10 +148,7 @@ module HTTPX
|
|
|
148
148
|
log { "failed to get response, #{request.retries} tries to go..." }
|
|
149
149
|
prepare_to_retry(request, response)
|
|
150
150
|
|
|
151
|
-
retry_after = options
|
|
152
|
-
retry_after = retry_after.call(request, response) if retry_after.respond_to?(:call)
|
|
153
|
-
|
|
154
|
-
if retry_after
|
|
151
|
+
if (retry_after = when_to_retry(request, response, options))
|
|
155
152
|
# apply jitter
|
|
156
153
|
if (jitter = request.options.retry_jitter)
|
|
157
154
|
retry_after = jitter.call(retry_after)
|
|
@@ -201,6 +198,12 @@ module HTTPX
|
|
|
201
198
|
request.transition(:idle)
|
|
202
199
|
end
|
|
203
200
|
|
|
201
|
+
def when_to_retry(request, response, options)
|
|
202
|
+
retry_after = options.retry_after
|
|
203
|
+
retry_after = retry_after.call(request, response) if retry_after.respond_to?(:call)
|
|
204
|
+
retry_after
|
|
205
|
+
end
|
|
206
|
+
|
|
204
207
|
#
|
|
205
208
|
# Attempt to set the request to perform a partial range request.
|
|
206
209
|
# This happens if the peer server accepts byte-range requests, and
|
|
@@ -237,14 +240,15 @@ module HTTPX
|
|
|
237
240
|
def initialize(*args)
|
|
238
241
|
super
|
|
239
242
|
@retries = @options.max_retries
|
|
243
|
+
@partial_response = nil
|
|
240
244
|
end
|
|
241
245
|
|
|
242
246
|
def response=(response)
|
|
243
|
-
if @partial_response
|
|
247
|
+
if (partial_response = @partial_response)
|
|
244
248
|
if response.is_a?(Response) && response.status == 206
|
|
245
|
-
response.from_partial_response(
|
|
249
|
+
response.from_partial_response(partial_response)
|
|
246
250
|
else
|
|
247
|
-
|
|
251
|
+
partial_response.close
|
|
248
252
|
end
|
|
249
253
|
@partial_response = nil
|
|
250
254
|
end
|
|
@@ -106,6 +106,7 @@ module HTTPX
|
|
|
106
106
|
error = ServerSideRequestForgeryError.new("#{request.uri} URI scheme not allowed")
|
|
107
107
|
error.set_backtrace(caller)
|
|
108
108
|
response = ErrorResponse.new(request, error)
|
|
109
|
+
request.response = response
|
|
109
110
|
request.emit(:response, response)
|
|
110
111
|
response
|
|
111
112
|
end
|
|
@@ -189,6 +189,10 @@ module HTTPX
|
|
|
189
189
|
!@closed
|
|
190
190
|
end
|
|
191
191
|
|
|
192
|
+
def force_close(*)
|
|
193
|
+
terminate
|
|
194
|
+
end
|
|
195
|
+
|
|
192
196
|
def terminate
|
|
193
197
|
return if @closed
|
|
194
198
|
|
|
@@ -202,6 +206,8 @@ module HTTPX
|
|
|
202
206
|
terminate
|
|
203
207
|
end
|
|
204
208
|
|
|
209
|
+
alias_method :on_io_error, :on_error
|
|
210
|
+
|
|
205
211
|
# noop (the owner connection will take of it)
|
|
206
212
|
def handle_socket_timeout(interval); end
|
|
207
213
|
end
|
data/lib/httpx/request.rb
CHANGED
|
@@ -91,7 +91,7 @@ module HTTPX
|
|
|
91
91
|
raise UnsupportedSchemeError, "#{@uri}: #{@uri.scheme}: unsupported URI scheme" unless ALLOWED_URI_SCHEMES.include?(@uri.scheme)
|
|
92
92
|
|
|
93
93
|
@state = :idle
|
|
94
|
-
@response = @peer_address = @informational_status = nil
|
|
94
|
+
@response = @drainer = @peer_address = @informational_status = nil
|
|
95
95
|
@ping = false
|
|
96
96
|
@persistent = @options.persistent
|
|
97
97
|
@active_timeouts = []
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -204,8 +204,7 @@ module HTTPX
|
|
|
204
204
|
rescue IOError => e
|
|
205
205
|
(Array(r) + Array(w)).each do |sel|
|
|
206
206
|
# TODO: is there a way to cheaply find the IO associated with the error?
|
|
207
|
-
sel.
|
|
208
|
-
sel.force_close(true)
|
|
207
|
+
sel.on_io_error(e)
|
|
209
208
|
end
|
|
210
209
|
rescue StandardError => e
|
|
211
210
|
(Array(r) + Array(w)).each do |sel|
|
|
@@ -249,8 +248,9 @@ module HTTPX
|
|
|
249
248
|
when :rw then rw_wait(io, interval)
|
|
250
249
|
end
|
|
251
250
|
rescue IOError => e
|
|
252
|
-
io.
|
|
253
|
-
|
|
251
|
+
io.on_io_error(e)
|
|
252
|
+
|
|
253
|
+
return
|
|
254
254
|
rescue StandardError => e
|
|
255
255
|
io.on_error(e)
|
|
256
256
|
|
data/lib/httpx/session.rb
CHANGED
|
@@ -140,9 +140,10 @@ module HTTPX
|
|
|
140
140
|
end
|
|
141
141
|
selector.deregister(connection)
|
|
142
142
|
|
|
143
|
+
# do not check-in connections only created for Happy Eyeballs
|
|
143
144
|
return if cloned
|
|
144
145
|
|
|
145
|
-
return if @closing && connection.state == :closed
|
|
146
|
+
return if @closing && connection.state == :closed && !connection.used?
|
|
146
147
|
|
|
147
148
|
connection.log(level: 2) { "check-in connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
|
|
148
149
|
@pool.checkin_connection(connection)
|
|
@@ -177,16 +178,15 @@ module HTTPX
|
|
|
177
178
|
|
|
178
179
|
# returns the HTTPX::Connection through which the +request+ should be sent through.
|
|
179
180
|
def find_connection(request_uri, selector, options)
|
|
180
|
-
log(level: 2) { "finding connection for #{request_uri}..." }
|
|
181
181
|
if (connection = selector.find_connection(request_uri, options))
|
|
182
182
|
connection.idling if connection.state == :closed
|
|
183
|
-
|
|
183
|
+
log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
|
|
184
184
|
return connection
|
|
185
185
|
end
|
|
186
186
|
|
|
187
187
|
connection = @pool.checkout_connection(request_uri, options)
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
|
|
190
190
|
|
|
191
191
|
case connection.state
|
|
192
192
|
when :idle
|
|
@@ -240,7 +240,7 @@ module HTTPX
|
|
|
240
240
|
|
|
241
241
|
return unless response && response.finished?
|
|
242
242
|
|
|
243
|
-
log(level: 2) { "response fetched" }
|
|
243
|
+
log(level: 2) { "response##{response.object_id} fetched" }
|
|
244
244
|
|
|
245
245
|
response
|
|
246
246
|
end
|
|
@@ -249,6 +249,7 @@ module HTTPX
|
|
|
249
249
|
def send_request(request, selector, options = request.options)
|
|
250
250
|
error = begin
|
|
251
251
|
catch(:resolve_error) do
|
|
252
|
+
log(level: 2) { "finding connection for request##{request.object_id}..." }
|
|
252
253
|
connection = find_connection(request.uri, selector, options)
|
|
253
254
|
connection.send(request)
|
|
254
255
|
end
|
data/lib/httpx/version.rb
CHANGED
data/sig/chainable.rbs
CHANGED
|
@@ -29,7 +29,7 @@ module HTTPX
|
|
|
29
29
|
| (:proxy, ?options) -> (Plugins::sessionProxy & Plugins::httpProxy)
|
|
30
30
|
| (:push_promise, ?options) -> Plugins::sessionPushPromise
|
|
31
31
|
| (:retries, ?options) -> Plugins::sessionRetries
|
|
32
|
-
| (:rate_limiter, ?options) ->
|
|
32
|
+
| (:rate_limiter, ?options) -> Plugins::sessionRateLimiter
|
|
33
33
|
| (:stream, ?options) -> Plugins::sessionStream
|
|
34
34
|
| (:stream_bidi, ?options) -> Plugins::sessionStreamBidi
|
|
35
35
|
| (:aws_sigv4, ?options) -> Plugins::awsSigV4Session
|
data/sig/connection/http2.rbs
CHANGED
|
@@ -79,6 +79,8 @@ module HTTPX
|
|
|
79
79
|
|
|
80
80
|
def on_stream_refuse: (::HTTP2::Stream stream, Request request, StandardError error) -> void
|
|
81
81
|
|
|
82
|
+
def on_stream_half_close: (::HTTP2::Stream stream, Request request) -> void
|
|
83
|
+
|
|
82
84
|
def on_stream_close: (::HTTP2::Stream stream, Request request, (Symbol | StandardError)? error) -> void
|
|
83
85
|
|
|
84
86
|
def on_frame: (string bytes) -> void
|
|
@@ -87,13 +89,15 @@ module HTTPX
|
|
|
87
89
|
|
|
88
90
|
def on_close: (Integer last_frame, Symbol? error, String? payload) -> void
|
|
89
91
|
|
|
90
|
-
def on_frame_sent: (::HTTP2::frame) -> void
|
|
92
|
+
def on_frame_sent: (::HTTP2::frame frame) -> void
|
|
93
|
+
|
|
94
|
+
def frame_with_extra_info: (::HTTP2::frame frame) -> Hash[Symbol, untyped]
|
|
91
95
|
|
|
92
|
-
def on_frame_received: (::HTTP2::frame) -> void
|
|
96
|
+
def on_frame_received: (::HTTP2::frame frame) -> void
|
|
93
97
|
|
|
94
|
-
def on_altsvc: (String origin, ::HTTP2::frame) -> void
|
|
98
|
+
def on_altsvc: (String origin, ::HTTP2::frame frame) -> void
|
|
95
99
|
|
|
96
|
-
def on_promise: (::HTTP2::Stream) -> void
|
|
100
|
+
def on_promise: (::HTTP2::Stream stream) -> void
|
|
97
101
|
|
|
98
102
|
def on_origin: (String) -> void
|
|
99
103
|
|
data/sig/connection.rbs
CHANGED
|
@@ -37,6 +37,7 @@ module HTTPX
|
|
|
37
37
|
@read_buffer: Buffer
|
|
38
38
|
@write_buffer: Buffer
|
|
39
39
|
@inflight: Integer
|
|
40
|
+
@max_concurrent_requests: Integer?
|
|
40
41
|
@keep_alive_timeout: Numeric?
|
|
41
42
|
@timeout: Numeric?
|
|
42
43
|
@current_timeout: Numeric?
|
|
@@ -119,6 +120,8 @@ module HTTPX
|
|
|
119
120
|
|
|
120
121
|
def on_error: (HTTPX::TimeoutError | Error | StandardError error, ?Request? request) -> void
|
|
121
122
|
|
|
123
|
+
def on_io_error: (IOError error) -> void
|
|
124
|
+
|
|
122
125
|
private
|
|
123
126
|
|
|
124
127
|
def initialize: (http_uri uri, Options options) -> void
|
|
@@ -31,6 +31,8 @@ module HTTPX
|
|
|
31
31
|
def cookie_value: () -> String
|
|
32
32
|
alias to_s cookie_value
|
|
33
33
|
|
|
34
|
+
def match?: (String | cookie_attributes name_or_options) -> bool
|
|
35
|
+
|
|
34
36
|
def valid_for_uri?: (http_uri uri) -> bool
|
|
35
37
|
|
|
36
38
|
def self.new: (Cookie) -> instance
|
|
@@ -41,8 +43,7 @@ module HTTPX
|
|
|
41
43
|
|
|
42
44
|
private
|
|
43
45
|
|
|
44
|
-
def initialize: (cookie_attributes) ->
|
|
45
|
-
| (_ToS, _ToS, ?cookie_attributes) -> untyped
|
|
46
|
+
def initialize: (_ToS name, _ToS value, ?cookie_attributes) -> void
|
|
46
47
|
|
|
47
48
|
def acceptable_from_uri?: (uri) -> bool
|
|
48
49
|
|
data/sig/plugins/cookies/jar.rbs
CHANGED
|
@@ -6,11 +6,20 @@ module HTTPX
|
|
|
6
6
|
include Enumerable[Cookie]
|
|
7
7
|
|
|
8
8
|
@cookies: Array[Cookie]
|
|
9
|
+
@mtx: Thread::Mutex
|
|
9
10
|
|
|
10
11
|
def parse: (String set_cookie) -> void
|
|
11
12
|
|
|
13
|
+
def get: (String | cookie_attributes name_or_options) -> Cookie?
|
|
14
|
+
|
|
15
|
+
def get_all: (String | cookie_attributes name_or_options) -> Array[Cookie]
|
|
16
|
+
|
|
17
|
+
def set: (_ToS | Cookie name, ?(cookie_attributes | _ToS) value_or_options) -> void
|
|
18
|
+
|
|
12
19
|
def add: (Cookie name, ?String path) -> void
|
|
13
20
|
|
|
21
|
+
def delete: (String | Cookie | cookie_attributes name_or_options) -> void
|
|
22
|
+
|
|
14
23
|
def []: (http_uri) -> Array[Cookie]
|
|
15
24
|
|
|
16
25
|
def each: (?http_uri?) { (Cookie) -> void } -> void
|
|
@@ -21,6 +30,8 @@ module HTTPX
|
|
|
21
30
|
private
|
|
22
31
|
|
|
23
32
|
def initialize: (?_Each[cookie] cookies) -> untyped
|
|
33
|
+
|
|
34
|
+
def synchronize: [T] { () -> T } -> T
|
|
24
35
|
end
|
|
25
36
|
end
|
|
26
37
|
end
|
data/sig/plugins/cookies.rbs
CHANGED
data/sig/plugins/expect.rbs
CHANGED
|
@@ -2,14 +2,29 @@ module HTTPX
|
|
|
2
2
|
module Plugins
|
|
3
3
|
module Expect
|
|
4
4
|
EXPECT_TIMEOUT: Integer
|
|
5
|
+
NOEXPECT_STORE_MUTEX: Thread::Mutex
|
|
6
|
+
|
|
7
|
+
self.@no_expect_store: Store
|
|
8
|
+
def self.no_expect_store: () -> Store
|
|
9
|
+
|
|
10
|
+
def self.extra_options: (Options) -> (Options & _ExpectOptions)
|
|
11
|
+
|
|
12
|
+
class Store
|
|
13
|
+
@store: Array[String]
|
|
14
|
+
@mutex: Thread::Mutex
|
|
15
|
+
|
|
16
|
+
def include?: (String host) -> bool
|
|
17
|
+
|
|
18
|
+
def add: (String host) -> void
|
|
19
|
+
|
|
20
|
+
def delete: (String host) -> void
|
|
21
|
+
end
|
|
5
22
|
|
|
6
23
|
interface _ExpectOptions
|
|
7
24
|
def expect_timeout: () -> Integer?
|
|
8
25
|
|
|
9
26
|
def expect_threshold_size: () -> Integer?
|
|
10
27
|
end
|
|
11
|
-
|
|
12
|
-
def self.extra_options: (Options) -> (Options & _ExpectOptions)
|
|
13
28
|
end
|
|
14
29
|
end
|
|
15
30
|
end
|
|
@@ -5,11 +5,11 @@ module HTTPX
|
|
|
5
5
|
|
|
6
6
|
def self.load_dependencies: (singleton(Session)) -> void
|
|
7
7
|
|
|
8
|
-
def self.retry_after_rate_limit: (untyped, response) -> Numeric?
|
|
9
|
-
|
|
10
8
|
module InstanceMethods
|
|
11
9
|
def rate_limit_error?: (response response) -> bool
|
|
12
10
|
end
|
|
13
11
|
end
|
|
12
|
+
|
|
13
|
+
type sessionRateLimiter = Session & RateLimiter::InstanceMethods
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -51,7 +51,7 @@ module HTTPX
|
|
|
51
51
|
module ResponseMethods
|
|
52
52
|
attr_writer original_request: cacheRequest
|
|
53
53
|
|
|
54
|
-
@
|
|
54
|
+
@cached: bool
|
|
55
55
|
@cache_control: Array[String]?
|
|
56
56
|
@vary: Array[String]?
|
|
57
57
|
@date: Time?
|
|
@@ -66,9 +66,9 @@ module HTTPX
|
|
|
66
66
|
|
|
67
67
|
def fresh?: () -> bool
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
%a{pure} def cache_control: () -> Array[String]?
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
%a{pure} def vary: () -> Array[String]?
|
|
72
72
|
|
|
73
73
|
private
|
|
74
74
|
|
data/sig/plugins/retries.rbs
CHANGED
|
@@ -8,16 +8,16 @@ module HTTPX
|
|
|
8
8
|
DEFAULT_JITTER: ^(Numeric) -> Numeric
|
|
9
9
|
BACKOFF_ALGORITHMS: Array[Symbol]
|
|
10
10
|
|
|
11
|
-
def self?.retry_after_polynomial_backoff: (retriesRequest request,
|
|
11
|
+
def self?.retry_after_polynomial_backoff: (retriesRequest request, retriesResponse response) -> Numeric
|
|
12
12
|
|
|
13
|
-
def self?.retry_after_exponential_backoff: (retriesRequest request,
|
|
13
|
+
def self?.retry_after_exponential_backoff: (retriesRequest request, retriesResponse response) -> Numeric
|
|
14
14
|
|
|
15
15
|
interface _RetryCallback
|
|
16
|
-
def call: (
|
|
16
|
+
def call: (retriesResponse response) -> bool?
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
interface _RetriesOptions
|
|
20
|
-
def retry_after: () -> (^(retriesRequest request,
|
|
20
|
+
def retry_after: () -> (^(retriesRequest request, retriesResponse response) -> Numeric | Numeric)?
|
|
21
21
|
|
|
22
22
|
def retry_jitter: () -> ^(Numeric jitter) -> Numeric
|
|
23
23
|
|
|
@@ -35,17 +35,19 @@ module HTTPX
|
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
|
-
def fetch_response: (retriesRequest request, Selector selector, retriesOptions options) ->
|
|
38
|
+
def fetch_response: (retriesRequest request, Selector selector, retriesOptions options) -> retriesResponse?
|
|
39
39
|
|
|
40
|
-
def retryable_request?: (retriesRequest request,
|
|
40
|
+
def retryable_request?: (retriesRequest request, retriesResponse response, retriesOptions options) -> boolish
|
|
41
41
|
|
|
42
|
-
def retryable_response?: (
|
|
42
|
+
def retryable_response?: (retriesResponse response, retriesOptions options) -> boolish
|
|
43
43
|
|
|
44
44
|
def retryable_error?: (_Exception error, Options options) -> boolish
|
|
45
45
|
|
|
46
|
-
def try_partial_retry: (retriesRequest request,
|
|
46
|
+
def try_partial_retry: (retriesRequest request, retriesResponse response) -> void
|
|
47
47
|
|
|
48
|
-
def prepare_to_retry: (Request & RequestMethods request,
|
|
48
|
+
def prepare_to_retry: (Request & RequestMethods request, retriesResponse response) -> void
|
|
49
|
+
|
|
50
|
+
def when_to_retry: (Request & RequestMethods request, retriesResponse response, retriesOptions options) -> void
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
module RequestMethods
|
|
@@ -53,20 +55,22 @@ module HTTPX
|
|
|
53
55
|
|
|
54
56
|
attr_accessor retries: Integer
|
|
55
57
|
|
|
56
|
-
attr_writer partial_response:
|
|
58
|
+
attr_writer partial_response: retriesResponse?
|
|
57
59
|
|
|
58
|
-
def response=: (retriesResponse
|
|
60
|
+
def response=: (retriesResponse response) -> void
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
module ResponseMethods
|
|
62
|
-
def from_partial_response: (
|
|
64
|
+
def from_partial_response: (retriesHTTPResponse response) -> void
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
type retriesOptions = Options & _RetriesOptions
|
|
66
68
|
|
|
67
69
|
type retriesRequest = Request & RequestMethods
|
|
68
70
|
|
|
69
|
-
type
|
|
71
|
+
type retriesHTTPResponse = Response & ResponseMethods
|
|
72
|
+
|
|
73
|
+
type retriesResponse = retriesHTTPResponse | ErrorResponse
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
type sessionRetries = Session & Retries::InstanceMethods
|
data/sig/request.rbs
CHANGED
data/sig/resolver/native.rbs
CHANGED
data/sig/resolver/resolver.rbs
CHANGED
|
@@ -27,8 +27,6 @@ module HTTPX
|
|
|
27
27
|
|
|
28
28
|
def terminate: () -> void
|
|
29
29
|
|
|
30
|
-
def force_close: (*untyped args) -> void
|
|
31
|
-
|
|
32
30
|
def closed?: () -> bool
|
|
33
31
|
|
|
34
32
|
def empty?: () -> bool
|
|
@@ -60,6 +58,8 @@ module HTTPX
|
|
|
60
58
|
def resolve_error: (String hostname, ?StandardError?) -> (ResolveError | ResolveTimeoutError)
|
|
61
59
|
|
|
62
60
|
def close_or_resolve: () -> void
|
|
61
|
+
|
|
62
|
+
def disconnect: () -> void
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
data/sig/selector.rbs
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: httpx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.7.
|
|
4
|
+
version: 1.7.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 1.
|
|
18
|
+
version: 1.1.3
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 1.
|
|
25
|
+
version: 1.1.3
|
|
26
26
|
description: A client library for making HTTP requests from Ruby.
|
|
27
27
|
email:
|
|
28
28
|
- cardoso_tiago@hotmail.com
|
|
@@ -162,6 +162,7 @@ extra_rdoc_files:
|
|
|
162
162
|
- doc/release_notes/1_7_0.md
|
|
163
163
|
- doc/release_notes/1_7_1.md
|
|
164
164
|
- doc/release_notes/1_7_2.md
|
|
165
|
+
- doc/release_notes/1_7_3.md
|
|
165
166
|
files:
|
|
166
167
|
- LICENSE.txt
|
|
167
168
|
- README.md
|
|
@@ -296,6 +297,7 @@ files:
|
|
|
296
297
|
- doc/release_notes/1_7_0.md
|
|
297
298
|
- doc/release_notes/1_7_1.md
|
|
298
299
|
- doc/release_notes/1_7_2.md
|
|
300
|
+
- doc/release_notes/1_7_3.md
|
|
299
301
|
- lib/httpx.rb
|
|
300
302
|
- lib/httpx/adapters/datadog.rb
|
|
301
303
|
- lib/httpx/adapters/faraday.rb
|