httpx 0.6.7 → 0.7.0
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/lib/httpx/adapters/faraday.rb +2 -2
- data/lib/httpx/chainable.rb +20 -3
- data/lib/httpx/connection.rb +42 -3
- data/lib/httpx/connection/http1.rb +11 -6
- data/lib/httpx/connection/http2.rb +26 -6
- data/lib/httpx/options.rb +28 -18
- data/lib/httpx/plugins/authentication.rb +1 -1
- data/lib/httpx/plugins/cookies.rb +12 -8
- data/lib/httpx/plugins/expect.rb +66 -0
- data/lib/httpx/plugins/follow_redirects.rb +1 -2
- data/lib/httpx/plugins/h2c.rb +0 -1
- data/lib/httpx/plugins/proxy.rb +7 -8
- data/lib/httpx/plugins/proxy/http.rb +2 -4
- data/lib/httpx/plugins/proxy/ssh.rb +0 -4
- data/lib/httpx/plugins/retries.rb +21 -24
- data/lib/httpx/pool.rb +8 -1
- data/lib/httpx/request.rb +1 -6
- data/lib/httpx/response.rb +2 -0
- data/lib/httpx/session.rb +18 -21
- data/lib/httpx/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0f44b6fc0f644a3f70b58a7051e963c5f74f9a5cb8ce6ccde0f6d2aa8909b14
|
4
|
+
data.tar.gz: d341c978e49312b3abe26c24845b672be5055fd4a4a2cab9e2aa9d1acf79aa63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6a9e8caa8f2424c8628153535deda5f1d8623253801ae329884ee51f10c5f0e5237d4c6e2548faa40d14c0e0e3dd982c050e6f67521a8b2a56e39a1bbab40aa
|
7
|
+
data.tar.gz: db1b34ff9023343b4f9580b1a72c54765cf7d446f59f96bea22ec9230825ce42cd42f0ac26ec168cd3b721e75e6cf5c2964a05afea1895a778d8f27932f61d65
|
@@ -153,7 +153,7 @@ module Faraday
|
|
153
153
|
proxy_options = { uri: env.request.proxy }
|
154
154
|
|
155
155
|
session = @session.with(options_from_env(env))
|
156
|
-
session = session.plugin(:proxy).
|
156
|
+
session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
|
157
157
|
|
158
158
|
responses = session.request(requests)
|
159
159
|
Array(responses).each_with_index do |response, index|
|
@@ -192,7 +192,7 @@ module Faraday
|
|
192
192
|
meth, uri, request_options = build_request(env)
|
193
193
|
|
194
194
|
session = @session.with(options_from_env(env))
|
195
|
-
session = session.plugin(:proxy).
|
195
|
+
session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
|
196
196
|
response = session.__send__(meth, uri, **request_options)
|
197
197
|
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
198
198
|
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
data/lib/httpx/chainable.rb
CHANGED
@@ -12,16 +12,20 @@ module HTTPX
|
|
12
12
|
branch(default_options).request(verb, uri, **options)
|
13
13
|
end
|
14
14
|
|
15
|
+
# :nocov:
|
15
16
|
def timeout(**args)
|
16
|
-
|
17
|
+
warn ":#{__method__} is deprecated, use :with_timeout instead"
|
18
|
+
branch(default_options.with(timeout: args))
|
17
19
|
end
|
18
20
|
|
19
21
|
def headers(headers)
|
20
|
-
|
22
|
+
warn ":#{__method__} is deprecated, use :with_headers instead"
|
23
|
+
branch(default_options.with(headers: headers))
|
21
24
|
end
|
25
|
+
# :nocov:
|
22
26
|
|
23
27
|
def accept(type)
|
24
|
-
headers
|
28
|
+
with(headers: { "accept" => String(type) })
|
25
29
|
end
|
26
30
|
|
27
31
|
def wrap(&blk)
|
@@ -59,5 +63,18 @@ module HTTPX
|
|
59
63
|
|
60
64
|
Session.new(options, &blk)
|
61
65
|
end
|
66
|
+
|
67
|
+
def method_missing(meth, *args, **options)
|
68
|
+
if meth =~ /\Awith_(.+)/
|
69
|
+
option = Regexp.last_match(1).to_sym
|
70
|
+
with(option => (args.first || options))
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def respond_to_missing?(meth, *args)
|
77
|
+
default_options.respond_to?(meth, *args) || super
|
78
|
+
end
|
62
79
|
end
|
63
80
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -46,6 +46,8 @@ module HTTPX
|
|
46
46
|
|
47
47
|
attr_reader :origin, :state, :pending, :options
|
48
48
|
|
49
|
+
attr_writer :timers
|
50
|
+
|
49
51
|
def initialize(type, uri, options)
|
50
52
|
@type = type
|
51
53
|
@origins = [uri.origin]
|
@@ -80,6 +82,8 @@ module HTTPX
|
|
80
82
|
def match?(uri, options)
|
81
83
|
return false if @state == :closing || @state == :closed
|
82
84
|
|
85
|
+
return false if exhausted?
|
86
|
+
|
83
87
|
(
|
84
88
|
(
|
85
89
|
@origins.include?(uri.origin) &&
|
@@ -95,6 +99,8 @@ module HTTPX
|
|
95
99
|
def mergeable?(connection)
|
96
100
|
return false if @state == :closing || @state == :closed || !@io
|
97
101
|
|
102
|
+
return false if exhausted?
|
103
|
+
|
98
104
|
!(@io.addresses & connection.addresses).empty? && @options == connection.options
|
99
105
|
end
|
100
106
|
|
@@ -110,10 +116,13 @@ module HTTPX
|
|
110
116
|
end
|
111
117
|
end
|
112
118
|
|
119
|
+
def create_idle
|
120
|
+
self.class.new(@type, @origin, @options)
|
121
|
+
end
|
122
|
+
|
113
123
|
def merge(connection)
|
114
124
|
@origins += connection.instance_variable_get(:@origins)
|
115
|
-
|
116
|
-
pending.each do |req|
|
125
|
+
connection.purge_pending do |req|
|
117
126
|
send(req)
|
118
127
|
end
|
119
128
|
end
|
@@ -130,7 +139,10 @@ module HTTPX
|
|
130
139
|
end
|
131
140
|
|
132
141
|
def purge_pending
|
133
|
-
[
|
142
|
+
pendings = []
|
143
|
+
pendings << @parser.pending if @parser
|
144
|
+
pendings << @pending
|
145
|
+
pendings.each do |pending|
|
134
146
|
pending.reject! do |request|
|
135
147
|
yield request
|
136
148
|
end
|
@@ -213,6 +225,10 @@ module HTTPX
|
|
213
225
|
|
214
226
|
private
|
215
227
|
|
228
|
+
def exhausted?
|
229
|
+
@parser && parser.exhausted?
|
230
|
+
end
|
231
|
+
|
216
232
|
def consume
|
217
233
|
catch(:called) do
|
218
234
|
dread
|
@@ -285,6 +301,9 @@ module HTTPX
|
|
285
301
|
parser.on(:promise) do |request, stream|
|
286
302
|
request.emit(:promise, parser, stream)
|
287
303
|
end
|
304
|
+
parser.on(:exhausted) do
|
305
|
+
emit(:exhausted)
|
306
|
+
end
|
288
307
|
parser.on(:origin) do |origin|
|
289
308
|
@origins << origin
|
290
309
|
end
|
@@ -319,6 +338,8 @@ module HTTPX
|
|
319
338
|
when :open
|
320
339
|
return if @state == :closed
|
321
340
|
|
341
|
+
total_timeout
|
342
|
+
|
322
343
|
@io.connect
|
323
344
|
return unless @io.connected?
|
324
345
|
|
@@ -330,6 +351,11 @@ module HTTPX
|
|
330
351
|
return unless @state == :closing
|
331
352
|
return unless @write_buffer.empty?
|
332
353
|
|
354
|
+
if @total_timeout
|
355
|
+
@total_timeout.cancel
|
356
|
+
remove_instance_variable(:@total_timeout)
|
357
|
+
end
|
358
|
+
|
333
359
|
@io.close
|
334
360
|
@read_buffer.clear
|
335
361
|
remove_instance_variable(:@timeout) if defined?(@timeout)
|
@@ -373,5 +399,18 @@ module HTTPX
|
|
373
399
|
request.emit(:response, ErrorResponse.new(request, error, @options))
|
374
400
|
end
|
375
401
|
end
|
402
|
+
|
403
|
+
def total_timeout
|
404
|
+
total = @options.timeout.total_timeout
|
405
|
+
|
406
|
+
return unless total
|
407
|
+
|
408
|
+
@total_timeout ||= @timers.after(total) do
|
409
|
+
ex = TotalTimeoutError.new(total, "Timed out after #{total} seconds")
|
410
|
+
ex.set_backtrace(caller)
|
411
|
+
@parser.close if @parser
|
412
|
+
on_error(ex)
|
413
|
+
end
|
414
|
+
end
|
376
415
|
end
|
377
416
|
end
|
@@ -7,14 +7,15 @@ module HTTPX
|
|
7
7
|
include Callbacks
|
8
8
|
include Loggable
|
9
9
|
|
10
|
+
MAX_REQUESTS = 100
|
10
11
|
CRLF = "\r\n"
|
11
12
|
|
12
13
|
attr_reader :pending
|
13
14
|
|
14
15
|
def initialize(buffer, options)
|
15
16
|
@options = Options.new(options)
|
16
|
-
@max_concurrent_requests = @options.max_concurrent_requests
|
17
|
-
@max_requests =
|
17
|
+
@max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
|
18
|
+
@max_requests = @options.max_requests || MAX_REQUESTS
|
18
19
|
@parser = Parser::HTTP1.new(self)
|
19
20
|
@buffer = buffer
|
20
21
|
@version = [1, 1]
|
@@ -23,6 +24,7 @@ module HTTPX
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def reset
|
27
|
+
@max_requests = @options.max_requests || MAX_REQUESTS
|
26
28
|
@parser.reset!
|
27
29
|
end
|
28
30
|
|
@@ -31,6 +33,10 @@ module HTTPX
|
|
31
33
|
emit(:close)
|
32
34
|
end
|
33
35
|
|
36
|
+
def exhausted?
|
37
|
+
!@max_requests.positive?
|
38
|
+
end
|
39
|
+
|
34
40
|
def empty?
|
35
41
|
# this means that for every request there's an available
|
36
42
|
# partial response, so there are no in-flight requests waiting.
|
@@ -42,15 +48,16 @@ module HTTPX
|
|
42
48
|
end
|
43
49
|
|
44
50
|
def send(request)
|
45
|
-
|
46
|
-
@requests.size >= @max_concurrent_requests
|
51
|
+
unless @max_requests.positive?
|
47
52
|
@pending << request
|
48
53
|
return
|
49
54
|
end
|
55
|
+
|
50
56
|
unless @requests.include?(request)
|
51
57
|
@requests << request
|
52
58
|
@pipelining = true if @requests.size > 1
|
53
59
|
end
|
60
|
+
|
54
61
|
handle(request)
|
55
62
|
end
|
56
63
|
|
@@ -163,13 +170,11 @@ module HTTPX
|
|
163
170
|
emit(:timeout, keep_alive_timeout)
|
164
171
|
end
|
165
172
|
when /close/i
|
166
|
-
@max_requests = Float::INFINITY
|
167
173
|
disable
|
168
174
|
when nil
|
169
175
|
# In HTTP/1.1, it's keep alive by default
|
170
176
|
return if response.version == "1.1"
|
171
177
|
|
172
|
-
@max_requests = Float::INFINITY
|
173
178
|
disable
|
174
179
|
end
|
175
180
|
end
|
@@ -8,6 +8,8 @@ module HTTPX
|
|
8
8
|
include Callbacks
|
9
9
|
include Loggable
|
10
10
|
|
11
|
+
MAX_CONCURRENT_REQUESTS = HTTP2Next::DEFAULT_MAX_CONCURRENT_STREAMS
|
12
|
+
|
11
13
|
Error = Class.new(Error) do
|
12
14
|
def initialize(id, code)
|
13
15
|
super("stream #{id} closed with error: #{code}")
|
@@ -18,7 +20,8 @@ module HTTPX
|
|
18
20
|
|
19
21
|
def initialize(buffer, options)
|
20
22
|
@options = Options.new(options)
|
21
|
-
@max_concurrent_requests = @options.max_concurrent_requests
|
23
|
+
@max_concurrent_requests = @options.max_concurrent_requests || MAX_CONCURRENT_REQUESTS
|
24
|
+
@max_requests = @options.max_requests || 0
|
22
25
|
@pending = []
|
23
26
|
@streams = {}
|
24
27
|
@drains = {}
|
@@ -28,20 +31,25 @@ module HTTPX
|
|
28
31
|
end
|
29
32
|
|
30
33
|
def close
|
31
|
-
@connection.goaway
|
34
|
+
@connection.goaway unless @connection.state == :closed
|
32
35
|
end
|
33
36
|
|
34
37
|
def empty?
|
35
38
|
@connection.state == :closed || @streams.empty?
|
36
39
|
end
|
37
40
|
|
41
|
+
def exhausted?
|
42
|
+
@connection.active_stream_count >= @max_requests
|
43
|
+
end
|
44
|
+
|
38
45
|
def <<(data)
|
39
46
|
@connection << data
|
40
47
|
end
|
41
48
|
|
42
|
-
def send(request
|
49
|
+
def send(request)
|
43
50
|
if !@handshake_completed ||
|
44
|
-
@streams.size >= @max_concurrent_requests
|
51
|
+
@streams.size >= @max_concurrent_requests ||
|
52
|
+
@streams.size >= @max_requests
|
45
53
|
@pending << request
|
46
54
|
return
|
47
55
|
end
|
@@ -49,9 +57,13 @@ module HTTPX
|
|
49
57
|
stream = @connection.new_stream
|
50
58
|
handle_stream(stream, request)
|
51
59
|
@streams[request] = stream
|
60
|
+
@max_requests -= 1
|
52
61
|
end
|
53
62
|
handle(request, stream)
|
54
63
|
true
|
64
|
+
rescue HTTP2Next::Error::StreamLimitExceeded
|
65
|
+
@pending.unshift(request)
|
66
|
+
emit(:exhausted)
|
55
67
|
end
|
56
68
|
|
57
69
|
def consume
|
@@ -95,6 +107,7 @@ module HTTPX
|
|
95
107
|
|
96
108
|
def init_connection
|
97
109
|
@connection = HTTP2Next::Client.new(@options.http2_settings)
|
110
|
+
@connection.max_streams = @max_requests if @connection.respond_to?(:max_streams=) && @max_requests.positive?
|
98
111
|
@connection.on(:frame, &method(:on_frame))
|
99
112
|
@connection.on(:frame_sent, &method(:on_frame_sent))
|
100
113
|
@connection.on(:frame_received, &method(:on_frame_received))
|
@@ -195,6 +208,11 @@ module HTTPX
|
|
195
208
|
|
196
209
|
@streams.delete(request)
|
197
210
|
send(@pending.shift) unless @pending.empty?
|
211
|
+
return unless @streams.empty? && exhausted?
|
212
|
+
|
213
|
+
close
|
214
|
+
emit(:close)
|
215
|
+
emit(:exhausted) unless @pending.empty?
|
198
216
|
end
|
199
217
|
|
200
218
|
def on_frame(bytes)
|
@@ -203,8 +221,10 @@ module HTTPX
|
|
203
221
|
|
204
222
|
def on_settings(*)
|
205
223
|
@handshake_completed = true
|
206
|
-
|
207
|
-
|
224
|
+
|
225
|
+
@max_requests = [@max_requests, @connection.remote_settings[:settings_max_concurrent_streams]].max
|
226
|
+
|
227
|
+
@max_concurrent_requests = [@max_concurrent_requests, @max_requests].min
|
208
228
|
send_pending
|
209
229
|
end
|
210
230
|
|
data/lib/httpx/options.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module HTTPX
|
4
4
|
class Options
|
5
|
-
MAX_CONCURRENT_REQUESTS = 100
|
6
5
|
WINDOW_SIZE = 1 << 14 # 16K
|
7
6
|
MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
|
8
7
|
|
@@ -28,7 +27,14 @@ module HTTPX
|
|
28
27
|
defined_options << name.to_sym
|
29
28
|
interpreter ||= ->(v) { v }
|
30
29
|
|
31
|
-
|
30
|
+
attr_reader name
|
31
|
+
|
32
|
+
define_method(:"#{name}=") do |value|
|
33
|
+
return if value.nil?
|
34
|
+
|
35
|
+
instance_variable_set(:"@#{name}", instance_exec(value, &interpreter))
|
36
|
+
end
|
37
|
+
|
32
38
|
protected :"#{name}="
|
33
39
|
|
34
40
|
define_method(:"with_#{name}") do |value|
|
@@ -48,7 +54,6 @@ module HTTPX
|
|
48
54
|
:fallback_protocol => "http/1.1",
|
49
55
|
:timeout => Timeout.new,
|
50
56
|
:headers => {},
|
51
|
-
:max_concurrent_requests => MAX_CONCURRENT_REQUESTS,
|
52
57
|
:window_size => WINDOW_SIZE,
|
53
58
|
:body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
|
54
59
|
:request_class => Class.new(Request),
|
@@ -66,37 +71,48 @@ module HTTPX
|
|
66
71
|
|
67
72
|
defaults.merge!(options)
|
68
73
|
defaults[:headers] = Headers.new(defaults[:headers])
|
69
|
-
defaults.each
|
74
|
+
defaults.each do |(k, v)|
|
75
|
+
__send__(:"#{k}=", v)
|
76
|
+
end
|
70
77
|
end
|
71
78
|
|
72
79
|
def_option(:headers) do |headers|
|
73
|
-
self.headers
|
80
|
+
if self.headers
|
81
|
+
self.headers.merge(headers)
|
82
|
+
else
|
83
|
+
headers
|
84
|
+
end
|
74
85
|
end
|
75
86
|
|
76
87
|
def_option(:timeout) do |opts|
|
77
|
-
|
88
|
+
Timeout.new(opts)
|
78
89
|
end
|
79
90
|
|
80
91
|
def_option(:max_concurrent_requests) do |num|
|
81
|
-
|
82
|
-
raise Error, ":max_concurrent_requests must be positive" unless max.positive?
|
92
|
+
raise Error, ":max_concurrent_requests must be positive" unless num.positive?
|
83
93
|
|
84
|
-
|
94
|
+
num
|
95
|
+
end
|
96
|
+
|
97
|
+
def_option(:max_requests) do |num|
|
98
|
+
raise Error, ":max_requests must be positive" unless num.positive?
|
99
|
+
|
100
|
+
num
|
85
101
|
end
|
86
102
|
|
87
103
|
def_option(:window_size) do |num|
|
88
|
-
|
104
|
+
Integer(num)
|
89
105
|
end
|
90
106
|
|
91
107
|
def_option(:body_threshold_size) do |num|
|
92
|
-
|
108
|
+
Integer(num)
|
93
109
|
end
|
94
110
|
|
95
111
|
def_option(:transport) do |tr|
|
96
112
|
transport = tr.to_s
|
97
113
|
raise Error, "#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
|
98
114
|
|
99
|
-
|
115
|
+
transport
|
100
116
|
end
|
101
117
|
|
102
118
|
%w[
|
@@ -172,11 +188,5 @@ module HTTPX
|
|
172
188
|
response_body_class.freeze
|
173
189
|
connection_class.freeze
|
174
190
|
end
|
175
|
-
|
176
|
-
protected
|
177
|
-
|
178
|
-
def []=(option, val)
|
179
|
-
send(:"#{option}=", val)
|
180
|
-
end
|
181
191
|
end
|
182
192
|
end
|
@@ -17,14 +17,22 @@ module HTTPX
|
|
17
17
|
def self.extra_options(options)
|
18
18
|
Class.new(options.class) do
|
19
19
|
def_option(:cookies) do |cookies|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if cookies.is_a?(Store)
|
21
|
+
cookies
|
22
|
+
else
|
23
|
+
Store.new(cookies)
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end.new(options)
|
25
27
|
end
|
26
28
|
|
27
29
|
class Store
|
30
|
+
def self.new(cookies = nil)
|
31
|
+
return cookies if cookies.is_a?(self)
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
28
36
|
def initialize(cookies = nil)
|
29
37
|
@store = Hash.new { |hash, origin| hash[origin] = HTTP::CookieJar.new }
|
30
38
|
return unless cookies
|
@@ -74,10 +82,6 @@ module HTTPX
|
|
74
82
|
super({ cookies: Store.new }.merge(options), &blk)
|
75
83
|
end
|
76
84
|
|
77
|
-
def with_cookies(cookies)
|
78
|
-
branch(default_options.with_cookies(cookies))
|
79
|
-
end
|
80
|
-
|
81
85
|
def wrap
|
82
86
|
return super unless block_given?
|
83
87
|
|
@@ -86,7 +90,7 @@ module HTTPX
|
|
86
90
|
begin
|
87
91
|
yield session
|
88
92
|
ensure
|
89
|
-
@options = @options.
|
93
|
+
@options = @options.with(cookies: old_cookies_store)
|
90
94
|
end
|
91
95
|
end
|
92
96
|
end
|
@@ -0,0 +1,66 @@
|
|
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
|
+
end.new(options).merge(expect_timeout: EXPECT_TIMEOUT)
|
22
|
+
end
|
23
|
+
|
24
|
+
module RequestBodyMethods
|
25
|
+
def initialize(*)
|
26
|
+
super
|
27
|
+
return if @body.nil?
|
28
|
+
|
29
|
+
@headers["expect"] = "100-continue"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ConnectionMethods
|
34
|
+
def send(request)
|
35
|
+
request.once(:expects) do
|
36
|
+
@timers.after(@options.expect_timeout) do
|
37
|
+
if request.state == :expects && !request.expects?
|
38
|
+
request.headers.delete("expect")
|
39
|
+
handle(request)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
def fetch_response(request, connections, options)
|
49
|
+
response = @responses.delete(request)
|
50
|
+
return unless response
|
51
|
+
|
52
|
+
if response.status == 417 && request.headers.key?("expect")
|
53
|
+
request.headers.delete("expect")
|
54
|
+
request.transition(:idle)
|
55
|
+
connection = find_connection(request, connections, options)
|
56
|
+
connection.send(request)
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
response
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
register_plugin :expect, Expect
|
65
|
+
end
|
66
|
+
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
|
@@ -91,15 +91,13 @@ module HTTPX
|
|
91
91
|
def headline_uri(request)
|
92
92
|
return super unless request.verb == :connect
|
93
93
|
|
94
|
-
|
95
|
-
tunnel = "#{uri.hostname}:#{uri.port}"
|
94
|
+
tunnel = request.path
|
96
95
|
log { "establishing HTTP proxy tunnel to #{tunnel}" }
|
97
96
|
tunnel
|
98
97
|
end
|
99
98
|
|
100
99
|
def empty?
|
101
|
-
@requests.reject { |r| r.verb == :connect }.empty? ||
|
102
|
-
@requests.all? { |request| !request.response.nil? }
|
100
|
+
@requests.reject { |r| r.verb == :connect }.empty? || @requests.all? { |request| !request.response.nil? }
|
103
101
|
end
|
104
102
|
end
|
105
103
|
|
@@ -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
|
@@ -73,8 +73,22 @@ module HTTPX
|
|
73
73
|
request.retries -= 1
|
74
74
|
log { "failed to get response, #{request.retries} tries to go..." }
|
75
75
|
request.transition(:idle)
|
76
|
-
|
77
|
-
|
76
|
+
|
77
|
+
retry_after = options.retry_after
|
78
|
+
if retry_after
|
79
|
+
retry_after = retry_after.call(request) if retry_after.respond_to?(:call)
|
80
|
+
|
81
|
+
log { "retrying after #{retry_after} secs..." }
|
82
|
+
pool.after(retry_after) do
|
83
|
+
log { "retrying!!" }
|
84
|
+
connection = find_connection(request, connections, options)
|
85
|
+
connection.send(request)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
connection = find_connection(request, connections, options)
|
89
|
+
connection.send(request)
|
90
|
+
end
|
91
|
+
|
78
92
|
return
|
79
93
|
end
|
80
94
|
response
|
@@ -87,23 +101,6 @@ module HTTPX
|
|
87
101
|
def __retryable_error?(ex)
|
88
102
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) }
|
89
103
|
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
104
|
end
|
108
105
|
|
109
106
|
module RequestMethods
|
data/lib/httpx/pool.rb
CHANGED
@@ -27,7 +27,13 @@ module HTTPX
|
|
27
27
|
|
28
28
|
def next_tick
|
29
29
|
catch(:jump_tick) do
|
30
|
-
|
30
|
+
timeout = [next_timeout, @timers.wait_interval].compact.min
|
31
|
+
if timeout.negative?
|
32
|
+
@timers.fire
|
33
|
+
throw(:jump_tick)
|
34
|
+
end
|
35
|
+
|
36
|
+
@selector.select(timeout) do |monitor|
|
31
37
|
monitor.io.call
|
32
38
|
monitor.interests = monitor.io.interests
|
33
39
|
end
|
@@ -51,6 +57,7 @@ module HTTPX
|
|
51
57
|
|
52
58
|
def init_connection(connection, _options)
|
53
59
|
resolve_connection(connection)
|
60
|
+
connection.timers = @timers
|
54
61
|
connection.on(:open) do
|
55
62
|
@connected_connections += 1
|
56
63
|
end
|
data/lib/httpx/request.rb
CHANGED
@@ -37,8 +37,6 @@ module HTTPX
|
|
37
37
|
|
38
38
|
attr_reader :options, :response
|
39
39
|
|
40
|
-
attr_accessor :timer
|
41
|
-
|
42
40
|
def_delegator :@body, :<<
|
43
41
|
|
44
42
|
def_delegator :@body, :empty?
|
@@ -83,7 +81,6 @@ module HTTPX
|
|
83
81
|
def response=(response)
|
84
82
|
return unless response
|
85
83
|
|
86
|
-
@timer.cancel if @timer
|
87
84
|
@response = response
|
88
85
|
end
|
89
86
|
|
@@ -238,15 +235,13 @@ module HTTPX
|
|
238
235
|
when 100
|
239
236
|
# deallocate
|
240
237
|
@response = nil
|
241
|
-
when 417
|
242
|
-
@response = @response
|
243
|
-
return
|
244
238
|
end
|
245
239
|
end
|
246
240
|
when :done
|
247
241
|
return if @state == :expect
|
248
242
|
end
|
249
243
|
@state = nextstate
|
244
|
+
emit(@state)
|
250
245
|
nil
|
251
246
|
end
|
252
247
|
|
data/lib/httpx/response.rb
CHANGED
data/lib/httpx/session.rb
CHANGED
@@ -60,22 +60,33 @@ module HTTPX
|
|
60
60
|
connection = pool.find_connection(uri, options) || build_connection(uri, options)
|
61
61
|
unless connections.nil? || connections.include?(connection)
|
62
62
|
connections << connection
|
63
|
-
set_connection_callbacks(connection, options)
|
63
|
+
set_connection_callbacks(connection, connections, options)
|
64
64
|
end
|
65
65
|
connection
|
66
66
|
end
|
67
67
|
|
68
|
-
def set_connection_callbacks(connection, options)
|
68
|
+
def set_connection_callbacks(connection, connections, options)
|
69
69
|
connection.on(:uncoalesce) do |uncoalesced_uri|
|
70
70
|
other_connection = build_connection(uncoalesced_uri, options)
|
71
|
+
connections << other_connection
|
71
72
|
connection.unmerge(other_connection)
|
72
73
|
end
|
73
74
|
connection.on(:altsvc) do |alt_origin, origin, alt_params|
|
74
|
-
build_altsvc_connection(connection, alt_origin, origin, alt_params, options)
|
75
|
+
other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
|
76
|
+
connections << other_connection if other_connection
|
77
|
+
end
|
78
|
+
connection.on(:exhausted) do
|
79
|
+
other_connection = connection.create_idle
|
80
|
+
other_connection.merge(connection)
|
81
|
+
catch(:coalesced) do
|
82
|
+
pool.init_connection(other_connection, options)
|
83
|
+
end
|
84
|
+
set_connection_callbacks(other_connection, connections, options)
|
85
|
+
connections << other_connection
|
75
86
|
end
|
76
87
|
end
|
77
88
|
|
78
|
-
def build_altsvc_connection(existing_connection, alt_origin, origin, alt_params, options)
|
89
|
+
def build_altsvc_connection(existing_connection, connections, alt_origin, origin, alt_params, options)
|
79
90
|
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
80
91
|
|
81
92
|
# altsvc already exists, somehow it wasn't advertised, probably noop
|
@@ -85,7 +96,7 @@ module HTTPX
|
|
85
96
|
# advertised altsvc is the same origin being used, ignore
|
86
97
|
return if connection == existing_connection
|
87
98
|
|
88
|
-
set_connection_callbacks(connection, options)
|
99
|
+
set_connection_callbacks(connection, connections, options)
|
89
100
|
|
90
101
|
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
91
102
|
|
@@ -99,8 +110,10 @@ module HTTPX
|
|
99
110
|
end
|
100
111
|
|
101
112
|
connection.merge(existing_connection)
|
113
|
+
connection
|
102
114
|
rescue UnsupportedSchemeError
|
103
115
|
altsvc["noop"] = true
|
116
|
+
nil
|
104
117
|
end
|
105
118
|
|
106
119
|
def build_requests(*args, options)
|
@@ -155,7 +168,6 @@ module HTTPX
|
|
155
168
|
error = catch(:resolve_error) do
|
156
169
|
connection = find_connection(request, connections, request_options)
|
157
170
|
connection.send(request)
|
158
|
-
set_request_timeout(connection, request, request_options)
|
159
171
|
end
|
160
172
|
next unless error.is_a?(ResolveError)
|
161
173
|
|
@@ -191,21 +203,6 @@ module HTTPX
|
|
191
203
|
request
|
192
204
|
end
|
193
205
|
|
194
|
-
def set_request_timeout(connection, request, options)
|
195
|
-
total = options.timeout.total_timeout
|
196
|
-
return unless total
|
197
|
-
|
198
|
-
timer = pool.after(total) do
|
199
|
-
unless @responses[request]
|
200
|
-
error = TotalTimeoutError.new(total, "Timed out after #{total} seconds")
|
201
|
-
response = ErrorResponse.new(request, error, options)
|
202
|
-
request.emit(:response, response)
|
203
|
-
connection.reset
|
204
|
-
end
|
205
|
-
end
|
206
|
-
request.timer = timer
|
207
|
-
end
|
208
|
-
|
209
206
|
@default_options = Options.new
|
210
207
|
@default_options.freeze
|
211
208
|
@plugins = []
|
data/lib/httpx/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2-next
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- lib/httpx/plugins/compression/gzip.rb
|
110
110
|
- lib/httpx/plugins/cookies.rb
|
111
111
|
- lib/httpx/plugins/digest_authentication.rb
|
112
|
+
- lib/httpx/plugins/expect.rb
|
112
113
|
- lib/httpx/plugins/follow_redirects.rb
|
113
114
|
- lib/httpx/plugins/h2c.rb
|
114
115
|
- lib/httpx/plugins/multipart.rb
|