httpx 1.1.4 → 1.2.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 +5 -5
- data/doc/release_notes/1_1_4.md +1 -1
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/lib/httpx/adapters/webmock.rb +29 -8
- data/lib/httpx/altsvc.rb +57 -2
- data/lib/httpx/chainable.rb +40 -29
- data/lib/httpx/connection/http1.rb +27 -22
- data/lib/httpx/connection/http2.rb +7 -3
- data/lib/httpx/connection.rb +45 -60
- data/lib/httpx/extensions.rb +0 -15
- data/lib/httpx/options.rb +84 -27
- data/lib/httpx/plugins/aws_sigv4.rb +2 -2
- data/lib/httpx/plugins/basic_auth.rb +1 -1
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +19 -9
- data/lib/httpx/plugins/digest_auth.rb +1 -1
- data/lib/httpx/plugins/follow_redirects.rb +11 -0
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -0
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/h2c.rb +20 -8
- data/lib/httpx/plugins/proxy/socks4.rb +2 -2
- data/lib/httpx/plugins/proxy/socks5.rb +2 -2
- data/lib/httpx/plugins/proxy.rb +14 -32
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +4 -0
- data/lib/httpx/plugins/ssrf_filter.rb +142 -0
- data/lib/httpx/plugins/stream.rb +9 -4
- data/lib/httpx/plugins/upgrade/h2.rb +1 -1
- data/lib/httpx/plugins/upgrade.rb +1 -1
- data/lib/httpx/plugins/webdav.rb +1 -1
- data/lib/httpx/pool.rb +32 -28
- data/lib/httpx/request/body.rb +3 -3
- data/lib/httpx/request.rb +12 -3
- data/lib/httpx/resolver/https.rb +3 -2
- data/lib/httpx/resolver/native.rb +1 -0
- data/lib/httpx/resolver/resolver.rb +17 -6
- data/lib/httpx/response.rb +1 -1
- data/lib/httpx/session.rb +13 -82
- data/lib/httpx/timers.rb +3 -10
- data/lib/httpx/transcoder.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +33 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -1
- data/sig/connection.rbs +16 -16
- data/sig/options.rbs +10 -2
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/follow_redirects.rbs +2 -0
- data/sig/plugins/proxy/socks4.rbs +2 -1
- data/sig/plugins/proxy/socks5.rbs +2 -1
- data/sig/plugins/proxy.rbs +11 -1
- data/sig/plugins/stream.rbs +24 -22
- data/sig/pool.rbs +1 -3
- data/sig/resolver/resolver.rbs +3 -1
- data/sig/session.rbs +4 -4
- metadata +12 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3df88a59256b85aacf9d98578c965626b3bfa4611695ae21a707252df71cb0a
|
4
|
+
data.tar.gz: f1616869724c217272d0dc165130548edc8ca35eadd97ab42d5a17012a0019d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4ca8efc0a1ffe3dec2d2d6ee345bcb58bc2eec9e4c5b4fe951a889041be8eeaeb4b005d9771f0e712f161dad69f8de25d07d520c3e0cd17ae1574f53fadace1
|
7
|
+
data.tar.gz: 509c48d74aafb520e3142c5f9b2356e2f36bf9d649eb723ca07a2863077057ab12ae2e17dbf61f13149eac4f80201252dad9b46cb904f5664b8910b62a868c1d
|
data/README.md
CHANGED
@@ -61,7 +61,7 @@ puts body #=> #<HTTPX::Response ...
|
|
61
61
|
You can also send as many requests as you want simultaneously:
|
62
62
|
|
63
63
|
```ruby
|
64
|
-
page1, page2, page3
|
64
|
+
page1, page2, page3 = HTTPX.get("https://news.ycombinator.com/news", "https://news.ycombinator.com/news?p=2", "https://news.ycombinator.com/news?p=3")
|
65
65
|
```
|
66
66
|
|
67
67
|
## Installation
|
@@ -107,22 +107,22 @@ HTTPX.get(
|
|
107
107
|
|
108
108
|
```ruby
|
109
109
|
response = HTTPX.get("https://www.google.com", params: { q: "me" })
|
110
|
-
response = HTTPX.post("https://www.nghttp2.org/httpbin/post", form: {name: "John", age: "22"})
|
110
|
+
response = HTTPX.post("https://www.nghttp2.org/httpbin/post", form: { name: "John", age: "22" })
|
111
111
|
response = HTTPX.plugin(:basic_auth)
|
112
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
|
116
|
-
http = HTTPX.plugin(:expect).with(headers: { "x-pvt-token" => "TOKEN"})
|
116
|
+
http = HTTPX.plugin(:expect).with(headers: { "x-pvt-token" => "TOKEN" })
|
117
117
|
http.get("https://example.com") # the above options will apply
|
118
|
-
http.post("https://example2.com",
|
118
|
+
http.post("https://example2.com", form: { name: "John", age: "22" }) # same, plus the form POST body
|
119
119
|
```
|
120
120
|
|
121
121
|
### Lightweight
|
122
122
|
|
123
123
|
It ships with most features published as a plugin, making vanilla `httpx` lightweight and dependency-free, while allowing you to "pay for what you use"
|
124
124
|
|
125
|
-
The plugin system is similar to the ones used by [sequel](https://github.com/jeremyevans/sequel), [roda](https://github.com/jeremyevans/roda) or [shrine](https://github.com/
|
125
|
+
The plugin system is similar to the ones used by [sequel](https://github.com/jeremyevans/sequel), [roda](https://github.com/jeremyevans/roda) or [shrine](https://github.com/shrinerb/shrine).
|
126
126
|
|
127
127
|
### Advanced DNS features
|
128
128
|
|
data/doc/release_notes/1_1_4.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# 1.1.4
|
2
2
|
|
3
|
-
##
|
3
|
+
## bugfixes
|
4
4
|
|
5
5
|
* datadog adapter: use `Gem::Version` to invoke the correct configuration API.
|
6
6
|
* stream plugin: do not preempt request enqueuing (this was making integration with the `:follow_redirects` plugin fail when set up with `webmock`).
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# 1.1.5
|
2
|
+
|
3
|
+
## improvements
|
4
|
+
|
5
|
+
* pattern matching support for responses has been backported to ruby 2.7 as well.
|
6
|
+
|
7
|
+
## bugfixes
|
8
|
+
|
9
|
+
* `stream` plugin: fix for `HTTPX::StreamResponse#each_line` not yielding the last line of the payload when not delimiter-terminated.
|
10
|
+
* `stream` plugin: fix `webmock` adapter integration when methods calls would happen in the `HTTPX::StreamResponse#each` block.
|
11
|
+
* `stream` plugin: fix `:follow_redirects` plugin integration which was caching the redirect response and using it for method calls inside the `HTTPX::StreamResponse#each` block.
|
12
|
+
* "103 early hints" responses will be ignored when processing the response (it was causing the response returned by sesssions to hold its headers, instead of the following 200 response, while keeping the 200 response body).
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# 1.2.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### `:ssrf_filter` plugin
|
6
|
+
|
7
|
+
The `:ssrf_filter` p plugin prevents server-side request forgery attacks, by blocking requests to the internal network. This is useful when the URLs used to perform requests aren’t under the developer control (such as when they are inserted via a web application form).
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
http = HTTPX.plugin(:ssrf_filter)
|
11
|
+
|
12
|
+
# this works
|
13
|
+
response = http.get("https://example.com")
|
14
|
+
|
15
|
+
# this doesn't
|
16
|
+
response = http.get("http://localhost:3002")
|
17
|
+
response = http.get("http://[::1]:3002")
|
18
|
+
response = http.get("http://169.254.169.254/latest/meta-data/")
|
19
|
+
```
|
20
|
+
|
21
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/SSRF-Filter
|
22
|
+
|
23
|
+
### `:callbacks` plugin
|
24
|
+
|
25
|
+
The session callbacks introduced in v0.24.0 are in its own plugin. Older code will still work and emit a deprecation warning.
|
26
|
+
|
27
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Callbacks
|
28
|
+
|
29
|
+
### `:redirect_on` option for `:follow_redirects` plugin
|
30
|
+
|
31
|
+
This option allows passing a callback which, when returning `false`, can interrupt the redirect loop.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
http = HTTPX.plugin(:follow_redirects).with(redirect_on: ->(location_uri) { BLACKLIST_HOSTS.include?(location_uri.host) ]
|
35
|
+
```
|
36
|
+
|
37
|
+
### `:close_on_handshake_timeout` timeout
|
38
|
+
|
39
|
+
A new `:timeout` option, `:close_handshake_timeout`, is added, which monitors connection readiness when performing HTTP/2 connection termination handshake.
|
40
|
+
|
41
|
+
## Improvements
|
42
|
+
|
43
|
+
* Internal "eden connections" concept was removed, and connection objects are now kept-and-reused during the lifetime of a session, even when closed. This simplified connectio pool implementation and improved performance.
|
44
|
+
* request using `:proxy` and `:retries` plugin enabled sessions will now retry on proxy connection establishment related errors.
|
45
|
+
|
46
|
+
## Bugfixes
|
47
|
+
|
48
|
+
* webmock adapter: mocked responses storing decoded payloads won't try to decode them again (fixes vcr/webmock integrations).
|
49
|
+
* webmock adapter: fix issue related with making real requests over webmock-enabled connection.
|
@@ -38,12 +38,12 @@ module WebMock
|
|
38
38
|
|
39
39
|
return build_error_response(request, webmock_response.exception) if webmock_response.exception
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
request.options.response_class.new(request,
|
42
|
+
webmock_response.status[0],
|
43
|
+
"2.0",
|
44
|
+
webmock_response.headers).tap do |res|
|
45
|
+
res.mocked = true
|
46
|
+
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def build_error_response(request, exception)
|
@@ -52,16 +52,36 @@ module WebMock
|
|
52
52
|
end
|
53
53
|
|
54
54
|
module InstanceMethods
|
55
|
-
def
|
55
|
+
def init_connection(*)
|
56
56
|
connection = super
|
57
57
|
connection.once(:unmock_connection) do
|
58
|
+
unless connection.addresses
|
59
|
+
connection.__send__(:callbacks)[:connect_error].clear
|
60
|
+
pool.__send__(:unregister_connection, connection)
|
61
|
+
end
|
58
62
|
pool.__send__(:resolve_connection, connection)
|
59
|
-
pool.__send__(:unregister_connection, connection) unless connection.addresses
|
60
63
|
end
|
61
64
|
connection
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
68
|
+
module ResponseMethods
|
69
|
+
attr_accessor :mocked
|
70
|
+
|
71
|
+
def initialize(*)
|
72
|
+
super
|
73
|
+
@mocked = false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module ResponseBodyMethods
|
78
|
+
def decode_chunk(chunk)
|
79
|
+
return chunk if @response.mocked
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
65
85
|
module ConnectionMethods
|
66
86
|
def initialize(*)
|
67
87
|
super
|
@@ -90,6 +110,7 @@ module WebMock
|
|
90
110
|
log { "mocking #{request.uri} with #{mock_response.inspect}" }
|
91
111
|
request.response = response
|
92
112
|
request.emit(:response, response)
|
113
|
+
response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
|
93
114
|
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
94
115
|
if WebMock::CallbackRegistry.any_callbacks?
|
95
116
|
request.on(:response) do |resp|
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -4,6 +4,58 @@ require "strscan"
|
|
4
4
|
|
5
5
|
module HTTPX
|
6
6
|
module AltSvc
|
7
|
+
# makes connections able to accept requests destined to primary service.
|
8
|
+
module ConnectionMixin
|
9
|
+
using URIExtensions
|
10
|
+
|
11
|
+
def send(request)
|
12
|
+
request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def match?(uri, options)
|
18
|
+
return false if !used? && (@state == :closing || @state == :closed)
|
19
|
+
|
20
|
+
match_altsvcs?(uri) && match_altsvc_options?(uri, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# checks if this is connection is an alternative service of
|
26
|
+
# +uri+
|
27
|
+
def match_altsvcs?(uri)
|
28
|
+
@origins.any? { |origin| altsvc_match?(uri, origin) } ||
|
29
|
+
AltSvc.cached_altsvc(@origin).any? do |altsvc|
|
30
|
+
origin = altsvc["origin"]
|
31
|
+
altsvc_match?(origin, uri.origin)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def match_altsvc_options?(uri, options)
|
36
|
+
return @options == options unless @options.ssl.all? do |k, v|
|
37
|
+
v == (k == :hostname ? uri.host : options.ssl[k])
|
38
|
+
end
|
39
|
+
|
40
|
+
@options.options_equals?(options, Options::REQUEST_BODY_IVARS + %i[@ssl])
|
41
|
+
end
|
42
|
+
|
43
|
+
def altsvc_match?(uri, other_uri)
|
44
|
+
other_uri = URI(other_uri)
|
45
|
+
|
46
|
+
uri.origin == other_uri.origin || begin
|
47
|
+
case uri.scheme
|
48
|
+
when "h2"
|
49
|
+
(other_uri.scheme == "https" || other_uri.scheme == "h2") &&
|
50
|
+
uri.host == other_uri.host &&
|
51
|
+
uri.port == other_uri.port
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
7
59
|
@altsvc_mutex = Thread::Mutex.new
|
8
60
|
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
9
61
|
|
@@ -46,7 +98,7 @@ module HTTPX
|
|
46
98
|
|
47
99
|
altsvc = response.headers["alt-svc"]
|
48
100
|
|
49
|
-
# https://
|
101
|
+
# https://datatracker.ietf.org/doc/html/rfc7838#section-3
|
50
102
|
# A field value containing the special value "clear" indicates that the
|
51
103
|
# origin requests all alternatives for that origin to be invalidated
|
52
104
|
# (including those specified in the same response, in case of an
|
@@ -99,7 +151,10 @@ module HTTPX
|
|
99
151
|
end
|
100
152
|
|
101
153
|
def parse_altsvc_origin(alt_proto, alt_origin)
|
102
|
-
alt_scheme = parse_altsvc_scheme(alt_proto)
|
154
|
+
alt_scheme = parse_altsvc_scheme(alt_proto)
|
155
|
+
|
156
|
+
return unless alt_scheme
|
157
|
+
|
103
158
|
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
104
159
|
|
105
160
|
URI.parse("#{alt_scheme}://#{alt_origin}")
|
data/lib/httpx/chainable.rb
CHANGED
@@ -10,19 +10,6 @@ module HTTPX
|
|
10
10
|
MOD
|
11
11
|
end
|
12
12
|
|
13
|
-
%i[
|
14
|
-
connection_opened connection_closed
|
15
|
-
request_error
|
16
|
-
request_started request_body_chunk request_completed
|
17
|
-
response_started response_body_chunk response_completed
|
18
|
-
].each do |meth|
|
19
|
-
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
20
|
-
def on_#{meth}(&blk) # def on_connection_opened(&blk)
|
21
|
-
on(:#{meth}, &blk) # on(:connection_opened, &blk)
|
22
|
-
end # end
|
23
|
-
MOD
|
24
|
-
end
|
25
|
-
|
26
13
|
def request(*args, **options)
|
27
14
|
branch(default_options).request(*args, **options)
|
28
15
|
end
|
@@ -46,12 +33,6 @@ module HTTPX
|
|
46
33
|
branch(default_options.merge(options), &blk)
|
47
34
|
end
|
48
35
|
|
49
|
-
protected
|
50
|
-
|
51
|
-
def on(*args, &blk)
|
52
|
-
branch(default_options).on(*args, &blk)
|
53
|
-
end
|
54
|
-
|
55
36
|
private
|
56
37
|
|
57
38
|
def default_options
|
@@ -64,22 +45,52 @@ module HTTPX
|
|
64
45
|
Session.new(options, &blk)
|
65
46
|
end
|
66
47
|
|
67
|
-
def method_missing(meth, *args, **options)
|
68
|
-
|
48
|
+
def method_missing(meth, *args, **options, &blk)
|
49
|
+
case meth
|
50
|
+
when /\Awith_(.+)/
|
69
51
|
|
70
|
-
|
52
|
+
option = Regexp.last_match(1)
|
71
53
|
|
72
|
-
|
54
|
+
return super unless option
|
73
55
|
|
74
|
-
|
75
|
-
|
56
|
+
with(option.to_sym => args.first || options)
|
57
|
+
when /\Aon_(.+)/
|
58
|
+
callback = Regexp.last_match(1)
|
76
59
|
|
77
|
-
|
78
|
-
|
60
|
+
return super unless %w[
|
61
|
+
connection_opened connection_closed
|
62
|
+
request_error
|
63
|
+
request_started request_body_chunk request_completed
|
64
|
+
response_started response_body_chunk response_completed
|
65
|
+
].include?(callback)
|
79
66
|
|
80
|
-
|
67
|
+
warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
|
68
|
+
"Use HTTPX.plugin(:callbacks).#{meth} instead."
|
81
69
|
|
82
|
-
|
70
|
+
plugin(:callbacks).__send__(meth, *args, **options, &blk)
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def respond_to_missing?(meth, *)
|
77
|
+
case meth
|
78
|
+
when /\Awith_(.+)/
|
79
|
+
option = Regexp.last_match(1)
|
80
|
+
|
81
|
+
default_options.respond_to?(option) || super
|
82
|
+
when /\Aon_(.+)/
|
83
|
+
callback = Regexp.last_match(1)
|
84
|
+
|
85
|
+
%w[
|
86
|
+
connection_opened connection_closed
|
87
|
+
request_error
|
88
|
+
request_started request_body_chunk request_completed
|
89
|
+
response_started response_body_chunk response_completed
|
90
|
+
].include?(callback) || super
|
91
|
+
else
|
92
|
+
super
|
93
|
+
end
|
83
94
|
end
|
84
95
|
end
|
85
96
|
end
|
@@ -12,6 +12,8 @@ module HTTPX
|
|
12
12
|
|
13
13
|
attr_reader :pending, :requests
|
14
14
|
|
15
|
+
attr_accessor :max_concurrent_requests
|
16
|
+
|
15
17
|
def initialize(buffer, options)
|
16
18
|
@options = Options.new(options)
|
17
19
|
@max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
|
@@ -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
|
@@ -218,6 +221,7 @@ module HTTPX
|
|
218
221
|
end
|
219
222
|
|
220
223
|
def ping
|
224
|
+
reset
|
221
225
|
emit(:reset)
|
222
226
|
emit(:exhausted)
|
223
227
|
end
|
@@ -262,6 +266,7 @@ module HTTPX
|
|
262
266
|
|
263
267
|
def disable
|
264
268
|
disable_pipelining
|
269
|
+
reset
|
265
270
|
emit(:reset)
|
266
271
|
throw(:called)
|
267
272
|
end
|
@@ -292,29 +297,31 @@ module HTTPX
|
|
292
297
|
request.body.chunk!
|
293
298
|
end
|
294
299
|
|
295
|
-
|
300
|
+
extra_headers = {}
|
296
301
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
# when it's not a persistent connection, it sets "Connection: close" always
|
307
|
-
# on the last request of the possible batch (either allowed max requests,
|
308
|
-
# or if smaller, the size of the batch itself)
|
309
|
-
requests_limit = [@max_requests, @requests.size].min
|
310
|
-
if request == @requests[requests_limit - 1]
|
311
|
-
"close"
|
302
|
+
unless request.headers.key?("connection")
|
303
|
+
connection_value = if request.persistent?
|
304
|
+
# when in a persistent connection, the request can't be at
|
305
|
+
# the edge of a renegotiation
|
306
|
+
if @requests.index(request) + 1 < @max_requests
|
307
|
+
"keep-alive"
|
308
|
+
else
|
309
|
+
"close"
|
310
|
+
end
|
312
311
|
else
|
313
|
-
"
|
312
|
+
# when it's not a persistent connection, it sets "Connection: close" always
|
313
|
+
# on the last request of the possible batch (either allowed max requests,
|
314
|
+
# or if smaller, the size of the batch itself)
|
315
|
+
requests_limit = [@max_requests, @requests.size].min
|
316
|
+
if request == @requests[requests_limit - 1]
|
317
|
+
"close"
|
318
|
+
else
|
319
|
+
"keep-alive"
|
320
|
+
end
|
314
321
|
end
|
315
|
-
end
|
316
322
|
|
317
|
-
|
323
|
+
extra_headers["connection"] = connection_value
|
324
|
+
end
|
318
325
|
extra_headers["host"] = request.authority unless request.headers.key?("host")
|
319
326
|
extra_headers
|
320
327
|
end
|
@@ -370,12 +377,10 @@ module HTTPX
|
|
370
377
|
end
|
371
378
|
|
372
379
|
def join_headers2(headers)
|
373
|
-
buffer = "".b
|
374
380
|
headers.each do |field, value|
|
375
|
-
buffer
|
381
|
+
buffer = "#{capitalized(field)}: #{value}#{CRLF}"
|
376
382
|
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
377
383
|
@buffer << buffer
|
378
|
-
buffer.clear
|
379
384
|
end
|
380
385
|
end
|
381
386
|
|
@@ -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?
|
@@ -147,6 +150,7 @@ module HTTPX
|
|
147
150
|
|
148
151
|
def send_pending
|
149
152
|
while (request = @pending.shift)
|
153
|
+
# TODO: this request should go back to top of stack
|
150
154
|
break unless send(request)
|
151
155
|
end
|
152
156
|
end
|