httpx 0.20.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|