httpx 0.22.5 → 0.23.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/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/sentry.rb +4 -4
- data/lib/httpx/adapters/webmock.rb +2 -2
- data/lib/httpx/buffer.rb +4 -0
- data/lib/httpx/chainable.rb +4 -4
- data/lib/httpx/connection/http1.rb +2 -2
- data/lib/httpx/connection/http2.rb +2 -3
- data/lib/httpx/connection.rb +29 -10
- data/lib/httpx/io/udp.rb +2 -0
- data/lib/httpx/io/unix.rb +1 -5
- data/lib/httpx/io.rb +0 -10
- data/lib/httpx/options.rb +16 -2
- data/lib/httpx/plugins/authentication/digest.rb +1 -1
- data/lib/httpx/plugins/aws_sdk_authentication.rb +1 -3
- data/lib/httpx/plugins/aws_sigv4.rb +1 -1
- data/lib/httpx/plugins/compression/brotli.rb +4 -4
- data/lib/httpx/plugins/compression/deflate.rb +12 -7
- data/lib/httpx/plugins/compression/gzip.rb +7 -5
- data/lib/httpx/plugins/compression.rb +9 -8
- data/lib/httpx/plugins/digest_authentication.rb +1 -4
- data/lib/httpx/plugins/follow_redirects.rb +1 -1
- data/lib/httpx/plugins/grpc/message.rb +3 -1
- data/lib/httpx/plugins/grpc.rb +3 -3
- data/lib/httpx/plugins/h2c.rb +5 -9
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/multipart.rb +14 -2
- data/lib/httpx/plugins/proxy/http.rb +4 -4
- data/lib/httpx/plugins/proxy.rb +65 -31
- data/lib/httpx/plugins/response_cache.rb +2 -2
- data/lib/httpx/plugins/retries.rb +49 -2
- data/lib/httpx/plugins/upgrade/h2.rb +3 -3
- data/lib/httpx/plugins/upgrade.rb +4 -7
- data/lib/httpx/plugins/webdav.rb +7 -7
- data/lib/httpx/pool.rb +1 -1
- data/lib/httpx/request.rb +23 -13
- data/lib/httpx/resolver/https.rb +37 -20
- data/lib/httpx/resolver/native.rb +131 -35
- data/lib/httpx/resolver.rb +26 -14
- data/lib/httpx/response.rb +14 -16
- data/lib/httpx/session.rb +1 -0
- data/lib/httpx/transcoder/body.rb +0 -1
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/form.rb +0 -1
- data/lib/httpx/transcoder/json.rb +0 -1
- data/lib/httpx/transcoder/xml.rb +0 -1
- data/lib/httpx/transcoder.rb +0 -2
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +0 -1
- data/sig/buffer.rbs +1 -0
- data/sig/chainable.rbs +3 -3
- data/sig/connection.rbs +17 -6
- data/sig/errors.rbs +9 -0
- data/sig/httpx.rbs +3 -3
- data/sig/io/ssl.rbs +17 -0
- data/sig/io/tcp.rbs +57 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +10 -0
- data/sig/options.rbs +5 -1
- data/sig/plugins/compression.rbs +6 -2
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/grpc.rbs +3 -3
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/proxy.rbs +1 -5
- data/sig/plugins/response_cache.rbs +1 -1
- data/sig/plugins/retries.rbs +28 -8
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/request.rbs +6 -2
- data/sig/resolver/https.rbs +3 -1
- data/sig/resolver/native.rbs +7 -2
- data/sig/resolver/resolver.rbs +0 -2
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +8 -4
- data/sig/response.rbs +6 -2
- data/sig/session.rbs +10 -10
- data/sig/transcoder/xml.rbs +1 -1
- data/sig/transcoder.rbs +4 -5
- metadata +11 -5
- data/lib/httpx/registry.rb +0 -85
- data/sig/registry.rbs +0 -13
@@ -32,6 +32,15 @@ module HTTPX
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
module NativeResolverMethods
|
36
|
+
def transition(nextstate)
|
37
|
+
state = @state
|
38
|
+
val = super
|
39
|
+
meter_elapsed_time("Resolver::Native: #{state} -> #{nextstate}")
|
40
|
+
val
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
module InstanceMethods
|
36
45
|
def self.included(klass)
|
37
46
|
klass.prepend TrackTimeMethods
|
@@ -42,6 +51,13 @@ module HTTPX
|
|
42
51
|
meter_elapsed_time("Session: initializing...")
|
43
52
|
super
|
44
53
|
meter_elapsed_time("Session: initialized!!!")
|
54
|
+
resolver_type = @options.resolver_class
|
55
|
+
resolver_type = Resolver.resolver_for(resolver_type)
|
56
|
+
return unless resolver_type <= Resolver::Native
|
57
|
+
|
58
|
+
resolver_type.prepend TrackTimeMethods
|
59
|
+
resolver_type.prepend NativeResolverMethods
|
60
|
+
@options = @options.merge(resolver_class: resolver_type)
|
45
61
|
end
|
46
62
|
|
47
63
|
def close(*)
|
@@ -40,9 +40,21 @@ module HTTPX
|
|
40
40
|
require "httpx/plugins/multipart/part"
|
41
41
|
require "httpx/plugins/multipart/mime_type_detector"
|
42
42
|
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module RequestBodyMethods
|
46
|
+
private
|
47
|
+
|
48
|
+
def initialize_body(options)
|
49
|
+
return FormTranscoder.encode(options.form) if options.form
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
43
54
|
|
44
|
-
|
45
|
-
|
55
|
+
module ResponseMethods
|
56
|
+
def form
|
57
|
+
decode(FormTranscoder)
|
46
58
|
end
|
47
59
|
end
|
48
60
|
|
@@ -61,7 +61,7 @@ module HTTPX
|
|
61
61
|
return unless @io.connected?
|
62
62
|
|
63
63
|
@parser || begin
|
64
|
-
@parser =
|
64
|
+
@parser = self.class.parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
65
65
|
parser = @parser
|
66
66
|
parser.extend(ProxyParser)
|
67
67
|
parser.on(:response, &method(:__http_on_connect))
|
@@ -141,9 +141,9 @@ module HTTPX
|
|
141
141
|
|
142
142
|
module ProxyParser
|
143
143
|
def join_headline(request)
|
144
|
-
return super if request.verb ==
|
144
|
+
return super if request.verb == "CONNECT"
|
145
145
|
|
146
|
-
"#{request.verb
|
146
|
+
"#{request.verb} #{request.uri} HTTP/#{@version.join(".")}"
|
147
147
|
end
|
148
148
|
|
149
149
|
def set_protocol_headers(request)
|
@@ -161,7 +161,7 @@ module HTTPX
|
|
161
161
|
|
162
162
|
class ConnectRequest < Request
|
163
163
|
def initialize(uri, _options)
|
164
|
-
super(
|
164
|
+
super("CONNECT", uri, {})
|
165
165
|
@headers.delete("accept")
|
166
166
|
end
|
167
167
|
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -18,6 +18,43 @@ module HTTPX
|
|
18
18
|
Error = HTTPProxyError
|
19
19
|
PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
|
20
20
|
|
21
|
+
class << self
|
22
|
+
def configure(klass)
|
23
|
+
klass.plugin(:"proxy/http")
|
24
|
+
klass.plugin(:"proxy/socks4")
|
25
|
+
klass.plugin(:"proxy/socks5")
|
26
|
+
end
|
27
|
+
|
28
|
+
if URI::Generic.methods.include?(:use_proxy?)
|
29
|
+
def use_proxy?(*args)
|
30
|
+
URI::Generic.use_proxy?(*args)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
# https://github.com/ruby/uri/blob/ae07f956a4bea00b4f54a75bd40b8fa918103eed/lib/uri/generic.rb
|
34
|
+
def use_proxy?(hostname, addr, port, no_proxy)
|
35
|
+
hostname = hostname.downcase
|
36
|
+
dothostname = ".#{hostname}"
|
37
|
+
no_proxy.scan(/([^:,\s]+)(?::(\d+))?/) do |p_host, p_port|
|
38
|
+
if !p_port || port == p_port.to_i
|
39
|
+
if p_host.start_with?(".")
|
40
|
+
return false if hostname.end_with?(p_host.downcase)
|
41
|
+
else
|
42
|
+
return false if dothostname.end_with?(".#{p_host.downcase}")
|
43
|
+
end
|
44
|
+
if addr
|
45
|
+
begin
|
46
|
+
return false if IPAddr.new(p_host).include?(addr)
|
47
|
+
rescue IPAddr::InvalidAddressError
|
48
|
+
next
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
21
58
|
class Parameters
|
22
59
|
attr_reader :uri, :username, :password, :scheme
|
23
60
|
|
@@ -77,14 +114,6 @@ module HTTPX
|
|
77
114
|
end
|
78
115
|
end
|
79
116
|
|
80
|
-
class << self
|
81
|
-
def configure(klass)
|
82
|
-
klass.plugin(:"proxy/http")
|
83
|
-
klass.plugin(:"proxy/socks4")
|
84
|
-
klass.plugin(:"proxy/socks5")
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
117
|
module OptionsMethods
|
89
118
|
def option_proxy(value)
|
90
119
|
value.is_a?(Parameters) ? value : Hash[value]
|
@@ -94,34 +123,39 @@ module HTTPX
|
|
94
123
|
module InstanceMethods
|
95
124
|
private
|
96
125
|
|
97
|
-
def
|
98
|
-
|
99
|
-
uris = options.proxy ? Array(options.proxy[:uri]) : []
|
100
|
-
if uris.empty?
|
101
|
-
uri = URI(uri).find_proxy
|
102
|
-
uris << uri if uri
|
103
|
-
end
|
104
|
-
uris
|
105
|
-
end
|
106
|
-
return if @_proxy_uris.empty?
|
126
|
+
def find_connection(request, connections, options)
|
127
|
+
return super unless options.respond_to?(:proxy)
|
107
128
|
|
108
|
-
|
129
|
+
uri = URI(request.uri)
|
109
130
|
|
110
|
-
|
131
|
+
proxy_opts = if (next_proxy = uri.find_proxy)
|
132
|
+
{ uri: next_proxy }
|
133
|
+
else
|
134
|
+
proxy = options.proxy
|
111
135
|
|
112
|
-
|
113
|
-
proxy_opts = options.proxy.merge(proxy_opts) if options.proxy
|
114
|
-
proxy_opts
|
115
|
-
end
|
136
|
+
return super unless proxy
|
116
137
|
|
117
|
-
|
118
|
-
return super unless options.respond_to?(:proxy)
|
138
|
+
return super(request, connections, options.merge(proxy: nil)) unless proxy.key?(:uri)
|
119
139
|
|
120
|
-
|
121
|
-
|
122
|
-
|
140
|
+
@_proxy_uris ||= Array(proxy[:uri])
|
141
|
+
|
142
|
+
next_proxy = @_proxy_uris.first
|
143
|
+
raise Error, "Failed to connect to proxy" unless next_proxy
|
144
|
+
|
145
|
+
if proxy.key?(:no_proxy)
|
146
|
+
next_proxy = URI(next_proxy)
|
147
|
+
|
148
|
+
no_proxy = proxy[:no_proxy]
|
149
|
+
no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
|
150
|
+
|
151
|
+
return super(request, connections, options.merge(proxy: nil)) unless Proxy.use_proxy?(uri.host, next_proxy.host,
|
152
|
+
next_proxy.port, no_proxy)
|
153
|
+
end
|
154
|
+
|
155
|
+
proxy.merge(uri: next_proxy)
|
156
|
+
end
|
123
157
|
|
124
|
-
proxy = Parameters.new(**
|
158
|
+
proxy = Parameters.new(**proxy_opts)
|
125
159
|
|
126
160
|
proxy_options = options.merge(proxy: proxy)
|
127
161
|
connection = pool.find_connection(uri, proxy_options) || build_connection(uri, proxy_options)
|
@@ -284,7 +318,7 @@ module HTTPX
|
|
284
318
|
register_plugin :proxy, Proxy
|
285
319
|
end
|
286
320
|
|
287
|
-
class ProxySSL <
|
321
|
+
class ProxySSL < SSL
|
288
322
|
def initialize(tcp, request_uri, options)
|
289
323
|
@io = tcp.to_io
|
290
324
|
super(request_uri, tcp.addresses, options)
|
@@ -8,7 +8,7 @@ module HTTPX
|
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/Response-Cache
|
9
9
|
#
|
10
10
|
module ResponseCache
|
11
|
-
CACHEABLE_VERBS = %
|
11
|
+
CACHEABLE_VERBS = %w[GET HEAD].freeze
|
12
12
|
CACHEABLE_STATUS_CODES = [200, 203, 206, 300, 301, 410].freeze
|
13
13
|
private_constant :CACHEABLE_VERBS
|
14
14
|
private_constant :CACHEABLE_STATUS_CODES
|
@@ -96,7 +96,7 @@ module HTTPX
|
|
96
96
|
|
97
97
|
module RequestMethods
|
98
98
|
def response_cache_key
|
99
|
-
@response_cache_key ||= Digest::SHA1.hexdigest("httpx-response-cache-#{@verb}
|
99
|
+
@response_cache_key ||= Digest::SHA1.hexdigest("httpx-response-cache-#{@verb}-#{@uri}")
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -11,7 +11,7 @@ module HTTPX
|
|
11
11
|
MAX_RETRIES = 3
|
12
12
|
# TODO: pass max_retries in a configure/load block
|
13
13
|
|
14
|
-
IDEMPOTENT_METHODS = %
|
14
|
+
IDEMPOTENT_METHODS = %w[GET OPTIONS HEAD PUT DELETE].freeze
|
15
15
|
RETRYABLE_ERRORS = [
|
16
16
|
IOError,
|
17
17
|
EOFError,
|
@@ -96,7 +96,7 @@ module HTTPX
|
|
96
96
|
)
|
97
97
|
# rubocop:enable Style/MultilineTernaryOperator
|
98
98
|
)
|
99
|
-
|
99
|
+
__try_partial_retry(request, response)
|
100
100
|
log { "failed to get response, #{request.retries} tries to go..." }
|
101
101
|
request.retries -= 1
|
102
102
|
request.transition(:idle)
|
@@ -134,15 +134,62 @@ module HTTPX
|
|
134
134
|
def __retryable_error?(ex)
|
135
135
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) }
|
136
136
|
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Atttempt to set the request to perform a partial range request.
|
140
|
+
# This happens if the peer server accepts byte-range requests, and
|
141
|
+
# the last response contains some body payload.
|
142
|
+
#
|
143
|
+
def __try_partial_retry(request, response)
|
144
|
+
response = response.response if response.is_a?(ErrorResponse)
|
145
|
+
|
146
|
+
return unless response
|
147
|
+
|
148
|
+
unless response.headers.key?("accept-ranges") &&
|
149
|
+
response.headers["accept-ranges"] == "bytes" && # there's nothing else supported though...
|
150
|
+
(original_body = response.body)
|
151
|
+
response.close if response.respond_to?(:close)
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
request.partial_response = response
|
156
|
+
|
157
|
+
size = original_body.bytesize
|
158
|
+
|
159
|
+
request.headers["range"] = "bytes=#{size}-"
|
160
|
+
end
|
137
161
|
end
|
138
162
|
|
139
163
|
module RequestMethods
|
140
164
|
attr_accessor :retries
|
141
165
|
|
166
|
+
attr_writer :partial_response
|
167
|
+
|
142
168
|
def initialize(*args)
|
143
169
|
super
|
144
170
|
@retries = @options.max_retries
|
145
171
|
end
|
172
|
+
|
173
|
+
def response=(response)
|
174
|
+
if @partial_response
|
175
|
+
if response.is_a?(Response) && response.status == 206
|
176
|
+
response.from_partial_response(@partial_response)
|
177
|
+
else
|
178
|
+
@partial_response.close
|
179
|
+
end
|
180
|
+
@partial_response = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
super
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
module ResponseMethods
|
188
|
+
def from_partial_response(response)
|
189
|
+
@status = response.status
|
190
|
+
@headers = response.headers
|
191
|
+
@body = response.body
|
192
|
+
end
|
146
193
|
end
|
147
194
|
end
|
148
195
|
register_plugin :retries, Retries
|
@@ -10,8 +10,8 @@ module HTTPX
|
|
10
10
|
#
|
11
11
|
module H2
|
12
12
|
class << self
|
13
|
-
def
|
14
|
-
|
13
|
+
def extra_options(options)
|
14
|
+
options.merge(upgrade_handlers: options.upgrade_handlers.merge("h2" => self))
|
15
15
|
end
|
16
16
|
|
17
17
|
def call(connection, _request, _response)
|
@@ -32,7 +32,7 @@ module HTTPX
|
|
32
32
|
|
33
33
|
@parser = Connection::HTTP2.new(@write_buffer, @options)
|
34
34
|
set_parser_callbacks(@parser)
|
35
|
-
@upgrade_protocol =
|
35
|
+
@upgrade_protocol = "h2"
|
36
36
|
|
37
37
|
# what's happening here:
|
38
38
|
# a deviation from the state machine is done to perform the actions when a
|
@@ -15,16 +15,13 @@ module HTTPX
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def extra_options(options)
|
18
|
-
upgrade_handlers
|
19
|
-
extend Registry
|
20
|
-
end
|
21
|
-
options.merge(upgrade_handlers: upgrade_handlers)
|
18
|
+
options.merge(upgrade_handlers: {})
|
22
19
|
end
|
23
20
|
end
|
24
21
|
|
25
22
|
module OptionsMethods
|
26
23
|
def option_upgrade_handlers(value)
|
27
|
-
raise TypeError, ":upgrade_handlers must be a
|
24
|
+
raise TypeError, ":upgrade_handlers must be a Hash" unless value.is_a?(Hash)
|
28
25
|
|
29
26
|
value
|
30
27
|
end
|
@@ -41,9 +38,9 @@ module HTTPX
|
|
41
38
|
|
42
39
|
upgrade_protocol = response.headers["upgrade"].split(/ *, */).first
|
43
40
|
|
44
|
-
return response unless upgrade_protocol && options.upgrade_handlers.
|
41
|
+
return response unless upgrade_protocol && options.upgrade_handlers.key?(upgrade_protocol)
|
45
42
|
|
46
|
-
protocol_handler = options.upgrade_handlers
|
43
|
+
protocol_handler = options.upgrade_handlers[upgrade_protocol]
|
47
44
|
|
48
45
|
return response unless protocol_handler
|
49
46
|
|
data/lib/httpx/plugins/webdav.rb
CHANGED
@@ -10,11 +10,11 @@ module HTTPX
|
|
10
10
|
module WebDav
|
11
11
|
module InstanceMethods
|
12
12
|
def copy(src, dest)
|
13
|
-
request(
|
13
|
+
request("COPY", src, headers: { "destination" => @options.origin.merge(dest) })
|
14
14
|
end
|
15
15
|
|
16
16
|
def move(src, dest)
|
17
|
-
request(
|
17
|
+
request("MOVE", src, headers: { "destination" => @options.origin.merge(dest) })
|
18
18
|
end
|
19
19
|
|
20
20
|
def lock(path, timeout: nil, &blk)
|
@@ -30,7 +30,7 @@ module HTTPX
|
|
30
30
|
"<D:locktype><D:write/></D:locktype>" \
|
31
31
|
"<D:owner>null</D:owner>" \
|
32
32
|
"</D:lockinfo>"
|
33
|
-
response = request(
|
33
|
+
response = request("LOCK", path, headers: headers, xml: xml)
|
34
34
|
|
35
35
|
return response unless response.is_a?(Response)
|
36
36
|
|
@@ -46,11 +46,11 @@ module HTTPX
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def unlock(path, lock_token)
|
49
|
-
request(
|
49
|
+
request("UNLOCK", path, headers: { "lock-token" => lock_token })
|
50
50
|
end
|
51
51
|
|
52
52
|
def mkcol(dir)
|
53
|
-
request(
|
53
|
+
request("MKCOL", dir)
|
54
54
|
end
|
55
55
|
|
56
56
|
def propfind(path, xml = nil)
|
@@ -64,13 +64,13 @@ module HTTPX
|
|
64
64
|
xml
|
65
65
|
end
|
66
66
|
|
67
|
-
request(
|
67
|
+
request("PROPFIND", path, headers: { "depth" => "1" }, xml: body)
|
68
68
|
end
|
69
69
|
|
70
70
|
def proppatch(path, xml)
|
71
71
|
body = "<?xml version=\"1.0\"?>" \
|
72
72
|
"<D:propertyupdate xmlns:D=\"DAV:\" xmlns:Z=\"http://ns.example.com/standards/z39.50/\">#{xml}</D:propertyupdate>"
|
73
|
-
request(
|
73
|
+
request("PROPPATCH", path, xml: body)
|
74
74
|
end
|
75
75
|
# %i[ orderpatch acl report search]
|
76
76
|
end
|
data/lib/httpx/pool.rb
CHANGED
@@ -244,7 +244,7 @@ module HTTPX
|
|
244
244
|
def find_resolver_for(connection)
|
245
245
|
connection_options = connection.options
|
246
246
|
resolver_type = connection_options.resolver_class
|
247
|
-
resolver_type = Resolver.
|
247
|
+
resolver_type = Resolver.resolver_for(resolver_type)
|
248
248
|
|
249
249
|
@resolvers[resolver_type] ||= begin
|
250
250
|
resolver_manager = if resolver_type.multi?
|
data/lib/httpx/request.rb
CHANGED
@@ -19,7 +19,11 @@ module HTTPX
|
|
19
19
|
def_delegator :@body, :empty?
|
20
20
|
|
21
21
|
def initialize(verb, uri, options = {})
|
22
|
-
|
22
|
+
if verb.is_a?(Symbol)
|
23
|
+
warn "DEPRECATION WARNING: Using symbols for `verb` is deprecated, and will not be supported in httpx 1.0. " \
|
24
|
+
"Use \"#{verb.to_s.upcase}\" instead."
|
25
|
+
end
|
26
|
+
@verb = verb.to_s.upcase
|
23
27
|
@options = Options.new(options)
|
24
28
|
@uri = Utils.to_uri(uri)
|
25
29
|
if @uri.relative?
|
@@ -116,7 +120,7 @@ module HTTPX
|
|
116
120
|
|
117
121
|
query = []
|
118
122
|
if (q = @options.params)
|
119
|
-
query << Transcoder.
|
123
|
+
query << Transcoder::Form.encode(q)
|
120
124
|
end
|
121
125
|
query << @uri.query if @uri.query
|
122
126
|
@query = query.join("&")
|
@@ -138,7 +142,7 @@ module HTTPX
|
|
138
142
|
# :nocov:
|
139
143
|
def inspect
|
140
144
|
"#<HTTPX::Request:#{object_id} " \
|
141
|
-
"#{@verb
|
145
|
+
"#{@verb} " \
|
142
146
|
"#{uri} " \
|
143
147
|
"@headers=#{@headers} " \
|
144
148
|
"@body=#{@body}>"
|
@@ -156,15 +160,7 @@ module HTTPX
|
|
156
160
|
|
157
161
|
def initialize(headers, options)
|
158
162
|
@headers = headers
|
159
|
-
@body =
|
160
|
-
Transcoder.registry("body").encode(options.body)
|
161
|
-
elsif options.form
|
162
|
-
Transcoder.registry("form").encode(options.form)
|
163
|
-
elsif options.json
|
164
|
-
Transcoder.registry("json").encode(options.json)
|
165
|
-
elsif options.xml
|
166
|
-
Transcoder.registry("xml").encode(options.xml)
|
167
|
-
end
|
163
|
+
@body = initialize_body(options)
|
168
164
|
return if @body.nil?
|
169
165
|
|
170
166
|
@headers["content-type"] ||= @body.content_type
|
@@ -207,7 +203,7 @@ module HTTPX
|
|
207
203
|
|
208
204
|
def stream(body)
|
209
205
|
encoded = body
|
210
|
-
encoded = Transcoder.
|
206
|
+
encoded = Transcoder::Chunker.encode(body.enum_for(:each)) if chunked?
|
211
207
|
encoded
|
212
208
|
end
|
213
209
|
|
@@ -231,6 +227,20 @@ module HTTPX
|
|
231
227
|
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
232
228
|
end
|
233
229
|
# :nocov:
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def initialize_body(options)
|
234
|
+
if options.body
|
235
|
+
Transcoder::Body.encode(options.body)
|
236
|
+
elsif options.form
|
237
|
+
Transcoder::Form.encode(options.form)
|
238
|
+
elsif options.json
|
239
|
+
Transcoder::JSON.encode(options.json)
|
240
|
+
elsif options.xml
|
241
|
+
Transcoder::Xml.encode(options.xml)
|
242
|
+
end
|
243
|
+
end
|
234
244
|
end
|
235
245
|
|
236
246
|
def transition(nextstate)
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -104,7 +104,7 @@ module HTTPX
|
|
104
104
|
resolver_connection.send(request)
|
105
105
|
@connections << connection
|
106
106
|
rescue ResolveError, Resolv::DNS::EncodeError => e
|
107
|
-
|
107
|
+
reset_hostname(hostname)
|
108
108
|
emit_resolve_error(connection, connection.origin.host, e)
|
109
109
|
end
|
110
110
|
end
|
@@ -113,7 +113,7 @@ module HTTPX
|
|
113
113
|
response.raise_for_status
|
114
114
|
rescue StandardError => e
|
115
115
|
hostname = @requests.delete(request)
|
116
|
-
connection =
|
116
|
+
connection = reset_hostname(hostname)
|
117
117
|
emit_resolve_error(connection, connection.origin.host, e)
|
118
118
|
else
|
119
119
|
# @type var response: HTTPX::Response
|
@@ -128,30 +128,35 @@ module HTTPX
|
|
128
128
|
end
|
129
129
|
|
130
130
|
def parse(request, response)
|
131
|
-
|
132
|
-
answers = decode_response_body(response)
|
133
|
-
rescue Resolv::DNS::DecodeError => e
|
134
|
-
host, connection = @queries.first
|
135
|
-
@queries.delete(host)
|
136
|
-
emit_resolve_error(connection, connection.origin.host, e)
|
137
|
-
return
|
138
|
-
end
|
131
|
+
code, result = decode_response_body(response)
|
139
132
|
|
140
|
-
|
133
|
+
case code
|
134
|
+
when :ok
|
135
|
+
parse_addresses(result)
|
136
|
+
when :no_domain_found
|
141
137
|
# Indicates no such domain was found.
|
142
138
|
|
143
139
|
host = @requests.delete(request)
|
144
|
-
connection =
|
140
|
+
connection = reset_hostname(host)
|
145
141
|
|
146
|
-
emit_resolve_error(connection)
|
147
|
-
|
148
|
-
# no address found, eliminate candidates
|
142
|
+
emit_resolve_error(connection)
|
143
|
+
when :dns_error
|
149
144
|
host = @requests.delete(request)
|
150
|
-
connection =
|
145
|
+
connection = reset_hostname(host)
|
151
146
|
|
152
|
-
|
153
|
-
|
147
|
+
emit_resolve_error(connection)
|
148
|
+
when :decode_error
|
149
|
+
host, connection = @queries.first
|
150
|
+
reset_hostname(host)
|
151
|
+
emit_resolve_error(connection, connection.origin.host, result)
|
152
|
+
end
|
153
|
+
end
|
154
154
|
|
155
|
+
def parse_addresses(answers)
|
156
|
+
if answers.empty?
|
157
|
+
# no address found, eliminate candidates
|
158
|
+
host = @requests.delete(request)
|
159
|
+
connection = reset_hostname(host)
|
155
160
|
emit_resolve_error(connection)
|
156
161
|
return
|
157
162
|
|
@@ -162,7 +167,7 @@ module HTTPX
|
|
162
167
|
if address.key?("alias")
|
163
168
|
alias_address = answers[address["alias"]]
|
164
169
|
if alias_address.nil?
|
165
|
-
|
170
|
+
reset_hostname(address["name"])
|
166
171
|
if catch(:coalesced) { early_resolve(connection, hostname: address["alias"]) }
|
167
172
|
@connections.delete(connection)
|
168
173
|
else
|
@@ -179,7 +184,7 @@ module HTTPX
|
|
179
184
|
next if addresses.empty?
|
180
185
|
|
181
186
|
hostname.delete_suffix!(".") if hostname.end_with?(".")
|
182
|
-
connection =
|
187
|
+
connection = reset_hostname(hostname, reset_candidates: false)
|
183
188
|
next unless connection # probably a retried query for which there's an answer
|
184
189
|
|
185
190
|
@connections.delete(connection)
|
@@ -224,5 +229,17 @@ module HTTPX
|
|
224
229
|
raise Error, "unsupported DNS mime-type (#{response.headers["content-type"]})"
|
225
230
|
end
|
226
231
|
end
|
232
|
+
|
233
|
+
def reset_hostname(hostname, reset_candidates: true)
|
234
|
+
connection = @queries.delete(hostname)
|
235
|
+
|
236
|
+
return connection unless connection && reset_candidates
|
237
|
+
|
238
|
+
# eliminate other candidates
|
239
|
+
candidates = @queries.select { |_, conn| connection == conn }.keys
|
240
|
+
@queries.delete_if { |h, _| candidates.include?(h) }
|
241
|
+
|
242
|
+
connection
|
243
|
+
end
|
227
244
|
end
|
228
245
|
end
|