httpx 1.0.2 → 1.1.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/README.md +2 -2
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/lib/httpx/adapters/faraday.rb +28 -19
- data/lib/httpx/connection/http1.rb +13 -6
- data/lib/httpx/connection/http2.rb +1 -1
- data/lib/httpx/connection.rb +53 -15
- data/lib/httpx/domain_name.rb +6 -2
- data/lib/httpx/errors.rb +32 -0
- data/lib/httpx/io/ssl.rb +3 -1
- data/lib/httpx/io/tcp.rb +4 -2
- data/lib/httpx/io.rb +5 -1
- data/lib/httpx/options.rb +48 -1
- data/lib/httpx/plugins/expect.rb +10 -8
- data/lib/httpx/plugins/proxy/http.rb +0 -1
- data/lib/httpx/pool.rb +0 -4
- data/lib/httpx/request/body.rb +22 -9
- data/lib/httpx/request.rb +63 -4
- data/lib/httpx/resolver/native.rb +2 -2
- data/lib/httpx/resolver/resolver.rb +5 -2
- data/lib/httpx/resolver/system.rb +5 -2
- data/lib/httpx/resolver.rb +6 -4
- data/lib/httpx/response/body.rb +30 -5
- data/lib/httpx/response/buffer.rb +20 -14
- data/lib/httpx/response.rb +95 -16
- data/lib/httpx/selector.rb +2 -2
- data/lib/httpx/session.rb +64 -2
- data/lib/httpx/timers.rb +35 -8
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http1.rbs +3 -3
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +4 -1
- data/sig/io/tcp.rbs +1 -1
- data/sig/options.rbs +2 -2
- data/sig/pool.rbs +1 -1
- data/sig/request/body.rbs +0 -2
- data/sig/request.rbs +9 -3
- data/sig/resolver/native.rbs +1 -1
- data/sig/resolver.rbs +1 -1
- data/sig/response/body.rbs +0 -1
- data/sig/response.rbs +11 -3
- data/sig/timers.rbs +17 -7
- data/sig/transcoder/utils/inflater.rbs +12 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39a2d410f391dedb077e3704edeafde92786201ebd7b1d3e4e2f6aa7e254797b
|
|
4
|
+
data.tar.gz: bdcc46a37c8cc4ba8edd11355c5dc439a7401569c8ef014c7ca45ac22e97d284
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: df50d7db27a18a2f7d610e12b618fc1a8ab2d18a6a1f3bbd70054a4fdab9bec8a8e428115696d5f042f28c7409e7539eaca0f3798d13bc6ab98d88e43d41808d
|
|
7
|
+
data.tar.gz: 63563a10039713d75e6d3d795ddfc08f91a46c2f73225b9987e96acbed9e23d0c5e69154589afbf0163ec69ff99eb9c34f517fb9d360ad7f951a06fb5569ce95
|
data/README.md
CHANGED
|
@@ -108,8 +108,8 @@ HTTPX.get(
|
|
|
108
108
|
```ruby
|
|
109
109
|
response = HTTPX.get("https://www.google.com", params: { q: "me" })
|
|
110
110
|
response = HTTPX.post("https://www.nghttp2.org/httpbin/post", form: {name: "John", age: "22"})
|
|
111
|
-
response = HTTPX.plugin(:
|
|
112
|
-
.
|
|
111
|
+
response = HTTPX.plugin(:basic_auth)
|
|
112
|
+
.basic_auth("user", "pass")
|
|
113
113
|
.get("https://www.google.com")
|
|
114
114
|
|
|
115
115
|
# more complex client objects can be cached, and are thread-safe
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 1.1.0
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
A function, `#peer_address`, was added to the response object, which returns the IP (either a string or an `IPAddr` object) from the socket used to get the response from.
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
response = HTTPX.get("https://example.com")
|
|
9
|
+
response.peer_address #=> #<IPAddr: IPv4:93.184.216.34/255.255.255.255>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
error responses will also expose an IP address via `#peer_address` as long a connection happened before the error.
|
|
13
|
+
|
|
14
|
+
## Improvements
|
|
15
|
+
|
|
16
|
+
* A performance regression involving the new default timeouts has been fixed, which could cause significant overhead in "multiple requests in sequence" scenarios, and was clearly visible in benchmarks.
|
|
17
|
+
* this regression will still be seen in jruby due to a bug, which fix will be released in jruby 9.4.5.0.
|
|
18
|
+
* HTTP/1.1 connections are now set to handle as many requests as they can by default (instead of the past default of max 200, at which point they'd be recycled).
|
|
19
|
+
* tolerate the inexistence of `openssl` in the installed ruby, like `net-http` does.
|
|
20
|
+
* `on_connection_opened` and `on_connection_closed` will yield the `OpenSSL::SSL::SSLSocket` instance for `https` backed origins (instead of always the `Socket` instance).
|
|
21
|
+
|
|
22
|
+
## Bugfixes
|
|
23
|
+
|
|
24
|
+
* when using the `:native` resolver (default option), a default of 1 for ndots is set, for systems which do not set one.
|
|
25
|
+
* replaced usage of `Float::INFINITY` with `nil` for timeout defaults, as the former can't be used in IO wait functions.
|
|
26
|
+
* `faraday` adapter timeout setup now maps to `:read_timeout` and `:write_timeout` options from `httpx`.
|
|
27
|
+
* fixed HTTP/1.1 connection recycling on number of max requests exhausted.
|
|
28
|
+
* `response.json` will now work when "content-type" header is set to "application/hal+json".
|
|
29
|
+
|
|
30
|
+
## Chore
|
|
31
|
+
|
|
32
|
+
* when using the `:cookies` plugin, a warning message to install the idnx message will only be emitted if the cookie domain is an IDN (this message was being shown all the time since v1 release).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 1.1.1
|
|
2
|
+
|
|
3
|
+
## improvements
|
|
4
|
+
|
|
5
|
+
* (Re-)enabling default retries in DNS name queries; this had been disabled as a result of revamping timouts, and resulted in queries only being sent once, which is very little for UDP-related traffic, and breaks if using DNs rate-limiting software. Retries the query just once, for now.
|
|
6
|
+
|
|
7
|
+
## bugfixes
|
|
8
|
+
|
|
9
|
+
* reset timers when adding new intervals, as these may be added as a result on after-select connection handling, and must wait for the next tick cycle (before the patch, they were triggering too soon).
|
|
10
|
+
* fixed "on close" callback leak on connection reuse, which caused linear performance regression in benchmarks performing one request per connection.
|
|
11
|
+
* fixed hanging connection whan an HTTP/1.1 emitted a "connection: close" header but the server would not emit one (it closes the connection now).
|
|
12
|
+
* fixed recursive dns cached lookups which may have already expired, and created nil entries in the returned address list.
|
|
13
|
+
* dns system resolver is now able to retry on failure.
|
|
14
|
+
|
|
15
|
+
## chore
|
|
16
|
+
|
|
17
|
+
* remove duplicated callback unregitering connections.
|
|
@@ -69,38 +69,47 @@ module Faraday
|
|
|
69
69
|
timeout_options = {}
|
|
70
70
|
req_opts = env.request
|
|
71
71
|
if (sec = request_timeout(:read, req_opts))
|
|
72
|
-
timeout_options[:
|
|
72
|
+
timeout_options[:read_timeout] = sec
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
if (sec = request_timeout(:write, req_opts))
|
|
76
|
-
timeout_options[:
|
|
76
|
+
timeout_options[:write_timeout] = sec
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
if (sec = request_timeout(:open, req_opts))
|
|
80
80
|
timeout_options[:connect_timeout] = sec
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
ssl_options = {}
|
|
84
|
-
|
|
85
|
-
unless env.ssl.verify.nil?
|
|
86
|
-
ssl_options[:verify_mode] = env.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
ssl_options[:ca_file] = env.ssl.ca_file if env.ssl.ca_file
|
|
90
|
-
ssl_options[:ca_path] = env.ssl.ca_path if env.ssl.ca_path
|
|
91
|
-
ssl_options[:cert_store] = env.ssl.cert_store if env.ssl.cert_store
|
|
92
|
-
ssl_options[:cert] = env.ssl.client_cert if env.ssl.client_cert
|
|
93
|
-
ssl_options[:key] = env.ssl.client_key if env.ssl.client_key
|
|
94
|
-
ssl_options[:ssl_version] = env.ssl.version if env.ssl.version
|
|
95
|
-
ssl_options[:verify_depth] = env.ssl.verify_depth if env.ssl.verify_depth
|
|
96
|
-
ssl_options[:min_version] = env.ssl.min_version if env.ssl.min_version
|
|
97
|
-
ssl_options[:max_version] = env.ssl.max_version if env.ssl.max_version
|
|
98
|
-
|
|
99
83
|
{
|
|
100
|
-
ssl:
|
|
84
|
+
ssl: ssl_options_from_env(env),
|
|
101
85
|
timeout: timeout_options,
|
|
102
86
|
}
|
|
103
87
|
end
|
|
88
|
+
|
|
89
|
+
if defined?(::OpenSSL)
|
|
90
|
+
def ssl_options_from_env(env)
|
|
91
|
+
ssl_options = {}
|
|
92
|
+
|
|
93
|
+
unless env.ssl.verify.nil?
|
|
94
|
+
ssl_options[:verify_mode] = env.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
ssl_options[:ca_file] = env.ssl.ca_file if env.ssl.ca_file
|
|
98
|
+
ssl_options[:ca_path] = env.ssl.ca_path if env.ssl.ca_path
|
|
99
|
+
ssl_options[:cert_store] = env.ssl.cert_store if env.ssl.cert_store
|
|
100
|
+
ssl_options[:cert] = env.ssl.client_cert if env.ssl.client_cert
|
|
101
|
+
ssl_options[:key] = env.ssl.client_key if env.ssl.client_key
|
|
102
|
+
ssl_options[:ssl_version] = env.ssl.version if env.ssl.version
|
|
103
|
+
ssl_options[:verify_depth] = env.ssl.verify_depth if env.ssl.verify_depth
|
|
104
|
+
ssl_options[:min_version] = env.ssl.min_version if env.ssl.min_version
|
|
105
|
+
ssl_options[:max_version] = env.ssl.max_version if env.ssl.max_version
|
|
106
|
+
ssl_options
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
def ssl_options_from_env(*)
|
|
110
|
+
{}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
104
113
|
end
|
|
105
114
|
|
|
106
115
|
include RequestMixin
|
|
@@ -15,7 +15,7 @@ module HTTPX
|
|
|
15
15
|
def initialize(buffer, options)
|
|
16
16
|
@options = Options.new(options)
|
|
17
17
|
@max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
|
|
18
|
-
@max_requests = @options.max_requests
|
|
18
|
+
@max_requests = @options.max_requests
|
|
19
19
|
@parser = Parser::HTTP1.new(self)
|
|
20
20
|
@buffer = buffer
|
|
21
21
|
@version = [1, 1]
|
|
@@ -181,10 +181,17 @@ module HTTPX
|
|
|
181
181
|
if response.is_a?(ErrorResponse)
|
|
182
182
|
disable
|
|
183
183
|
else
|
|
184
|
-
manage_connection(response)
|
|
184
|
+
manage_connection(request, response)
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
if exhausted?
|
|
188
|
+
@pending.concat(@requests)
|
|
189
|
+
@requests.clear
|
|
190
|
+
|
|
191
|
+
emit(:exhausted)
|
|
192
|
+
else
|
|
193
|
+
send(@pending.shift) unless @pending.empty?
|
|
194
|
+
end
|
|
188
195
|
end
|
|
189
196
|
|
|
190
197
|
def handle_error(ex)
|
|
@@ -217,7 +224,7 @@ module HTTPX
|
|
|
217
224
|
|
|
218
225
|
private
|
|
219
226
|
|
|
220
|
-
def manage_connection(response)
|
|
227
|
+
def manage_connection(request, response)
|
|
221
228
|
connection = response.headers["connection"]
|
|
222
229
|
case connection
|
|
223
230
|
when /keep-alive/i
|
|
@@ -247,7 +254,7 @@ module HTTPX
|
|
|
247
254
|
disable
|
|
248
255
|
when nil
|
|
249
256
|
# In HTTP/1.1, it's keep alive by default
|
|
250
|
-
return if response.version == "1.1"
|
|
257
|
+
return if response.version == "1.1" && request.headers["connection"] != "close"
|
|
251
258
|
|
|
252
259
|
disable
|
|
253
260
|
end
|
|
@@ -287,7 +294,7 @@ module HTTPX
|
|
|
287
294
|
|
|
288
295
|
connection = request.headers["connection"]
|
|
289
296
|
|
|
290
|
-
connection ||= if request.
|
|
297
|
+
connection ||= if request.persistent?
|
|
291
298
|
# when in a persistent connection, the request can't be at
|
|
292
299
|
# the edge of a renegotiation
|
|
293
300
|
if @requests.index(request) + 1 < @max_requests
|
|
@@ -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
|
|
data/lib/httpx/connection.rb
CHANGED
|
@@ -70,6 +70,8 @@ module HTTPX
|
|
|
70
70
|
@inflight = 0
|
|
71
71
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
|
72
72
|
|
|
73
|
+
@intervals = []
|
|
74
|
+
|
|
73
75
|
self.addresses = @options.addresses if @options.addresses
|
|
74
76
|
end
|
|
75
77
|
|
|
@@ -213,6 +215,9 @@ module HTTPX
|
|
|
213
215
|
|
|
214
216
|
def call
|
|
215
217
|
case @state
|
|
218
|
+
when :idle
|
|
219
|
+
connect
|
|
220
|
+
consume
|
|
216
221
|
when :closed
|
|
217
222
|
return
|
|
218
223
|
when :closing
|
|
@@ -268,7 +273,7 @@ module HTTPX
|
|
|
268
273
|
end
|
|
269
274
|
|
|
270
275
|
def timeout
|
|
271
|
-
return @timeout if
|
|
276
|
+
return @timeout if @timeout
|
|
272
277
|
|
|
273
278
|
return @options.timeout[:connect_timeout] if @state == :idle
|
|
274
279
|
|
|
@@ -294,7 +299,15 @@ module HTTPX
|
|
|
294
299
|
@state == :open || @state == :inactive
|
|
295
300
|
end
|
|
296
301
|
|
|
297
|
-
def
|
|
302
|
+
def handle_socket_timeout(interval)
|
|
303
|
+
@intervals.delete_if(&:elapsed?)
|
|
304
|
+
|
|
305
|
+
unless @intervals.empty?
|
|
306
|
+
# remove the intervals which will elapse
|
|
307
|
+
|
|
308
|
+
return
|
|
309
|
+
end
|
|
310
|
+
|
|
298
311
|
error = HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
|
|
299
312
|
error.set_backtrace(caller)
|
|
300
313
|
on_error(error)
|
|
@@ -449,6 +462,7 @@ module HTTPX
|
|
|
449
462
|
|
|
450
463
|
def send_request_to_parser(request)
|
|
451
464
|
@inflight += 1
|
|
465
|
+
request.peer_address = @io.ip
|
|
452
466
|
parser.send(request)
|
|
453
467
|
|
|
454
468
|
set_request_timeouts(request)
|
|
@@ -504,7 +518,6 @@ module HTTPX
|
|
|
504
518
|
else
|
|
505
519
|
transition(:closing)
|
|
506
520
|
transition(:closed)
|
|
507
|
-
emit(:reset)
|
|
508
521
|
|
|
509
522
|
@parser.reset if @parser
|
|
510
523
|
transition(:idle)
|
|
@@ -603,7 +616,7 @@ module HTTPX
|
|
|
603
616
|
def purge_after_closed
|
|
604
617
|
@io.close if @io
|
|
605
618
|
@read_buffer.clear
|
|
606
|
-
|
|
619
|
+
@timeout = nil
|
|
607
620
|
end
|
|
608
621
|
|
|
609
622
|
def build_socket(addrs = nil)
|
|
@@ -655,19 +668,26 @@ module HTTPX
|
|
|
655
668
|
|
|
656
669
|
def set_request_timeouts(request)
|
|
657
670
|
write_timeout = request.write_timeout
|
|
658
|
-
request.once(:headers) do
|
|
659
|
-
@timers.after(write_timeout) { write_timeout_callback(request, write_timeout) }
|
|
660
|
-
end unless write_timeout.nil? || write_timeout.infinite?
|
|
661
|
-
|
|
662
671
|
read_timeout = request.read_timeout
|
|
663
|
-
request.once(:done) do
|
|
664
|
-
@timers.after(read_timeout) { read_timeout_callback(request, read_timeout) }
|
|
665
|
-
end unless read_timeout.nil? || read_timeout.infinite?
|
|
666
|
-
|
|
667
672
|
request_timeout = request.request_timeout
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
673
|
+
|
|
674
|
+
unless write_timeout.nil? || write_timeout.infinite?
|
|
675
|
+
set_request_timeout(request, write_timeout, :headers, %i[done response]) do
|
|
676
|
+
write_timeout_callback(request, write_timeout)
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
unless read_timeout.nil? || read_timeout.infinite?
|
|
681
|
+
set_request_timeout(request, read_timeout, :done, :response) do
|
|
682
|
+
read_timeout_callback(request, read_timeout)
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
return if request_timeout.nil? || request_timeout.infinite?
|
|
687
|
+
|
|
688
|
+
set_request_timeout(request, request_timeout, :headers, :response) do
|
|
689
|
+
read_timeout_callback(request, request_timeout, RequestTimeoutError)
|
|
690
|
+
end
|
|
671
691
|
end
|
|
672
692
|
|
|
673
693
|
def write_timeout_callback(request, write_timeout)
|
|
@@ -688,6 +708,24 @@ module HTTPX
|
|
|
688
708
|
on_error(error)
|
|
689
709
|
end
|
|
690
710
|
|
|
711
|
+
def set_request_timeout(request, timeout, start_event, finish_events, &callback)
|
|
712
|
+
request.once(start_event) do
|
|
713
|
+
interval = @timers.after(timeout, callback)
|
|
714
|
+
|
|
715
|
+
Array(finish_events).each do |event|
|
|
716
|
+
# clean up reques timeouts if the connection errors out
|
|
717
|
+
request.once(event) do
|
|
718
|
+
if @intervals.include?(interval)
|
|
719
|
+
interval.delete(callback)
|
|
720
|
+
@intervals.delete(interval) if interval.no_callbacks?
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
@intervals << interval
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
|
|
691
729
|
class << self
|
|
692
730
|
def parser_type(protocol)
|
|
693
731
|
case protocol
|
data/lib/httpx/domain_name.rb
CHANGED
|
@@ -61,8 +61,12 @@ module HTTPX
|
|
|
61
61
|
# Normalizes a _domain_ using the Punycode algorithm as necessary.
|
|
62
62
|
# The result will be a downcased, ASCII-only string.
|
|
63
63
|
def normalize(domain)
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
unless domain.ascii_only?
|
|
65
|
+
domain = domain.chomp(".").unicode_normalize(:nfc)
|
|
66
|
+
domain = Punycode.encode_hostname(domain)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
domain.downcase
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
72
|
|
data/lib/httpx/errors.rb
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTPX
|
|
4
|
+
# the default exception class for exceptions raised by HTTPX.
|
|
4
5
|
class Error < StandardError; end
|
|
5
6
|
|
|
6
7
|
class UnsupportedSchemeError < Error; end
|
|
7
8
|
|
|
8
9
|
class ConnectionError < Error; end
|
|
9
10
|
|
|
11
|
+
# Error raised when there was a timeout. Its subclasses allow for finer-grained
|
|
12
|
+
# control of which timeout happened.
|
|
10
13
|
class TimeoutError < Error
|
|
14
|
+
# The timeout value which caused this error to be raised.
|
|
11
15
|
attr_reader :timeout
|
|
12
16
|
|
|
17
|
+
# initializes the timeout exception with the +timeout+ causing the error, and the
|
|
18
|
+
# error +message+ for it.
|
|
13
19
|
def initialize(timeout, message)
|
|
14
20
|
@timeout = timeout
|
|
15
21
|
super(message)
|
|
16
22
|
end
|
|
17
23
|
|
|
24
|
+
# clones this error into a HTTPX::ConnectionTimeoutError.
|
|
18
25
|
def to_connection_error
|
|
19
26
|
ex = ConnectTimeoutError.new(@timeout, message)
|
|
20
27
|
ex.set_backtrace(backtrace)
|
|
@@ -22,11 +29,19 @@ module HTTPX
|
|
|
22
29
|
end
|
|
23
30
|
end
|
|
24
31
|
|
|
32
|
+
# Error raised when there was a timeout establishing the connection to a server.
|
|
33
|
+
# This may be raised due to timeouts during TCP and TLS (when applicable) connection
|
|
34
|
+
# establishment.
|
|
25
35
|
class ConnectTimeoutError < TimeoutError; end
|
|
26
36
|
|
|
37
|
+
# Error raised when there was a timeout while sending a request, or receiving a response
|
|
38
|
+
# from the server.
|
|
27
39
|
class RequestTimeoutError < TimeoutError
|
|
40
|
+
# The HTTPX::Request request object this exception refers to.
|
|
28
41
|
attr_reader :request
|
|
29
42
|
|
|
43
|
+
# initializes the exception with the +request+ and +response+ it refers to, and the
|
|
44
|
+
# +timeout+ causing the error, and the
|
|
30
45
|
def initialize(request, response, timeout)
|
|
31
46
|
@request = request
|
|
32
47
|
@response = response
|
|
@@ -38,19 +53,28 @@ module HTTPX
|
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
|
|
56
|
+
# Error raised when there was a timeout while receiving a response from the server.
|
|
41
57
|
class ReadTimeoutError < RequestTimeoutError; end
|
|
42
58
|
|
|
59
|
+
# Error raised when there was a timeout while sending a request from the server.
|
|
43
60
|
class WriteTimeoutError < RequestTimeoutError; end
|
|
44
61
|
|
|
62
|
+
# Error raised when there was a timeout while waiting for the HTTP/2 settings frame from the server.
|
|
45
63
|
class SettingsTimeoutError < TimeoutError; end
|
|
46
64
|
|
|
65
|
+
# Error raised when there was a timeout while resolving a domain to an IP.
|
|
47
66
|
class ResolveTimeoutError < TimeoutError; end
|
|
48
67
|
|
|
68
|
+
# Error raised when there was an error while resolving a domain to an IP.
|
|
49
69
|
class ResolveError < Error; end
|
|
50
70
|
|
|
71
|
+
# Error raised when there was an error while resolving a domain to an IP
|
|
72
|
+
# using a HTTPX::Resolver::Native resolver.
|
|
51
73
|
class NativeResolveError < ResolveError
|
|
52
74
|
attr_reader :connection, :host
|
|
53
75
|
|
|
76
|
+
# initializes the exception with the +connection+ it refers to, the +host+ domain
|
|
77
|
+
# which failed to resolve, and the error +message+.
|
|
54
78
|
def initialize(connection, host, message = "Can't resolve #{host}")
|
|
55
79
|
@connection = connection
|
|
56
80
|
@host = host
|
|
@@ -58,18 +82,26 @@ module HTTPX
|
|
|
58
82
|
end
|
|
59
83
|
end
|
|
60
84
|
|
|
85
|
+
# The exception class for HTTP responses with 4xx or 5xx status.
|
|
61
86
|
class HTTPError < Error
|
|
87
|
+
# The HTTPX::Response response object this exception refers to.
|
|
62
88
|
attr_reader :response
|
|
63
89
|
|
|
90
|
+
# Creates the instance and assigns the HTTPX::Response +response+.
|
|
64
91
|
def initialize(response)
|
|
65
92
|
@response = response
|
|
66
93
|
super("HTTP Error: #{@response.status} #{@response.headers}\n#{@response.body}")
|
|
67
94
|
end
|
|
68
95
|
|
|
96
|
+
# The HTTP response status.
|
|
97
|
+
#
|
|
98
|
+
# error.status #=> 404
|
|
69
99
|
def status
|
|
70
100
|
@response.status
|
|
71
101
|
end
|
|
72
102
|
end
|
|
73
103
|
|
|
104
|
+
# error raised when a request was sent a server which can't reproduce a response, and
|
|
105
|
+
# has therefore returned an HTTP response using the 421 status code.
|
|
74
106
|
class MisdirectedRequestError < HTTPError; end
|
|
75
107
|
end
|
data/lib/httpx/io/ssl.rb
CHANGED
|
@@ -114,7 +114,9 @@ module HTTPX
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def try_ssl_connect
|
|
117
|
-
|
|
117
|
+
ret = @io.connect_nonblock(exception: false)
|
|
118
|
+
log(level: 3, color: :cyan) { "TLS CONNECT: #{ret}..." }
|
|
119
|
+
case ret
|
|
118
120
|
when :wait_readable
|
|
119
121
|
@interests = :r
|
|
120
122
|
return
|
data/lib/httpx/io/tcp.rb
CHANGED
|
@@ -41,7 +41,7 @@ module HTTPX
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def socket
|
|
44
|
-
@io
|
|
44
|
+
@io
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def add_addresses(addrs)
|
|
@@ -95,7 +95,9 @@ module HTTPX
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def try_connect
|
|
98
|
-
|
|
98
|
+
ret = @io.connect_nonblock(Socket.sockaddr_in(@port, @ip.to_s), exception: false)
|
|
99
|
+
log(level: 3, color: :cyan) { "TCP CONNECT: #{ret}..." }
|
|
100
|
+
case ret
|
|
99
101
|
when :wait_readable
|
|
100
102
|
@interests = :r
|
|
101
103
|
return
|
data/lib/httpx/io.rb
CHANGED
data/lib/httpx/options.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "socket"
|
|
4
4
|
|
|
5
5
|
module HTTPX
|
|
6
|
+
# Contains a set of options which are passed and shared across from session to its requests or
|
|
7
|
+
# responses.
|
|
6
8
|
class Options
|
|
7
9
|
BUFFER_SIZE = 1 << 14
|
|
8
10
|
WINDOW_SIZE = 1 << 14 # 16K
|
|
@@ -10,7 +12,7 @@ module HTTPX
|
|
|
10
12
|
KEEP_ALIVE_TIMEOUT = 20
|
|
11
13
|
SETTINGS_TIMEOUT = 10
|
|
12
14
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
|
13
|
-
REQUEST_TIMEOUT = OPERATION_TIMEOUT =
|
|
15
|
+
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
|
14
16
|
|
|
15
17
|
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
|
|
16
18
|
ip_address_families = begin
|
|
@@ -25,6 +27,7 @@ module HTTPX
|
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
DEFAULT_OPTIONS = {
|
|
30
|
+
:max_requests => Float::INFINITY,
|
|
28
31
|
:debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
|
|
29
32
|
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
|
30
33
|
:ssl => {},
|
|
@@ -81,6 +84,50 @@ module HTTPX
|
|
|
81
84
|
end
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
# creates a new options instance from a given hash, which optionally define the following:
|
|
88
|
+
#
|
|
89
|
+
# :debug :: an object which log messages are written to (must respond to <tt><<</tt>)
|
|
90
|
+
# :debug_level :: the log level of messages (can be 1, 2, or 3).
|
|
91
|
+
# :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::IO::SSL)
|
|
92
|
+
# :http2_settings :: a hash of options to be passed to a HTTP2Next::Connection (ex: <tt>{ max_concurrent_streams: 2 }</tt>)
|
|
93
|
+
# :fallback_protocol :: version of HTTP protocol to use by default in the absence of protocol negotiation
|
|
94
|
+
# like ALPN (defaults to <tt>"http/1.1"</tt>)
|
|
95
|
+
# :supported_compression_formats :: list of compressions supported by the transcoder layer (defaults to <tt>%w[gzip deflate]</tt>).
|
|
96
|
+
# :decompress_response_body :: whether to auto-decompress response body (defaults to <tt>true</tt>).
|
|
97
|
+
# :compress_request_body :: whether to auto-decompress response body (defaults to <tt>true</tt>)
|
|
98
|
+
# :timeout :: hash of timeout configurations (supports <tt>:connect_timeout</tt>, <tt>:settings_timeout</tt>,
|
|
99
|
+
# <tt>:operation_timeout</tt>, <tt>:keep_alive_timeout</tt>, <tt>:read_timeout</tt>, <tt>:write_timeout</tt>
|
|
100
|
+
# and <tt>:request_timeout</tt>
|
|
101
|
+
# :headers :: hash of HTTP headers (ex: <tt>{ "x-custom-foo" => "bar" }</tt>)
|
|
102
|
+
# :window_size :: number of bytes to read from a socket
|
|
103
|
+
# :buffer_size :: internal read and write buffer size in bytes
|
|
104
|
+
# :body_threshold_size :: maximum size in bytes of response payload that is buffered in memory.
|
|
105
|
+
# :request_class :: class used to instantiate a request
|
|
106
|
+
# :response_class :: class used to instantiate a response
|
|
107
|
+
# :headers_class :: class used to instantiate headers
|
|
108
|
+
# :request_body_class :: class used to instantiate a request body
|
|
109
|
+
# :response_body_class :: class used to instantiate a response body
|
|
110
|
+
# :connection_class :: class used to instantiate connections
|
|
111
|
+
# :options_class :: class used to instantiate options
|
|
112
|
+
# :transport :: type of transport to use (set to "unix" for UNIX sockets)
|
|
113
|
+
# :addresses :: bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses;
|
|
114
|
+
# paths should be used for UNIX sockets instead)
|
|
115
|
+
# :io :: open socket, or domain/ip-to-socket hash, which requests should be sent to
|
|
116
|
+
# :persistent :: whether to persist connections in between requests (defaults to <tt>true</tt>)
|
|
117
|
+
# :resolver_class :: which resolver to use (defaults to <tt>:native</tt>, can also be <tt>:system<tt> for
|
|
118
|
+
# using getaddrinfo or <tt>:https</tt> for DoH resolver, or a custom class)
|
|
119
|
+
# :resolver_options :: hash of options passed to the resolver
|
|
120
|
+
# :ip_families :: which socket families are supported (system-dependent)
|
|
121
|
+
# :origin :: HTTP origin to set on requests with relative path (ex: "https://api.serv.com")
|
|
122
|
+
# :base_path :: path to prefix given relative paths with (ex: "/v2")
|
|
123
|
+
# :max_concurrent_requests :: max number of requests which can be set concurrently
|
|
124
|
+
# :max_requests :: max number of requests which can be made on socket before it reconnects.
|
|
125
|
+
# :params :: hash or array of key-values which will be encoded and set in the query string of request uris.
|
|
126
|
+
# :form :: hash of array of key-values which will be form-or-multipart-encoded in requests body payload.
|
|
127
|
+
# :json :: hash of array of key-values which will be JSON-encoded in requests body payload.
|
|
128
|
+
# :xml :: Nokogiri XML nodes which will be encoded in requests body payload.
|
|
129
|
+
#
|
|
130
|
+
# This list of options are enhanced with each loaded plugin, see the plugin docs for details.
|
|
84
131
|
def initialize(options = {})
|
|
85
132
|
do_initialize(options)
|
|
86
133
|
freeze
|
data/lib/httpx/plugins/expect.rb
CHANGED
|
@@ -75,14 +75,16 @@ module HTTPX
|
|
|
75
75
|
|
|
76
76
|
return unless request.headers["expect"] == "100-continue"
|
|
77
77
|
|
|
78
|
-
request.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
expect_timeout = request.options.expect_timeout
|
|
79
|
+
|
|
80
|
+
return if expect_timeout.nil? || expect_timeout.infinite?
|
|
81
|
+
|
|
82
|
+
set_request_timeout(request, expect_timeout, :expect, %i[body response]) do
|
|
83
|
+
# expect timeout expired
|
|
84
|
+
if request.state == :expect && !request.expects?
|
|
85
|
+
Expect.no_expect_store << request.origin
|
|
86
|
+
request.headers.delete("expect")
|
|
87
|
+
consume
|
|
86
88
|
end
|
|
87
89
|
end
|
|
88
90
|
end
|
data/lib/httpx/pool.rb
CHANGED
|
@@ -52,7 +52,6 @@ module HTTPX
|
|
|
52
52
|
def close(connections = @connections)
|
|
53
53
|
return if connections.empty?
|
|
54
54
|
|
|
55
|
-
@timers.cancel
|
|
56
55
|
@eden_connections.clear
|
|
57
56
|
connections = connections.reject(&:inflight?)
|
|
58
57
|
connections.each(&:close)
|
|
@@ -224,9 +223,6 @@ module HTTPX
|
|
|
224
223
|
@connected_connections += 1
|
|
225
224
|
end
|
|
226
225
|
select_connection(connection)
|
|
227
|
-
connection.on(:close) do
|
|
228
|
-
unregister_connection(connection)
|
|
229
|
-
end
|
|
230
226
|
end
|
|
231
227
|
|
|
232
228
|
def unregister_connection(connection)
|