httpx 0.8.0 → 0.10.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/LICENSE.txt +48 -0
- data/README.md +9 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +39 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx.rb +2 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/chainable.rb +11 -11
- data/lib/httpx/connection.rb +23 -31
- data/lib/httpx/connection/http1.rb +30 -4
- data/lib/httpx/connection/http2.rb +29 -10
- data/lib/httpx/domain_name.rb +440 -0
- data/lib/httpx/errors.rb +2 -1
- data/lib/httpx/extensions.rb +22 -2
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +0 -1
- data/lib/httpx/io/tcp.rb +6 -5
- data/lib/httpx/io/udp.rb +4 -1
- data/lib/httpx/options.rb +5 -1
- data/lib/httpx/parser/http1.rb +14 -17
- data/lib/httpx/plugins/compression.rb +46 -65
- data/lib/httpx/plugins/compression/brotli.rb +10 -14
- data/lib/httpx/plugins/compression/deflate.rb +7 -6
- data/lib/httpx/plugins/compression/gzip.rb +23 -5
- data/lib/httpx/plugins/cookies.rb +21 -60
- data/lib/httpx/plugins/cookies/cookie.rb +173 -0
- data/lib/httpx/plugins/cookies/jar.rb +74 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
- data/lib/httpx/plugins/expect.rb +12 -1
- data/lib/httpx/plugins/follow_redirects.rb +20 -2
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart.rb +12 -6
- data/lib/httpx/plugins/persistent.rb +6 -1
- data/lib/httpx/plugins/proxy.rb +16 -2
- data/lib/httpx/plugins/proxy/socks4.rb +14 -14
- data/lib/httpx/plugins/rate_limiter.rb +51 -0
- data/lib/httpx/plugins/retries.rb +3 -2
- data/lib/httpx/plugins/stream.rb +109 -13
- data/lib/httpx/pool.rb +14 -17
- data/lib/httpx/request.rb +8 -20
- data/lib/httpx/resolver.rb +7 -10
- data/lib/httpx/resolver/https.rb +22 -24
- data/lib/httpx/resolver/native.rb +19 -16
- data/lib/httpx/resolver/resolver_mixin.rb +4 -2
- data/lib/httpx/resolver/system.rb +2 -2
- data/lib/httpx/response.rb +16 -25
- data/lib/httpx/selector.rb +11 -18
- data/lib/httpx/session.rb +40 -26
- data/lib/httpx/transcoder.rb +18 -0
- data/lib/httpx/transcoder/chunker.rb +0 -2
- data/lib/httpx/transcoder/form.rb +9 -7
- data/lib/httpx/transcoder/json.rb +0 -4
- data/lib/httpx/utils.rb +45 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +24 -0
- data/sig/callbacks.rbs +14 -0
- data/sig/chainable.rbs +37 -0
- data/sig/connection.rbs +85 -0
- data/sig/connection/http1.rbs +66 -0
- data/sig/connection/http2.rbs +78 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +3 -0
- data/sig/headers.rbs +42 -0
- data/sig/httpx.rbs +15 -0
- data/sig/loggable.rbs +11 -0
- data/sig/missing.rbs +12 -0
- data/sig/options.rbs +118 -0
- data/sig/parser/http1.rbs +50 -0
- data/sig/plugins/authentication.rbs +11 -0
- data/sig/plugins/basic_authentication.rbs +13 -0
- data/sig/plugins/compression.rbs +55 -0
- data/sig/plugins/compression/brotli.rbs +21 -0
- data/sig/plugins/compression/deflate.rbs +17 -0
- data/sig/plugins/compression/gzip.rbs +29 -0
- data/sig/plugins/cookies.rbs +26 -0
- data/sig/plugins/cookies/cookie.rbs +50 -0
- data/sig/plugins/cookies/jar.rbs +27 -0
- data/sig/plugins/digest_authentication.rbs +33 -0
- data/sig/plugins/expect.rbs +19 -0
- data/sig/plugins/follow_redirects.rbs +37 -0
- data/sig/plugins/h2c.rbs +26 -0
- data/sig/plugins/multipart.rbs +21 -0
- data/sig/plugins/persistent.rbs +17 -0
- data/sig/plugins/proxy.rbs +47 -0
- data/sig/plugins/proxy/http.rbs +14 -0
- data/sig/plugins/proxy/socks4.rbs +33 -0
- data/sig/plugins/proxy/socks5.rbs +36 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/push_promise.rbs +22 -0
- data/sig/plugins/rate_limiter.rbs +11 -0
- data/sig/plugins/retries.rbs +48 -0
- data/sig/plugins/stream.rbs +39 -0
- data/sig/pool.rbs +36 -0
- data/sig/registry.rbs +9 -0
- data/sig/request.rbs +61 -0
- data/sig/resolver.rbs +26 -0
- data/sig/resolver/https.rbs +49 -0
- data/sig/resolver/native.rbs +60 -0
- data/sig/resolver/resolver_mixin.rbs +27 -0
- data/sig/resolver/system.rbs +17 -0
- data/sig/response.rbs +87 -0
- data/sig/selector.rbs +20 -0
- data/sig/session.rbs +49 -0
- data/sig/timeout.rbs +29 -0
- data/sig/transcoder.rbs +18 -0
- data/sig/transcoder/body.rbs +18 -0
- data/sig/transcoder/chunker.rbs +32 -0
- data/sig/transcoder/form.rbs +16 -0
- data/sig/transcoder/json.rbs +14 -0
- metadata +128 -22
- data/lib/httpx/resolver/options.rb +0 -25
data/lib/httpx/pool.rb
CHANGED
|
@@ -41,13 +41,18 @@ module HTTPX
|
|
|
41
41
|
@connections.each do |connection|
|
|
42
42
|
connection.emit(:error, e)
|
|
43
43
|
end
|
|
44
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
45
|
+
@connections.each(&:reset)
|
|
46
|
+
raise
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def close(connections = @connections)
|
|
50
|
+
return if connections.empty?
|
|
51
|
+
|
|
47
52
|
@timers.cancel
|
|
48
53
|
connections = connections.reject(&:inflight?)
|
|
49
54
|
connections.each(&:close)
|
|
50
|
-
next_tick until connections.none? { |c| @connections.include?(c) }
|
|
55
|
+
next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
|
|
51
56
|
@resolvers.each_value do |resolver|
|
|
52
57
|
resolver.close unless resolver.closed?
|
|
53
58
|
end if @connections.empty?
|
|
@@ -103,10 +108,10 @@ module HTTPX
|
|
|
103
108
|
end
|
|
104
109
|
end
|
|
105
110
|
|
|
106
|
-
def on_resolver_error(
|
|
107
|
-
|
|
111
|
+
def on_resolver_error(connection, error)
|
|
112
|
+
connection.emit(:error, error)
|
|
108
113
|
# must remove connection by hand, hasn't been started yet
|
|
109
|
-
unregister_connection(
|
|
114
|
+
unregister_connection(connection)
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
def on_resolver_close(resolver)
|
|
@@ -139,12 +144,12 @@ module HTTPX
|
|
|
139
144
|
@connected_connections -= 1
|
|
140
145
|
end
|
|
141
146
|
|
|
142
|
-
def coalesce_connections(
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
@connections.delete(
|
|
147
|
+
def coalesce_connections(conn1, conn2)
|
|
148
|
+
if conn1.coalescable?(conn2)
|
|
149
|
+
conn1.merge(conn2)
|
|
150
|
+
@connections.delete(conn2)
|
|
146
151
|
else
|
|
147
|
-
register_connection(
|
|
152
|
+
register_connection(conn2)
|
|
148
153
|
end
|
|
149
154
|
end
|
|
150
155
|
|
|
@@ -163,15 +168,7 @@ module HTTPX
|
|
|
163
168
|
resolver.on(:error, &method(:on_resolver_error))
|
|
164
169
|
resolver.on(:close) { on_resolver_close(resolver) }
|
|
165
170
|
resolver
|
|
166
|
-
# rubocop: disable Layout/RescueEnsureAlignment
|
|
167
|
-
rescue ArgumentError
|
|
168
|
-
# this block is here because of an error which happens on CI from time to time
|
|
169
|
-
warn "tried resolver: #{resolver_type}"
|
|
170
|
-
warn "initialize: #{resolver_type.instance_method(:initialize).source_location}"
|
|
171
|
-
warn "new: #{resolver_type.method(:new).source_location}"
|
|
172
|
-
raise
|
|
173
171
|
end
|
|
174
|
-
# rubocop: enable Layout/RescueEnsureAlignment
|
|
175
172
|
end
|
|
176
173
|
end
|
|
177
174
|
end
|
data/lib/httpx/request.rb
CHANGED
|
@@ -33,11 +33,7 @@ module HTTPX
|
|
|
33
33
|
|
|
34
34
|
USER_AGENT = "httpx.rb/#{VERSION}"
|
|
35
35
|
|
|
36
|
-
attr_reader :verb, :uri, :headers, :body, :state
|
|
37
|
-
|
|
38
|
-
attr_reader :options, :response
|
|
39
|
-
|
|
40
|
-
def_delegator :@body, :<<
|
|
36
|
+
attr_reader :verb, :uri, :headers, :body, :state, :options, :response
|
|
41
37
|
|
|
42
38
|
def_delegator :@body, :empty?
|
|
43
39
|
|
|
@@ -45,7 +41,7 @@ module HTTPX
|
|
|
45
41
|
|
|
46
42
|
def initialize(verb, uri, options = {})
|
|
47
43
|
@verb = verb.to_s.downcase.to_sym
|
|
48
|
-
@uri =
|
|
44
|
+
@uri = Utils.uri(uri)
|
|
49
45
|
@options = Options.new(options)
|
|
50
46
|
|
|
51
47
|
raise(Error, "unknown method: #{verb}") unless METHODS.include?(@verb)
|
|
@@ -64,17 +60,15 @@ module HTTPX
|
|
|
64
60
|
:w
|
|
65
61
|
end
|
|
66
62
|
|
|
67
|
-
# :nocov:
|
|
68
63
|
if RUBY_VERSION < "2.2"
|
|
69
|
-
|
|
64
|
+
URIParser = URI::DEFAULT_PARSER
|
|
65
|
+
|
|
70
66
|
def initialize_with_escape(verb, uri, options = {})
|
|
71
|
-
initialize_without_escape(verb,
|
|
67
|
+
initialize_without_escape(verb, URIParser.escape(uri.to_s), options)
|
|
72
68
|
end
|
|
73
69
|
alias_method :initialize_without_escape, :initialize
|
|
74
70
|
alias_method :initialize, :initialize_with_escape
|
|
75
|
-
# rubocop: enable Lint/UriEscapeUnescape:
|
|
76
71
|
end
|
|
77
|
-
# :nocov:
|
|
78
72
|
|
|
79
73
|
def merge_headers(h)
|
|
80
74
|
@headers = @headers.merge(h)
|
|
@@ -112,7 +106,7 @@ module HTTPX
|
|
|
112
106
|
|
|
113
107
|
query = []
|
|
114
108
|
if (q = @options.params)
|
|
115
|
-
query <<
|
|
109
|
+
query << Transcoder.registry("form").encode(q)
|
|
116
110
|
end
|
|
117
111
|
query << @uri.query if @uri.query
|
|
118
112
|
@query = query.join("&")
|
|
@@ -180,19 +174,13 @@ module HTTPX
|
|
|
180
174
|
return true if @body.nil?
|
|
181
175
|
return false if chunked?
|
|
182
176
|
|
|
183
|
-
bytesize.zero?
|
|
177
|
+
@body.bytesize.zero?
|
|
184
178
|
end
|
|
185
179
|
|
|
186
180
|
def bytesize
|
|
187
181
|
return 0 if @body.nil?
|
|
188
182
|
|
|
189
|
-
|
|
190
|
-
@body.bytesize
|
|
191
|
-
elsif @body.respond_to?(:size)
|
|
192
|
-
@body.size
|
|
193
|
-
else
|
|
194
|
-
raise Error, "cannot determine size of body: #{@body.inspect}"
|
|
195
|
-
end
|
|
183
|
+
@body.bytesize
|
|
196
184
|
end
|
|
197
185
|
|
|
198
186
|
def stream(body)
|
data/lib/httpx/resolver.rb
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "resolv"
|
|
4
|
+
require "httpx/resolver/resolver_mixin"
|
|
5
|
+
require "httpx/resolver/system"
|
|
6
|
+
require "httpx/resolver/native"
|
|
7
|
+
require "httpx/resolver/https"
|
|
4
8
|
|
|
5
9
|
module HTTPX
|
|
6
10
|
module Resolver
|
|
7
|
-
autoload :ResolverMixin, "httpx/resolver/resolver_mixin"
|
|
8
|
-
autoload :System, "httpx/resolver/system"
|
|
9
|
-
autoload :Native, "httpx/resolver/native"
|
|
10
|
-
autoload :HTTPS, "httpx/resolver/https"
|
|
11
|
-
|
|
12
11
|
extend Registry
|
|
13
12
|
|
|
14
|
-
register :system,
|
|
15
|
-
register :native,
|
|
16
|
-
register :https,
|
|
13
|
+
register :system, System
|
|
14
|
+
register :native, Native
|
|
15
|
+
register :https, HTTPS
|
|
17
16
|
|
|
18
17
|
@lookup_mutex = Mutex.new
|
|
19
18
|
@lookups = Hash.new { |h, k| h[k] = [] }
|
|
@@ -102,5 +101,3 @@ module HTTPX
|
|
|
102
101
|
end
|
|
103
102
|
end
|
|
104
103
|
end
|
|
105
|
-
|
|
106
|
-
require "httpx/resolver/options"
|
data/lib/httpx/resolver/https.rb
CHANGED
|
@@ -21,6 +21,7 @@ module HTTPX
|
|
|
21
21
|
DEFAULTS = {
|
|
22
22
|
uri: NAMESERVER,
|
|
23
23
|
use_get: false,
|
|
24
|
+
record_types: RECORD_TYPES.keys,
|
|
24
25
|
}.freeze
|
|
25
26
|
|
|
26
27
|
def_delegator :@connections, :empty?
|
|
@@ -29,12 +30,12 @@ module HTTPX
|
|
|
29
30
|
|
|
30
31
|
def initialize(options)
|
|
31
32
|
@options = Options.new(options)
|
|
32
|
-
@resolver_options =
|
|
33
|
-
@_record_types = Hash.new { |types, host| types[host] =
|
|
33
|
+
@resolver_options = DEFAULTS.merge(@options.resolver_options)
|
|
34
|
+
@_record_types = Hash.new { |types, host| types[host] = @resolver_options[:record_types].dup }
|
|
34
35
|
@queries = {}
|
|
35
36
|
@requests = {}
|
|
36
37
|
@connections = []
|
|
37
|
-
@uri = URI(@resolver_options
|
|
38
|
+
@uri = URI(@resolver_options[:uri])
|
|
38
39
|
@uri_addresses = nil
|
|
39
40
|
end
|
|
40
41
|
|
|
@@ -44,12 +45,12 @@ module HTTPX
|
|
|
44
45
|
@uri_addresses ||= Resolv.getaddresses(@uri.host)
|
|
45
46
|
|
|
46
47
|
if @uri_addresses.empty?
|
|
47
|
-
ex = ResolveError.new("Can't resolve #{
|
|
48
|
+
ex = ResolveError.new("Can't resolve DNS server #{@uri.host}")
|
|
48
49
|
ex.set_backtrace(caller)
|
|
49
|
-
|
|
50
|
-
else
|
|
51
|
-
early_resolve(connection) || resolve(connection)
|
|
50
|
+
throw(:resolve_error, ex)
|
|
52
51
|
end
|
|
52
|
+
|
|
53
|
+
early_resolve(connection) || resolve(connection)
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def timeout
|
|
@@ -70,12 +71,6 @@ module HTTPX
|
|
|
70
71
|
|
|
71
72
|
private
|
|
72
73
|
|
|
73
|
-
def connect
|
|
74
|
-
return if @queries.empty?
|
|
75
|
-
|
|
76
|
-
resolver_connection.__send__(__method__)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
74
|
def pool
|
|
80
75
|
Thread.current[:httpx_connection_pool] ||= Pool.new
|
|
81
76
|
end
|
|
@@ -94,11 +89,18 @@ module HTTPX
|
|
|
94
89
|
def resolve(connection = @connections.first, hostname = nil)
|
|
95
90
|
return if @building_connection
|
|
96
91
|
|
|
97
|
-
hostname
|
|
98
|
-
|
|
92
|
+
hostname ||= @queries.key(connection)
|
|
93
|
+
|
|
94
|
+
if hostname.nil?
|
|
95
|
+
hostname = connection.origin.host
|
|
96
|
+
log { "resolver: resolve IDN #{connection.origin.non_ascii_hostname} as #{hostname}" } if connection.origin.non_ascii_hostname
|
|
97
|
+
end
|
|
98
|
+
type = @_record_types[hostname].first || "A"
|
|
99
99
|
log { "resolver: query #{type} for #{hostname}" }
|
|
100
100
|
begin
|
|
101
101
|
request = build_request(hostname, type)
|
|
102
|
+
request.on(:response, &method(:on_response).curry[request])
|
|
103
|
+
request.on(:promise, &method(:on_promise))
|
|
102
104
|
@requests[request] = connection
|
|
103
105
|
resolver_connection.send(request)
|
|
104
106
|
@queries[hostname] = connection
|
|
@@ -113,9 +115,7 @@ module HTTPX
|
|
|
113
115
|
rescue StandardError => e
|
|
114
116
|
connection = @requests[request]
|
|
115
117
|
hostname = @queries.key(connection)
|
|
116
|
-
|
|
117
|
-
error.set_backtrace(e.backtrace)
|
|
118
|
-
emit(:error, connection, error)
|
|
118
|
+
emit_resolve_error(connection, hostname, e)
|
|
119
119
|
else
|
|
120
120
|
parse(response)
|
|
121
121
|
ensure
|
|
@@ -138,7 +138,7 @@ module HTTPX
|
|
|
138
138
|
return
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
|
-
if answers.empty?
|
|
141
|
+
if answers.nil? || answers.empty?
|
|
142
142
|
host, connection = @queries.first
|
|
143
143
|
@_record_types[host].shift
|
|
144
144
|
if @_record_types[host].empty?
|
|
@@ -172,7 +172,7 @@ module HTTPX
|
|
|
172
172
|
next unless connection # probably a retried query for which there's an answer
|
|
173
173
|
|
|
174
174
|
@connections.delete(connection)
|
|
175
|
-
Resolver.cached_lookup_set(hostname, addresses) if @resolver_options
|
|
175
|
+
Resolver.cached_lookup_set(hostname, addresses) if @resolver_options[:cache]
|
|
176
176
|
emit_addresses(connection, addresses.map { |addr| addr["data"] })
|
|
177
177
|
end
|
|
178
178
|
end
|
|
@@ -186,7 +186,7 @@ module HTTPX
|
|
|
186
186
|
rklass = @options.request_class
|
|
187
187
|
payload = Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
|
|
188
188
|
|
|
189
|
-
if @resolver_options
|
|
189
|
+
if @resolver_options[:use_get]
|
|
190
190
|
params = URI.decode_www_form(uri.query.to_s)
|
|
191
191
|
params << ["type", type]
|
|
192
192
|
params << ["dns", Base64.urlsafe_encode64(payload, padding: false)]
|
|
@@ -197,8 +197,6 @@ module HTTPX
|
|
|
197
197
|
request.headers["content-type"] = "application/dns-message"
|
|
198
198
|
end
|
|
199
199
|
request.headers["accept"] = "application/dns-message"
|
|
200
|
-
request.on(:response, &method(:on_response).curry[request])
|
|
201
|
-
request.on(:promise, &method(:on_promise))
|
|
202
200
|
request
|
|
203
201
|
end
|
|
204
202
|
|
|
@@ -206,7 +204,7 @@ module HTTPX
|
|
|
206
204
|
case response.headers["content-type"]
|
|
207
205
|
when "application/dns-json",
|
|
208
206
|
"application/json",
|
|
209
|
-
%r{^application
|
|
207
|
+
%r{^application/x-javascript} # because google...
|
|
210
208
|
payload = JSON.parse(response.to_s)
|
|
211
209
|
payload["Answer"]
|
|
212
210
|
when "application/dns-udpwireformat",
|
|
@@ -7,6 +7,7 @@ module HTTPX
|
|
|
7
7
|
class Resolver::Native
|
|
8
8
|
extend Forwardable
|
|
9
9
|
include Resolver::ResolverMixin
|
|
10
|
+
using URIExtensions
|
|
10
11
|
|
|
11
12
|
RESOLVE_TIMEOUT = 5
|
|
12
13
|
RECORD_TYPES = {
|
|
@@ -14,7 +15,6 @@ module HTTPX
|
|
|
14
15
|
"AAAA" => Resolv::DNS::Resource::IN::AAAA,
|
|
15
16
|
}.freeze
|
|
16
17
|
|
|
17
|
-
# :nocov:
|
|
18
18
|
DEFAULTS = if RUBY_VERSION < "2.2"
|
|
19
19
|
{
|
|
20
20
|
**Resolv::DNS::Config.default_config_hash,
|
|
@@ -43,7 +43,6 @@ module HTTPX
|
|
|
43
43
|
false
|
|
44
44
|
end
|
|
45
45
|
end if DEFAULTS[:nameserver]
|
|
46
|
-
# :nocov:
|
|
47
46
|
|
|
48
47
|
DNS_PORT = 53
|
|
49
48
|
|
|
@@ -52,15 +51,15 @@ module HTTPX
|
|
|
52
51
|
def initialize(options)
|
|
53
52
|
@options = Options.new(options)
|
|
54
53
|
@ns_index = 0
|
|
55
|
-
@resolver_options =
|
|
56
|
-
@nameserver = @resolver_options
|
|
57
|
-
@_timeouts = Array(@resolver_options
|
|
54
|
+
@resolver_options = DEFAULTS.merge(@options.resolver_options)
|
|
55
|
+
@nameserver = @resolver_options[:nameserver]
|
|
56
|
+
@_timeouts = Array(@resolver_options[:timeouts])
|
|
58
57
|
@timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup }
|
|
59
|
-
@_record_types = Hash.new { |types, host| types[host] = @resolver_options
|
|
58
|
+
@_record_types = Hash.new { |types, host| types[host] = @resolver_options[:record_types].dup }
|
|
60
59
|
@connections = []
|
|
61
60
|
@queries = {}
|
|
62
61
|
@read_buffer = "".b
|
|
63
|
-
@write_buffer = Buffer.new(@resolver_options
|
|
62
|
+
@write_buffer = Buffer.new(@resolver_options[:packet_size])
|
|
64
63
|
@state = :idle
|
|
65
64
|
end
|
|
66
65
|
|
|
@@ -110,9 +109,9 @@ module HTTPX
|
|
|
110
109
|
return if early_resolve(connection)
|
|
111
110
|
|
|
112
111
|
if @nameserver.nil?
|
|
113
|
-
ex = ResolveError.new("
|
|
112
|
+
ex = ResolveError.new("No available nameserver")
|
|
114
113
|
ex.set_backtrace(caller)
|
|
115
|
-
|
|
114
|
+
throw(:resolve_error, ex)
|
|
116
115
|
else
|
|
117
116
|
@connections << connection
|
|
118
117
|
resolve
|
|
@@ -163,12 +162,11 @@ module HTTPX
|
|
|
163
162
|
connections.each { |ch| resolve(ch) }
|
|
164
163
|
end
|
|
165
164
|
|
|
166
|
-
def dread(wsize = @resolver_options
|
|
165
|
+
def dread(wsize = @resolver_options[:packet_size])
|
|
167
166
|
loop do
|
|
168
167
|
siz = @io.read(wsize, @read_buffer)
|
|
169
168
|
return unless siz && siz.positive?
|
|
170
169
|
|
|
171
|
-
log { "resolver: READ: #{siz} bytes..." }
|
|
172
170
|
parse(@read_buffer)
|
|
173
171
|
return if @state == :closed
|
|
174
172
|
end
|
|
@@ -181,7 +179,6 @@ module HTTPX
|
|
|
181
179
|
siz = @io.write(@write_buffer)
|
|
182
180
|
return unless siz && siz.positive?
|
|
183
181
|
|
|
184
|
-
log { "resolver: WRITE: #{siz} bytes..." }
|
|
185
182
|
return if @state == :closed
|
|
186
183
|
end
|
|
187
184
|
end
|
|
@@ -200,13 +197,14 @@ module HTTPX
|
|
|
200
197
|
end
|
|
201
198
|
end
|
|
202
199
|
|
|
203
|
-
if addresses.empty?
|
|
200
|
+
if addresses.nil? || addresses.empty?
|
|
204
201
|
hostname, connection = @queries.first
|
|
205
202
|
@_record_types[hostname].shift
|
|
206
203
|
if @_record_types[hostname].empty?
|
|
207
204
|
@queries.delete(hostname)
|
|
208
205
|
@_record_types.delete(hostname)
|
|
209
206
|
@connections.delete(connection)
|
|
207
|
+
|
|
210
208
|
raise NativeResolveError.new(connection, hostname)
|
|
211
209
|
end
|
|
212
210
|
else
|
|
@@ -224,7 +222,7 @@ module HTTPX
|
|
|
224
222
|
end
|
|
225
223
|
else
|
|
226
224
|
@connections.delete(connection)
|
|
227
|
-
Resolver.cached_lookup_set(connection.origin.host, addresses) if @resolver_options
|
|
225
|
+
Resolver.cached_lookup_set(connection.origin.host, addresses) if @resolver_options[:cache]
|
|
228
226
|
emit_addresses(connection, addresses.map { |addr| addr["data"] })
|
|
229
227
|
end
|
|
230
228
|
end
|
|
@@ -237,9 +235,14 @@ module HTTPX
|
|
|
237
235
|
raise Error, "no URI to resolve" unless connection
|
|
238
236
|
return unless @write_buffer.empty?
|
|
239
237
|
|
|
240
|
-
hostname
|
|
238
|
+
hostname ||= @queries.key(connection)
|
|
239
|
+
|
|
240
|
+
if hostname.nil?
|
|
241
|
+
hostname = connection.origin.host
|
|
242
|
+
log { "resolver: resolve IDN #{connection.origin.non_ascii_hostname} as #{hostname}" } if connection.origin.non_ascii_hostname
|
|
243
|
+
end
|
|
241
244
|
@queries[hostname] = connection
|
|
242
|
-
type = @_record_types[hostname].first
|
|
245
|
+
type = @_record_types[hostname].first || "A"
|
|
243
246
|
log { "resolver: query #{type} for #{hostname}" }
|
|
244
247
|
begin
|
|
245
248
|
@write_buffer << Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
|
|
@@ -38,7 +38,7 @@ module HTTPX
|
|
|
38
38
|
def early_resolve(connection, hostname: connection.origin.host)
|
|
39
39
|
addresses = connection.addresses ||
|
|
40
40
|
ip_resolve(hostname) ||
|
|
41
|
-
(@resolver_options
|
|
41
|
+
(@resolver_options[:cache] && Resolver.cached_lookup(hostname)) ||
|
|
42
42
|
system_resolve(hostname)
|
|
43
43
|
return unless addresses
|
|
44
44
|
|
|
@@ -57,11 +57,13 @@ module HTTPX
|
|
|
57
57
|
ips.map { |ip| IPAddr.new(ip) }
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
def emit_resolve_error(connection, hostname, ex = nil)
|
|
60
|
+
def emit_resolve_error(connection, hostname = connection.origin.host, ex = nil)
|
|
61
61
|
emit(:error, connection, resolve_error(hostname, ex))
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def resolve_error(hostname, ex = nil)
|
|
65
|
+
return ex if ex.is_a?(ResolveError)
|
|
66
|
+
|
|
65
67
|
message = ex ? ex.message : "Can't resolve #{hostname}"
|
|
66
68
|
error = ResolveError.new(message)
|
|
67
69
|
error.set_backtrace(ex ? ex.backtrace : caller)
|
|
@@ -14,9 +14,9 @@ module HTTPX
|
|
|
14
14
|
|
|
15
15
|
def initialize(options)
|
|
16
16
|
@options = Options.new(options)
|
|
17
|
-
@resolver_options =
|
|
17
|
+
@resolver_options = @options.resolver_options
|
|
18
18
|
@state = :idle
|
|
19
|
-
resolv_options = @resolver_options.
|
|
19
|
+
resolv_options = @resolver_options.dup
|
|
20
20
|
timeouts = resolv_options.delete(:timeouts)
|
|
21
21
|
resolv_options.delete(:cache)
|
|
22
22
|
@resolver = Resolv::DNS.new(resolv_options.empty? ? nil : resolv_options)
|