httpx 0.20.0 → 1.3.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/LICENSE.txt +0 -48
- data/README.md +54 -45
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +5 -5
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_19_8.md +1 -1
- data/doc/release_notes/0_20_0.md +2 -2
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/lib/httpx/adapters/datadog.rb +215 -122
- data/lib/httpx/adapters/faraday.rb +145 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +34 -18
- data/lib/httpx/altsvc.rb +63 -26
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +12 -0
- data/lib/httpx/callbacks.rb +5 -3
- data/lib/httpx/chainable.rb +54 -39
- data/lib/httpx/connection/http1.rb +75 -44
- data/lib/httpx/connection/http2.rb +31 -38
- data/lib/httpx/connection.rb +287 -117
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +52 -2
- data/lib/httpx/extensions.rb +24 -131
- data/lib/httpx/io/ssl.rb +83 -77
- data/lib/httpx/io/tcp.rb +48 -71
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +10 -15
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +176 -118
- data/lib/httpx/parser/http1.rb +4 -0
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
- data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
- data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
- data/lib/httpx/plugins/aws_sigv4.rb +12 -9
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +148 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +30 -17
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
- data/lib/httpx/plugins/expect.rb +21 -14
- data/lib/httpx/plugins/follow_redirects.rb +140 -41
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -37
- data/lib/httpx/plugins/grpc.rb +36 -29
- data/lib/httpx/plugins/h2c.rb +26 -19
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
- data/lib/httpx/plugins/oauth.rb +175 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +23 -13
- data/lib/httpx/plugins/proxy/socks4.rb +9 -7
- data/lib/httpx/plugins/proxy/socks5.rb +11 -9
- data/lib/httpx/plugins/proxy.rb +80 -61
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +5 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +62 -25
- data/lib/httpx/plugins/response_cache.rb +105 -12
- data/lib/httpx/plugins/retries.rb +87 -17
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +27 -23
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +80 -0
- data/lib/httpx/pool/synch_pool.rb +93 -0
- data/lib/httpx/pool.rb +102 -27
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +154 -0
- data/lib/httpx/request.rb +130 -146
- data/lib/httpx/resolver/https.rb +62 -27
- data/lib/httpx/resolver/multi.rb +9 -13
- data/lib/httpx/resolver/native.rb +192 -76
- data/lib/httpx/resolver/resolver.rb +34 -9
- data/lib/httpx/resolver/system.rb +16 -11
- data/lib/httpx/resolver.rb +38 -16
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +96 -0
- data/lib/httpx/response.rb +159 -217
- data/lib/httpx/selector.rb +9 -4
- data/lib/httpx/session.rb +137 -89
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +34 -8
- data/lib/httpx/transcoder/body.rb +0 -2
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -33
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +21 -8
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
- data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
- data/lib/httpx/transcoder/multipart.rb +17 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +72 -0
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/transcoder/xml.rb +52 -0
- data/lib/httpx/transcoder.rb +5 -6
- data/lib/httpx/utils.rb +36 -16
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +12 -14
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +2 -1
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +11 -9
- data/sig/connection/http1.rbs +8 -7
- data/sig/connection/http2.rbs +19 -19
- data/sig/connection.rbs +64 -24
- data/sig/errors.rbs +22 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +27 -0
- data/sig/io.rbs +6 -0
- data/sig/options.rbs +32 -22
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +18 -4
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +7 -32
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +54 -0
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks4.rbs +9 -6
- data/sig/plugins/proxy/socks5.rbs +10 -6
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +13 -5
- data/sig/plugins/push_promise.rbs +3 -3
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache.rbs +36 -7
- data/sig/plugins/retries.rbs +30 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +10 -7
- data/sig/request/body.rbs +38 -0
- data/sig/request.rbs +15 -24
- data/sig/resolver/https.rbs +8 -3
- data/sig/resolver/native.rbs +17 -4
- data/sig/resolver/resolver.rbs +8 -6
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +9 -5
- data/sig/response/body.rbs +53 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +24 -39
- data/sig/selector.rbs +1 -1
- data/sig/session.rbs +29 -18
- data/sig/timers.rbs +18 -8
- data/sig/transcoder/body.rbs +4 -3
- data/sig/transcoder/deflate.rbs +11 -0
- data/sig/transcoder/form.rbs +5 -3
- data/sig/transcoder/gzip.rbs +24 -0
- data/sig/transcoder/json.rbs +8 -3
- data/sig/{plugins → transcoder}/multipart.rbs +15 -19
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder/utils/inflater.rbs +12 -0
- data/sig/transcoder/xml.rbs +22 -0
- data/sig/transcoder.rbs +24 -9
- data/sig/utils.rbs +8 -2
- metadata +163 -41
- data/lib/httpx/plugins/authentication.rb +0 -20
- data/lib/httpx/plugins/basic_authentication.rb +0 -30
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -49
- data/lib/httpx/plugins/compression/gzip.rb +0 -88
- data/lib/httpx/plugins/compression.rb +0 -164
- data/lib/httpx/plugins/multipart/decoder.rb +0 -187
- data/lib/httpx/plugins/multipart.rb +0 -84
- data/lib/httpx/registry.rb +0 -85
- data/sig/plugins/authentication.rbs +0 -11
- data/sig/plugins/compression/brotli.rbs +0 -21
- data/sig/plugins/compression/deflate.rbs +0 -17
- data/sig/plugins/compression/gzip.rbs +0 -29
- data/sig/registry.rbs +0 -12
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
data/lib/httpx/callbacks.rb
CHANGED
@@ -4,6 +4,7 @@ module HTTPX
|
|
4
4
|
module Callbacks
|
5
5
|
def on(type, &action)
|
6
6
|
callbacks(type) << action
|
7
|
+
self
|
7
8
|
end
|
8
9
|
|
9
10
|
def once(type, &block)
|
@@ -11,6 +12,7 @@ module HTTPX
|
|
11
12
|
block.call(*args, &callback)
|
12
13
|
:delete
|
13
14
|
end
|
15
|
+
self
|
14
16
|
end
|
15
17
|
|
16
18
|
def only(type, &block)
|
@@ -22,12 +24,12 @@ module HTTPX
|
|
22
24
|
callbacks(type).delete_if { |pr| :delete == pr.call(*args) } # rubocop:disable Style/YodaCondition
|
23
25
|
end
|
24
26
|
|
25
|
-
protected
|
26
|
-
|
27
27
|
def callbacks_for?(type)
|
28
|
-
@callbacks.key?(type) &&
|
28
|
+
@callbacks.key?(type) && @callbacks[type].any?
|
29
29
|
end
|
30
30
|
|
31
|
+
protected
|
32
|
+
|
31
33
|
def callbacks(type = nil)
|
32
34
|
return @callbacks unless type
|
33
35
|
|
data/lib/httpx/chainable.rb
CHANGED
@@ -1,89 +1,104 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
+
# Session mixin, implements most of the APIs that the users call.
|
5
|
+
# delegates to a default session when extended.
|
4
6
|
module Chainable
|
5
|
-
%
|
7
|
+
%w[head get post put delete trace options connect patch].each do |meth|
|
6
8
|
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
7
|
-
def #{meth}(*uri, **options)
|
8
|
-
request(
|
9
|
-
end
|
9
|
+
def #{meth}(*uri, **options) # def get(*uri, **options)
|
10
|
+
request("#{meth.upcase}", uri, **options) # request("GET", uri, **options)
|
11
|
+
end # end
|
10
12
|
MOD
|
11
13
|
end
|
12
14
|
|
15
|
+
# delegates to the default session (see HTTPX::Session#request).
|
13
16
|
def request(*args, **options)
|
14
17
|
branch(default_options).request(*args, **options)
|
15
18
|
end
|
16
19
|
|
17
|
-
# :nocov:
|
18
|
-
def timeout(**args)
|
19
|
-
warn ":#{__method__} is deprecated, use :with_timeout instead"
|
20
|
-
with(timeout: args)
|
21
|
-
end
|
22
|
-
|
23
|
-
def headers(headers)
|
24
|
-
warn ":#{__method__} is deprecated, use :with_headers instead"
|
25
|
-
with(headers: headers)
|
26
|
-
end
|
27
|
-
# :nocov:
|
28
|
-
|
29
20
|
def accept(type)
|
30
21
|
with(headers: { "accept" => String(type) })
|
31
22
|
end
|
32
23
|
|
24
|
+
# delegates to the default session (see HTTPX::Session#wrap).
|
33
25
|
def wrap(&blk)
|
34
26
|
branch(default_options).wrap(&blk)
|
35
27
|
end
|
36
28
|
|
29
|
+
# returns a new instance loaded with the +pl+ plugin and +options+.
|
37
30
|
def plugin(pl, options = nil, &blk)
|
38
|
-
klass = is_a?(
|
31
|
+
klass = is_a?(S) ? self.class : Session
|
39
32
|
klass = Class.new(klass)
|
40
33
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
41
34
|
klass.plugin(pl, options, &blk).new
|
42
35
|
end
|
43
36
|
|
44
|
-
#
|
45
|
-
# :nocov:
|
46
|
-
def plugins(pls)
|
47
|
-
warn ":#{__method__} is deprecated, use :plugin instead"
|
48
|
-
klass = is_a?(Session) ? self.class : Session
|
49
|
-
klass = Class.new(klass)
|
50
|
-
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
51
|
-
klass.plugins(pls).new
|
52
|
-
end
|
53
|
-
# :nocov:
|
54
|
-
|
37
|
+
# returns a new instance loaded with +options+.
|
55
38
|
def with(options, &blk)
|
56
39
|
branch(default_options.merge(options), &blk)
|
57
40
|
end
|
58
41
|
|
59
42
|
private
|
60
43
|
|
44
|
+
# returns default instance of HTTPX::Options.
|
61
45
|
def default_options
|
62
46
|
@options || Session.default_options
|
63
47
|
end
|
64
48
|
|
49
|
+
# returns a default instance of HTTPX::Session.
|
65
50
|
def branch(options, &blk)
|
66
|
-
return self.class.new(options, &blk) if is_a?(
|
51
|
+
return self.class.new(options, &blk) if is_a?(S)
|
67
52
|
|
68
53
|
Session.new(options, &blk)
|
69
54
|
end
|
70
55
|
|
71
|
-
def method_missing(meth, *args, **options)
|
72
|
-
|
56
|
+
def method_missing(meth, *args, **options, &blk)
|
57
|
+
case meth
|
58
|
+
when /\Awith_(.+)/
|
73
59
|
|
74
|
-
|
60
|
+
option = Regexp.last_match(1)
|
75
61
|
|
76
|
-
|
62
|
+
return super unless option
|
77
63
|
|
78
|
-
|
79
|
-
|
64
|
+
with(option.to_sym => args.first || options)
|
65
|
+
when /\Aon_(.+)/
|
66
|
+
callback = Regexp.last_match(1)
|
80
67
|
|
81
|
-
|
82
|
-
|
68
|
+
return super unless %w[
|
69
|
+
connection_opened connection_closed
|
70
|
+
request_error
|
71
|
+
request_started request_body_chunk request_completed
|
72
|
+
response_started response_body_chunk response_completed
|
73
|
+
].include?(callback)
|
83
74
|
|
84
|
-
|
75
|
+
warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
|
76
|
+
"Use HTTPX.plugin(:callbacks).#{meth} instead."
|
85
77
|
|
86
|
-
|
78
|
+
plugin(:callbacks).__send__(meth, *args, **options, &blk)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def respond_to_missing?(meth, *)
|
85
|
+
case meth
|
86
|
+
when /\Awith_(.+)/
|
87
|
+
option = Regexp.last_match(1)
|
88
|
+
|
89
|
+
default_options.respond_to?(option) || super
|
90
|
+
when /\Aon_(.+)/
|
91
|
+
callback = Regexp.last_match(1)
|
92
|
+
|
93
|
+
%w[
|
94
|
+
connection_opened connection_closed
|
95
|
+
request_error
|
96
|
+
request_started request_body_chunk request_completed
|
97
|
+
response_started response_body_chunk response_completed
|
98
|
+
].include?(callback) || super
|
99
|
+
else
|
100
|
+
super
|
101
|
+
end
|
87
102
|
end
|
88
103
|
end
|
89
104
|
end
|
@@ -7,15 +7,17 @@ module HTTPX
|
|
7
7
|
include Callbacks
|
8
8
|
include Loggable
|
9
9
|
|
10
|
-
MAX_REQUESTS =
|
10
|
+
MAX_REQUESTS = 200
|
11
11
|
CRLF = "\r\n"
|
12
12
|
|
13
13
|
attr_reader :pending, :requests
|
14
14
|
|
15
|
+
attr_accessor :max_concurrent_requests
|
16
|
+
|
15
17
|
def initialize(buffer, options)
|
16
|
-
@options =
|
18
|
+
@options = options
|
17
19
|
@max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
|
18
|
-
@max_requests = @options.max_requests
|
20
|
+
@max_requests = @options.max_requests
|
19
21
|
@parser = Parser::HTTP1.new(self)
|
20
22
|
@buffer = buffer
|
21
23
|
@version = [1, 1]
|
@@ -47,6 +49,7 @@ module HTTPX
|
|
47
49
|
@max_requests = @options.max_requests || MAX_REQUESTS
|
48
50
|
@parser.reset!
|
49
51
|
@handshake_completed = false
|
52
|
+
@pending.concat(@requests) unless @requests.empty?
|
50
53
|
end
|
51
54
|
|
52
55
|
def close
|
@@ -106,6 +109,7 @@ module HTTPX
|
|
106
109
|
|
107
110
|
def on_headers(h)
|
108
111
|
@request = @requests.first
|
112
|
+
|
109
113
|
return if @request.response
|
110
114
|
|
111
115
|
log(level: 2) { "headers received" }
|
@@ -118,7 +122,7 @@ module HTTPX
|
|
118
122
|
log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
119
123
|
|
120
124
|
@request.response = response
|
121
|
-
on_complete if response.
|
125
|
+
on_complete if response.finished?
|
122
126
|
end
|
123
127
|
|
124
128
|
def on_trailers(h)
|
@@ -132,32 +136,42 @@ module HTTPX
|
|
132
136
|
end
|
133
137
|
|
134
138
|
def on_data(chunk)
|
135
|
-
|
139
|
+
request = @request
|
140
|
+
|
141
|
+
return unless request
|
136
142
|
|
137
143
|
log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
138
144
|
log(level: 2, color: :green) { "-> #{chunk.inspect}" }
|
139
|
-
response =
|
145
|
+
response = request.response
|
140
146
|
|
141
147
|
response << chunk
|
148
|
+
rescue StandardError => e
|
149
|
+
error_response = ErrorResponse.new(request, e)
|
150
|
+
request.response = error_response
|
151
|
+
dispatch
|
142
152
|
end
|
143
153
|
|
144
154
|
def on_complete
|
145
|
-
|
155
|
+
request = @request
|
156
|
+
|
157
|
+
return unless request
|
146
158
|
|
147
159
|
log(level: 2) { "parsing complete" }
|
148
160
|
dispatch
|
149
161
|
end
|
150
162
|
|
151
163
|
def dispatch
|
152
|
-
|
164
|
+
request = @request
|
165
|
+
|
166
|
+
if request.expects?
|
153
167
|
@parser.reset!
|
154
|
-
return handle(
|
168
|
+
return handle(request)
|
155
169
|
end
|
156
170
|
|
157
|
-
request = @request
|
158
171
|
@request = nil
|
159
172
|
@requests.shift
|
160
173
|
response = request.response
|
174
|
+
response.finish! unless response.is_a?(ErrorResponse)
|
161
175
|
emit(:response, request, response)
|
162
176
|
|
163
177
|
if @parser.upgrade?
|
@@ -167,12 +181,23 @@ module HTTPX
|
|
167
181
|
|
168
182
|
@parser.reset!
|
169
183
|
@max_requests -= 1
|
170
|
-
|
184
|
+
if response.is_a?(ErrorResponse)
|
185
|
+
disable
|
186
|
+
else
|
187
|
+
manage_connection(request, response)
|
188
|
+
end
|
171
189
|
|
172
|
-
|
190
|
+
if exhausted?
|
191
|
+
@pending.concat(@requests)
|
192
|
+
@requests.clear
|
193
|
+
|
194
|
+
emit(:exhausted)
|
195
|
+
else
|
196
|
+
send(@pending.shift) unless @pending.empty?
|
197
|
+
end
|
173
198
|
end
|
174
199
|
|
175
|
-
def handle_error(ex)
|
200
|
+
def handle_error(ex, request = nil)
|
176
201
|
if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
|
177
202
|
!@request.response.headers.key?("content-length") &&
|
178
203
|
!@request.response.headers.key?("transfer-encoding")
|
@@ -186,23 +211,28 @@ module HTTPX
|
|
186
211
|
if @pipelining
|
187
212
|
catch(:called) { disable }
|
188
213
|
else
|
189
|
-
@requests.each do |
|
190
|
-
|
214
|
+
@requests.each do |req|
|
215
|
+
next if request && request == req
|
216
|
+
|
217
|
+
emit(:error, req, ex)
|
191
218
|
end
|
192
|
-
@pending.each do |
|
193
|
-
|
219
|
+
@pending.each do |req|
|
220
|
+
next if request && request == req
|
221
|
+
|
222
|
+
emit(:error, req, ex)
|
194
223
|
end
|
195
224
|
end
|
196
225
|
end
|
197
226
|
|
198
227
|
def ping
|
228
|
+
reset
|
199
229
|
emit(:reset)
|
200
230
|
emit(:exhausted)
|
201
231
|
end
|
202
232
|
|
203
233
|
private
|
204
234
|
|
205
|
-
def manage_connection(response)
|
235
|
+
def manage_connection(request, response)
|
206
236
|
connection = response.headers["connection"]
|
207
237
|
case connection
|
208
238
|
when /keep-alive/i
|
@@ -219,7 +249,7 @@ module HTTPX
|
|
219
249
|
return unless keep_alive
|
220
250
|
|
221
251
|
parameters = Hash[keep_alive.split(/ *, */).map do |pair|
|
222
|
-
pair.split(/ *=
|
252
|
+
pair.split(/ *= */, 2)
|
223
253
|
end]
|
224
254
|
@max_requests = parameters["max"].to_i - 1 if parameters.key?("max")
|
225
255
|
|
@@ -232,7 +262,7 @@ module HTTPX
|
|
232
262
|
disable
|
233
263
|
when nil
|
234
264
|
# In HTTP/1.1, it's keep alive by default
|
235
|
-
return if response.version == "1.1"
|
265
|
+
return if response.version == "1.1" && request.headers["connection"] != "close"
|
236
266
|
|
237
267
|
disable
|
238
268
|
end
|
@@ -240,6 +270,7 @@ module HTTPX
|
|
240
270
|
|
241
271
|
def disable
|
242
272
|
disable_pipelining
|
273
|
+
reset
|
243
274
|
emit(:reset)
|
244
275
|
throw(:called)
|
245
276
|
end
|
@@ -270,29 +301,31 @@ module HTTPX
|
|
270
301
|
request.body.chunk!
|
271
302
|
end
|
272
303
|
|
273
|
-
|
304
|
+
extra_headers = {}
|
274
305
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
# when it's not a persistent connection, it sets "Connection: close" always
|
285
|
-
# on the last request of the possible batch (either allowed max requests,
|
286
|
-
# or if smaller, the size of the batch itself)
|
287
|
-
requests_limit = [@max_requests, @requests.size].min
|
288
|
-
if request == @requests[requests_limit - 1]
|
289
|
-
"close"
|
306
|
+
unless request.headers.key?("connection")
|
307
|
+
connection_value = if request.persistent?
|
308
|
+
# when in a persistent connection, the request can't be at
|
309
|
+
# the edge of a renegotiation
|
310
|
+
if @requests.index(request) + 1 < @max_requests
|
311
|
+
"keep-alive"
|
312
|
+
else
|
313
|
+
"close"
|
314
|
+
end
|
290
315
|
else
|
291
|
-
"
|
316
|
+
# when it's not a persistent connection, it sets "Connection: close" always
|
317
|
+
# on the last request of the possible batch (either allowed max requests,
|
318
|
+
# or if smaller, the size of the batch itself)
|
319
|
+
requests_limit = [@max_requests, @requests.size].min
|
320
|
+
if request == @requests[requests_limit - 1]
|
321
|
+
"close"
|
322
|
+
else
|
323
|
+
"keep-alive"
|
324
|
+
end
|
292
325
|
end
|
293
|
-
end
|
294
326
|
|
295
|
-
|
327
|
+
extra_headers["connection"] = connection_value
|
328
|
+
end
|
296
329
|
extra_headers["host"] = request.authority unless request.headers.key?("host")
|
297
330
|
extra_headers
|
298
331
|
end
|
@@ -311,7 +344,7 @@ module HTTPX
|
|
311
344
|
end
|
312
345
|
|
313
346
|
def join_headline(request)
|
314
|
-
"#{request.verb
|
347
|
+
"#{request.verb} #{request.path} HTTP/#{@version.join(".")}"
|
315
348
|
end
|
316
349
|
|
317
350
|
def join_headers(request)
|
@@ -348,23 +381,21 @@ module HTTPX
|
|
348
381
|
end
|
349
382
|
|
350
383
|
def join_headers2(headers)
|
351
|
-
buffer = "".b
|
352
384
|
headers.each do |field, value|
|
353
|
-
buffer
|
385
|
+
buffer = "#{capitalized(field)}: #{value}#{CRLF}"
|
354
386
|
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
355
387
|
@buffer << buffer
|
356
|
-
buffer.clear
|
357
388
|
end
|
358
389
|
end
|
359
390
|
|
360
391
|
UPCASED = {
|
361
392
|
"www-authenticate" => "WWW-Authenticate",
|
362
393
|
"http2-settings" => "HTTP2-Settings",
|
394
|
+
"content-md5" => "Content-MD5",
|
363
395
|
}.freeze
|
364
396
|
|
365
397
|
def capitalized(field)
|
366
398
|
UPCASED[field] || field.split("-").map(&:capitalize).join("-")
|
367
399
|
end
|
368
400
|
end
|
369
|
-
Connection.register "http/1.1", Connection::HTTP1
|
370
401
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
-
require "http/2
|
4
|
+
require "http/2"
|
5
5
|
|
6
6
|
module HTTPX
|
7
7
|
class Connection::HTTP2
|
8
8
|
include Callbacks
|
9
9
|
include Loggable
|
10
10
|
|
11
|
-
MAX_CONCURRENT_REQUESTS =
|
11
|
+
MAX_CONCURRENT_REQUESTS = ::HTTP2::DEFAULT_MAX_CONCURRENT_STREAMS
|
12
12
|
|
13
13
|
class Error < Error
|
14
14
|
def initialize(id, code)
|
@@ -25,7 +25,7 @@ module HTTPX
|
|
25
25
|
attr_reader :streams, :pending
|
26
26
|
|
27
27
|
def initialize(buffer, options)
|
28
|
-
@options =
|
28
|
+
@options = options
|
29
29
|
@settings = @options.http2_settings
|
30
30
|
@pending = []
|
31
31
|
@streams = {}
|
@@ -35,7 +35,7 @@ module HTTPX
|
|
35
35
|
@handshake_completed = false
|
36
36
|
@wait_for_handshake = @settings.key?(:wait_for_handshake) ? @settings.delete(:wait_for_handshake) : true
|
37
37
|
@max_concurrent_requests = @options.max_concurrent_requests || MAX_CONCURRENT_REQUESTS
|
38
|
-
@max_requests = @options.max_requests
|
38
|
+
@max_requests = @options.max_requests
|
39
39
|
init_connection
|
40
40
|
end
|
41
41
|
|
@@ -55,7 +55,7 @@ module HTTPX
|
|
55
55
|
return :w
|
56
56
|
end
|
57
57
|
|
58
|
-
unless
|
58
|
+
unless @connection.state == :connected && @handshake_completed
|
59
59
|
return @buffer.empty? ? :r : :rw
|
60
60
|
end
|
61
61
|
|
@@ -73,8 +73,11 @@ module HTTPX
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def close
|
76
|
-
|
77
|
-
|
76
|
+
unless @connection.state == :closed
|
77
|
+
@connection.goaway
|
78
|
+
emit(:timeout, @options.timeout[:close_handshake_timeout])
|
79
|
+
end
|
80
|
+
emit(:close, true)
|
78
81
|
end
|
79
82
|
|
80
83
|
def empty?
|
@@ -82,9 +85,7 @@ module HTTPX
|
|
82
85
|
end
|
83
86
|
|
84
87
|
def exhausted?
|
85
|
-
|
86
|
-
|
87
|
-
@connection.active_stream_count >= @max_requests
|
88
|
+
!@max_requests.positive?
|
88
89
|
end
|
89
90
|
|
90
91
|
def <<(data)
|
@@ -92,13 +93,9 @@ module HTTPX
|
|
92
93
|
end
|
93
94
|
|
94
95
|
def can_buffer_more_requests?
|
95
|
-
|
96
|
+
(@handshake_completed || !@wait_for_handshake) &&
|
96
97
|
@streams.size < @max_concurrent_requests &&
|
97
|
-
|
98
|
-
else
|
99
|
-
!@wait_for_handshake &&
|
100
|
-
@streams.size < @max_concurrent_requests
|
101
|
-
end
|
98
|
+
@streams.size < @max_requests
|
102
99
|
end
|
103
100
|
|
104
101
|
def send(request)
|
@@ -114,9 +111,8 @@ module HTTPX
|
|
114
111
|
end
|
115
112
|
handle(request, stream)
|
116
113
|
true
|
117
|
-
rescue
|
114
|
+
rescue ::HTTP2::Error::StreamLimitExceeded
|
118
115
|
@pending.unshift(request)
|
119
|
-
emit(:exhausted)
|
120
116
|
end
|
121
117
|
|
122
118
|
def consume
|
@@ -127,7 +123,7 @@ module HTTPX
|
|
127
123
|
end
|
128
124
|
end
|
129
125
|
|
130
|
-
def handle_error(ex)
|
126
|
+
def handle_error(ex, request = nil)
|
131
127
|
if ex.instance_of?(TimeoutError) && !@handshake_completed && @connection.state != :closed
|
132
128
|
@connection.goaway(:settings_timeout, "closing due to settings timeout")
|
133
129
|
emit(:close_handshake)
|
@@ -135,11 +131,15 @@ module HTTPX
|
|
135
131
|
settings_ex.set_backtrace(ex.backtrace)
|
136
132
|
ex = settings_ex
|
137
133
|
end
|
138
|
-
@streams.each_key do |
|
139
|
-
|
134
|
+
@streams.each_key do |req|
|
135
|
+
next if request && request == req
|
136
|
+
|
137
|
+
emit(:error, req, ex)
|
140
138
|
end
|
141
|
-
@pending.each do |
|
142
|
-
|
139
|
+
@pending.each do |req|
|
140
|
+
next if request && request == req
|
141
|
+
|
142
|
+
emit(:error, req, ex)
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
@@ -154,6 +154,7 @@ module HTTPX
|
|
154
154
|
|
155
155
|
def send_pending
|
156
156
|
while (request = @pending.shift)
|
157
|
+
# TODO: this request should go back to top of stack
|
157
158
|
break unless send(request)
|
158
159
|
end
|
159
160
|
end
|
@@ -171,8 +172,7 @@ module HTTPX
|
|
171
172
|
end
|
172
173
|
|
173
174
|
def init_connection
|
174
|
-
@connection =
|
175
|
-
@connection.max_streams = @max_requests if @connection.respond_to?(:max_streams=) && @max_requests.positive?
|
175
|
+
@connection = ::HTTP2::Client.new(@settings)
|
176
176
|
@connection.on(:frame, &method(:on_frame))
|
177
177
|
@connection.on(:frame_sent, &method(:on_frame_sent))
|
178
178
|
@connection.on(:frame_received, &method(:on_frame_received))
|
@@ -208,7 +208,7 @@ module HTTPX
|
|
208
208
|
def set_protocol_headers(request)
|
209
209
|
{
|
210
210
|
":scheme" => request.scheme,
|
211
|
-
":method" => request.verb
|
211
|
+
":method" => request.verb,
|
212
212
|
":path" => request.path,
|
213
213
|
":authority" => request.authority,
|
214
214
|
}
|
@@ -313,11 +313,12 @@ module HTTPX
|
|
313
313
|
if error
|
314
314
|
ex = Error.new(stream.id, error)
|
315
315
|
ex.set_backtrace(caller)
|
316
|
-
response = ErrorResponse.new(request, ex
|
316
|
+
response = ErrorResponse.new(request, ex)
|
317
|
+
request.response = response
|
317
318
|
emit(:response, request, response)
|
318
319
|
else
|
319
320
|
response = request.response
|
320
|
-
if response && response.status == 421
|
321
|
+
if response && response.is_a?(Response) && response.status == 421
|
321
322
|
ex = MisdirectedRequestError.new(response)
|
322
323
|
ex.set_backtrace(caller)
|
323
324
|
emit(:error, request, ex)
|
@@ -339,14 +340,7 @@ module HTTPX
|
|
339
340
|
def on_settings(*)
|
340
341
|
@handshake_completed = true
|
341
342
|
emit(:current_timeout)
|
342
|
-
|
343
|
-
if @max_requests.zero?
|
344
|
-
@max_requests = @connection.remote_settings[:settings_max_concurrent_streams]
|
345
|
-
|
346
|
-
@connection.max_streams = @max_requests if @connection.respond_to?(:max_streams=) && @max_requests.positive?
|
347
|
-
end
|
348
|
-
|
349
|
-
@max_concurrent_requests = [@max_concurrent_requests, @max_requests].min
|
343
|
+
@max_concurrent_requests = [@max_concurrent_requests, @connection.remote_settings[:settings_max_concurrent_streams]].min
|
350
344
|
send_pending
|
351
345
|
end
|
352
346
|
|
@@ -365,7 +359,7 @@ module HTTPX
|
|
365
359
|
ex.set_backtrace(caller)
|
366
360
|
handle_error(ex)
|
367
361
|
end
|
368
|
-
return unless is_connection_closed && @streams.
|
362
|
+
return unless is_connection_closed && @streams.empty?
|
369
363
|
|
370
364
|
emit(:close, is_connection_closed)
|
371
365
|
end
|
@@ -412,5 +406,4 @@ module HTTPX
|
|
412
406
|
end
|
413
407
|
end
|
414
408
|
end
|
415
|
-
Connection.register "h2", Connection::HTTP2
|
416
409
|
end
|