httpx 0.6.7 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx/adapters/faraday.rb +2 -2
- data/lib/httpx/altsvc.rb +18 -2
- data/lib/httpx/chainable.rb +27 -9
- data/lib/httpx/connection.rb +215 -65
- data/lib/httpx/connection/http1.rb +54 -18
- data/lib/httpx/connection/http2.rb +100 -37
- data/lib/httpx/extensions.rb +2 -2
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +11 -3
- data/lib/httpx/io/tcp.rb +12 -2
- data/lib/httpx/loggable.rb +6 -6
- data/lib/httpx/options.rb +43 -28
- data/lib/httpx/plugins/authentication.rb +1 -1
- data/lib/httpx/plugins/compression.rb +28 -8
- data/lib/httpx/plugins/compression/gzip.rb +22 -12
- data/lib/httpx/plugins/cookies.rb +12 -8
- data/lib/httpx/plugins/digest_authentication.rb +2 -0
- data/lib/httpx/plugins/expect.rb +79 -0
- data/lib/httpx/plugins/follow_redirects.rb +1 -2
- data/lib/httpx/plugins/h2c.rb +0 -1
- data/lib/httpx/plugins/proxy.rb +23 -20
- data/lib/httpx/plugins/proxy/http.rb +9 -6
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +5 -1
- data/lib/httpx/plugins/proxy/ssh.rb +0 -4
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/retries.rb +32 -29
- data/lib/httpx/pool.rb +15 -10
- data/lib/httpx/registry.rb +2 -1
- data/lib/httpx/request.rb +8 -6
- data/lib/httpx/resolver.rb +7 -8
- data/lib/httpx/resolver/https.rb +15 -3
- data/lib/httpx/resolver/native.rb +22 -32
- data/lib/httpx/resolver/options.rb +2 -2
- data/lib/httpx/resolver/resolver_mixin.rb +1 -1
- data/lib/httpx/response.rb +17 -3
- data/lib/httpx/selector.rb +96 -95
- data/lib/httpx/session.rb +33 -34
- data/lib/httpx/timeout.rb +7 -1
- data/lib/httpx/version.rb +1 -1
- metadata +77 -20
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin makes all HTTP/1.1 requests with a body send the "Expect: 100-continue".
|
7
|
+
#
|
8
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/Expect#expect
|
9
|
+
#
|
10
|
+
module Expect
|
11
|
+
EXPECT_TIMEOUT = 2
|
12
|
+
|
13
|
+
def self.extra_options(options)
|
14
|
+
Class.new(options.class) do
|
15
|
+
def_option(:expect_timeout) do |seconds|
|
16
|
+
seconds = Integer(seconds)
|
17
|
+
raise Error, ":expect_timeout must be positive" unless seconds.positive?
|
18
|
+
|
19
|
+
seconds
|
20
|
+
end
|
21
|
+
|
22
|
+
def_option(:expect_threshold_size) do |bytes|
|
23
|
+
bytes = Integer(bytes)
|
24
|
+
raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
|
25
|
+
|
26
|
+
bytes
|
27
|
+
end
|
28
|
+
end.new(options).merge(expect_timeout: EXPECT_TIMEOUT)
|
29
|
+
end
|
30
|
+
|
31
|
+
module RequestBodyMethods
|
32
|
+
def initialize(*, options)
|
33
|
+
super
|
34
|
+
return if @body.nil?
|
35
|
+
|
36
|
+
if (threshold = options.expect_threshold_size)
|
37
|
+
unless unbounded_body?
|
38
|
+
return if @body.bytesize < threshold
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@headers["expect"] = "100-continue"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module ConnectionMethods
|
47
|
+
def send(request)
|
48
|
+
request.once(:expects) do
|
49
|
+
@timers.after(@options.expect_timeout) do
|
50
|
+
if request.state == :expects && !request.expects?
|
51
|
+
request.headers.delete("expect")
|
52
|
+
handle(request)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module InstanceMethods
|
61
|
+
def fetch_response(request, connections, options)
|
62
|
+
response = @responses.delete(request)
|
63
|
+
return unless response
|
64
|
+
|
65
|
+
if response.status == 417 && request.headers.key?("expect")
|
66
|
+
request.headers.delete("expect")
|
67
|
+
request.transition(:idle)
|
68
|
+
connection = find_connection(request, connections, options)
|
69
|
+
connection.send(request)
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
response
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
register_plugin :expect, Expect
|
78
|
+
end
|
79
|
+
end
|
@@ -21,7 +21,7 @@ module HTTPX
|
|
21
21
|
Class.new(options.class) do
|
22
22
|
def_option(:max_redirects) do |num|
|
23
23
|
num = Integer(num)
|
24
|
-
raise Error, ":max_redirects must be positive"
|
24
|
+
raise Error, ":max_redirects must be positive" if num.negative?
|
25
25
|
|
26
26
|
num
|
27
27
|
end
|
@@ -61,7 +61,6 @@ module HTTPX
|
|
61
61
|
|
62
62
|
connection = find_connection(retry_request, connections, options)
|
63
63
|
connection.send(retry_request)
|
64
|
-
set_request_timeout(connection, retry_request, options)
|
65
64
|
nil
|
66
65
|
end
|
67
66
|
|
data/lib/httpx/plugins/h2c.rb
CHANGED
@@ -42,7 +42,6 @@ module HTTPX
|
|
42
42
|
connection = find_connection(request, connections, options)
|
43
43
|
connections << connection unless connections.include?(connection)
|
44
44
|
connection.upgrade(request, response)
|
45
|
-
set_request_timeout(connection, request, options)
|
46
45
|
end
|
47
46
|
response
|
48
47
|
end
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -68,17 +68,17 @@ module HTTPX
|
|
68
68
|
def extra_options(options)
|
69
69
|
Class.new(options.class) do
|
70
70
|
def_option(:proxy) do |pr|
|
71
|
-
|
71
|
+
if pr.is_a?(Parameters)
|
72
|
+
pr
|
73
|
+
else
|
74
|
+
Hash[pr]
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end.new(options)
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
81
|
module InstanceMethods
|
78
|
-
def with_proxy(*args)
|
79
|
-
branch(default_options.with_proxy(*args))
|
80
|
-
end
|
81
|
-
|
82
82
|
private
|
83
83
|
|
84
84
|
def proxy_uris(uri, options)
|
@@ -104,7 +104,7 @@ module HTTPX
|
|
104
104
|
connection = pool.find_connection(uri, proxy_options) || build_connection(uri, proxy_options)
|
105
105
|
unless connections.nil? || connections.include?(connection)
|
106
106
|
connections << connection
|
107
|
-
set_connection_callbacks(connection, options)
|
107
|
+
set_connection_callbacks(connection, connections, options)
|
108
108
|
end
|
109
109
|
connection
|
110
110
|
end
|
@@ -129,13 +129,12 @@ module HTTPX
|
|
129
129
|
connection = find_connection(request, connections, options)
|
130
130
|
connections << connection unless connections.include?(connection)
|
131
131
|
connection.send(request)
|
132
|
-
set_request_timeout(connection, request, options)
|
133
132
|
return
|
134
133
|
end
|
135
134
|
response
|
136
135
|
end
|
137
136
|
|
138
|
-
def build_altsvc_connection(_, _, _, _, options)
|
137
|
+
def build_altsvc_connection(_, _, _, _, _, options)
|
139
138
|
return if options.proxy
|
140
139
|
|
141
140
|
super
|
@@ -180,20 +179,9 @@ module HTTPX
|
|
180
179
|
super || @state == :connecting || @state == :connected
|
181
180
|
end
|
182
181
|
|
183
|
-
def to_io
|
184
|
-
return super unless @options.proxy
|
185
|
-
|
186
|
-
case @state
|
187
|
-
when :idle
|
188
|
-
transition(:connecting)
|
189
|
-
when :connected
|
190
|
-
transition(:open)
|
191
|
-
end
|
192
|
-
@io.to_io
|
193
|
-
end
|
194
|
-
|
195
182
|
def call
|
196
183
|
super
|
184
|
+
|
197
185
|
return unless @options.proxy
|
198
186
|
|
199
187
|
case @state
|
@@ -211,11 +199,26 @@ module HTTPX
|
|
211
199
|
emit(:close)
|
212
200
|
end
|
213
201
|
|
202
|
+
private
|
203
|
+
|
204
|
+
def connect
|
205
|
+
return super unless @options.proxy
|
206
|
+
|
207
|
+
case @state
|
208
|
+
when :idle
|
209
|
+
transition(:connecting)
|
210
|
+
when :connected
|
211
|
+
transition(:open)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
214
215
|
def transition(nextstate)
|
215
216
|
return super unless @options.proxy
|
216
217
|
|
217
218
|
case nextstate
|
218
219
|
when :closing
|
220
|
+
# this is a hack so that we can use the super method
|
221
|
+
# and it'll thing that the current state is open
|
219
222
|
@state = :open if @state == :connecting
|
220
223
|
end
|
221
224
|
super
|
@@ -7,6 +7,10 @@ module HTTPX
|
|
7
7
|
module Proxy
|
8
8
|
module HTTP
|
9
9
|
module ConnectionMethods
|
10
|
+
def connecting?
|
11
|
+
super || @state == :connecting || @state == :connected
|
12
|
+
end
|
13
|
+
|
10
14
|
private
|
11
15
|
|
12
16
|
def transition(nextstate)
|
@@ -34,7 +38,6 @@ module HTTPX
|
|
34
38
|
when :idle
|
35
39
|
@parser = ProxyParser.new(@write_buffer, @options)
|
36
40
|
set_parser_callbacks(@parser)
|
37
|
-
@parser.on(:close) { transition(:closing) }
|
38
41
|
end
|
39
42
|
end
|
40
43
|
super
|
@@ -48,7 +51,7 @@ module HTTPX
|
|
48
51
|
#
|
49
52
|
if req.uri.scheme == "https"
|
50
53
|
connect_request = ConnectRequest.new(req.uri, @options)
|
51
|
-
|
54
|
+
@inflight += 1
|
52
55
|
parser.send(connect_request)
|
53
56
|
else
|
54
57
|
transition(:connected)
|
@@ -56,6 +59,7 @@ module HTTPX
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def __http_on_connect(_, response)
|
62
|
+
@inflight -= 1
|
59
63
|
if response.status == 200
|
60
64
|
req = @pending.first
|
61
65
|
request_uri = req.uri
|
@@ -67,6 +71,7 @@ module HTTPX
|
|
67
71
|
while (req = pending.shift)
|
68
72
|
req.emit(:response, response)
|
69
73
|
end
|
74
|
+
reset
|
70
75
|
end
|
71
76
|
end
|
72
77
|
end
|
@@ -91,15 +96,13 @@ module HTTPX
|
|
91
96
|
def headline_uri(request)
|
92
97
|
return super unless request.verb == :connect
|
93
98
|
|
94
|
-
|
95
|
-
tunnel = "#{uri.hostname}:#{uri.port}"
|
99
|
+
tunnel = request.path
|
96
100
|
log { "establishing HTTP proxy tunnel to #{tunnel}" }
|
97
101
|
tunnel
|
98
102
|
end
|
99
103
|
|
100
104
|
def empty?
|
101
|
-
@requests.reject { |r| r.verb == :connect }.empty? ||
|
102
|
-
@requests.all? { |request| !request.response.nil? }
|
105
|
+
@requests.reject { |r| r.verb == :connect }.empty? || @requests.all? { |request| !request.response.nil? }
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
@@ -39,7 +39,7 @@ module HTTPX
|
|
39
39
|
|
40
40
|
@parser = nil
|
41
41
|
end
|
42
|
-
log(level: 1
|
42
|
+
log(level: 1) { "SOCKS4: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
43
43
|
super
|
44
44
|
end
|
45
45
|
|
@@ -31,6 +31,10 @@ module HTTPX
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
def connecting?
|
35
|
+
super || @state == :authenticating || @state == :negotiating
|
36
|
+
end
|
37
|
+
|
34
38
|
private
|
35
39
|
|
36
40
|
def transition(nextstate)
|
@@ -60,7 +64,7 @@ module HTTPX
|
|
60
64
|
|
61
65
|
@parser = nil
|
62
66
|
end
|
63
|
-
log(level: 1
|
67
|
+
log(level: 1) { "SOCKS5: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
64
68
|
super
|
65
69
|
end
|
66
70
|
|
@@ -43,9 +43,9 @@ module HTTPX
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def __on_promise_request(parser, stream, h)
|
46
|
-
log(level: 1
|
46
|
+
log(level: 1) do
|
47
47
|
# :nocov:
|
48
|
-
h.map { |k, v| "-> PROMISE HEADER: #{k}: #{v}" }.join("\n")
|
48
|
+
h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
|
49
49
|
# :nocov:
|
50
50
|
end
|
51
51
|
headers = @options.headers_class.new(h)
|
@@ -28,10 +28,10 @@ module HTTPX
|
|
28
28
|
# number of seconds after which one can retry the request
|
29
29
|
def_option(:retry_after) do |num|
|
30
30
|
# return early if callable
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
unless num.respond_to?(:call)
|
32
|
+
num = Integer(num)
|
33
|
+
raise Error, ":retry_after must be positive" unless num.positive?
|
34
|
+
end
|
35
35
|
|
36
36
|
num
|
37
37
|
end
|
@@ -46,7 +46,7 @@ module HTTPX
|
|
46
46
|
def_option(:retry_change_requests)
|
47
47
|
|
48
48
|
def_option(:retry_on) do |callback|
|
49
|
-
raise ":retry_on must be called with the response" unless callback.respond_to?(:call)
|
49
|
+
raise ":retry_on must be called with the response" unless callback.respond_to?(:call)
|
50
50
|
|
51
51
|
callback
|
52
52
|
end
|
@@ -63,18 +63,38 @@ module HTTPX
|
|
63
63
|
def fetch_response(request, connections, options)
|
64
64
|
response = super
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
if response.is_a?(ErrorResponse) &&
|
66
|
+
if response &&
|
69
67
|
request.retries.positive? &&
|
70
68
|
__repeatable_request?(request, options) &&
|
71
|
-
|
72
|
-
|
69
|
+
(
|
70
|
+
# rubocop:disable Style/MultilineTernaryOperator
|
71
|
+
options.retry_on ?
|
72
|
+
options.retry_on.call(response) :
|
73
|
+
(
|
74
|
+
response.is_a?(ErrorResponse) && __retryable_error?(response.error)
|
75
|
+
)
|
76
|
+
# rubocop:enable Style/MultilineTernaryOperator
|
77
|
+
)
|
78
|
+
|
73
79
|
request.retries -= 1
|
74
80
|
log { "failed to get response, #{request.retries} tries to go..." }
|
75
81
|
request.transition(:idle)
|
76
|
-
|
77
|
-
|
82
|
+
|
83
|
+
retry_after = options.retry_after
|
84
|
+
if retry_after
|
85
|
+
retry_after = retry_after.call(request) if retry_after.respond_to?(:call)
|
86
|
+
|
87
|
+
log { "retrying after #{retry_after} secs..." }
|
88
|
+
pool.after(retry_after) do
|
89
|
+
log { "retrying!!" }
|
90
|
+
connection = find_connection(request, connections, options)
|
91
|
+
connection.send(request)
|
92
|
+
end
|
93
|
+
else
|
94
|
+
connection = find_connection(request, connections, options)
|
95
|
+
connection.send(request)
|
96
|
+
end
|
97
|
+
|
78
98
|
return
|
79
99
|
end
|
80
100
|
response
|
@@ -87,23 +107,6 @@ module HTTPX
|
|
87
107
|
def __retryable_error?(ex)
|
88
108
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) }
|
89
109
|
end
|
90
|
-
|
91
|
-
def __retry_request(connection, request, options)
|
92
|
-
retry_after = options.retry_after
|
93
|
-
unless retry_after
|
94
|
-
connection.send(request)
|
95
|
-
set_request_timeout(connection, request, options)
|
96
|
-
return
|
97
|
-
end
|
98
|
-
|
99
|
-
retry_after = retry_after.call(request) if retry_after.respond_to?(:call)
|
100
|
-
log { "retrying after #{retry_after} secs..." }
|
101
|
-
pool.after(retry_after) do
|
102
|
-
log { "retrying!!" }
|
103
|
-
connection.send(request)
|
104
|
-
set_request_timeout(connection, request, options)
|
105
|
-
end
|
106
|
-
end
|
107
110
|
end
|
108
111
|
|
109
112
|
module RequestMethods
|
data/lib/httpx/pool.rb
CHANGED
@@ -14,7 +14,7 @@ module HTTPX
|
|
14
14
|
|
15
15
|
def initialize
|
16
16
|
@resolvers = {}
|
17
|
-
@
|
17
|
+
@_resolver_ios = {}
|
18
18
|
@timers = Timers::Group.new
|
19
19
|
@selector = Selector.new
|
20
20
|
@connections = []
|
@@ -27,12 +27,19 @@ module HTTPX
|
|
27
27
|
|
28
28
|
def next_tick
|
29
29
|
catch(:jump_tick) do
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
timeout = [next_timeout, @timers.wait_interval].compact.min
|
31
|
+
if timeout && timeout.negative?
|
32
|
+
@timers.fire
|
33
|
+
throw(:jump_tick)
|
33
34
|
end
|
35
|
+
|
36
|
+
@selector.select(timeout, &:call)
|
37
|
+
|
34
38
|
@timers.fire
|
35
39
|
end
|
40
|
+
rescue Interrupt
|
41
|
+
@connections.each(&:reset)
|
42
|
+
raise
|
36
43
|
rescue StandardError => e
|
37
44
|
@connections.each do |connection|
|
38
45
|
connection.emit(:error, e)
|
@@ -51,6 +58,7 @@ module HTTPX
|
|
51
58
|
|
52
59
|
def init_connection(connection, _options)
|
53
60
|
resolve_connection(connection)
|
61
|
+
connection.timers = @timers
|
54
62
|
connection.on(:open) do
|
55
63
|
@connected_connections += 1
|
56
64
|
end
|
@@ -79,7 +87,7 @@ module HTTPX
|
|
79
87
|
resolver << connection
|
80
88
|
return if resolver.empty?
|
81
89
|
|
82
|
-
@
|
90
|
+
@_resolver_ios[resolver] ||= @selector.register(resolver)
|
83
91
|
end
|
84
92
|
|
85
93
|
def on_resolver_connection(connection)
|
@@ -111,8 +119,7 @@ module HTTPX
|
|
111
119
|
@resolvers.delete(resolver_type)
|
112
120
|
|
113
121
|
@selector.deregister(resolver)
|
114
|
-
|
115
|
-
monitor.close if monitor
|
122
|
+
@_resolver_ios.delete(resolver)
|
116
123
|
resolver.close unless resolver.closed?
|
117
124
|
end
|
118
125
|
|
@@ -121,10 +128,8 @@ module HTTPX
|
|
121
128
|
# if open, an IO was passed upstream, therefore
|
122
129
|
# consider it connected already.
|
123
130
|
@connected_connections += 1
|
124
|
-
@selector.register(connection, :rw)
|
125
|
-
else
|
126
|
-
@selector.register(connection, :w)
|
127
131
|
end
|
132
|
+
@selector.register(connection)
|
128
133
|
connection.on(:close) do
|
129
134
|
unregister_connection(connection)
|
130
135
|
end
|