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.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +48 -0
  3. data/README.md +9 -5
  4. data/doc/release_notes/0_0_1.md +7 -0
  5. data/doc/release_notes/0_0_2.md +9 -0
  6. data/doc/release_notes/0_0_3.md +9 -0
  7. data/doc/release_notes/0_0_4.md +7 -0
  8. data/doc/release_notes/0_0_5.md +5 -0
  9. data/doc/release_notes/0_10_0.md +66 -0
  10. data/doc/release_notes/0_10_1.md +39 -0
  11. data/doc/release_notes/0_1_0.md +9 -0
  12. data/doc/release_notes/0_2_0.md +5 -0
  13. data/doc/release_notes/0_2_1.md +16 -0
  14. data/doc/release_notes/0_3_0.md +12 -0
  15. data/doc/release_notes/0_3_1.md +6 -0
  16. data/doc/release_notes/0_4_0.md +51 -0
  17. data/doc/release_notes/0_4_1.md +3 -0
  18. data/doc/release_notes/0_5_0.md +15 -0
  19. data/doc/release_notes/0_5_1.md +14 -0
  20. data/doc/release_notes/0_6_0.md +5 -0
  21. data/doc/release_notes/0_6_1.md +6 -0
  22. data/doc/release_notes/0_6_2.md +6 -0
  23. data/doc/release_notes/0_6_3.md +13 -0
  24. data/doc/release_notes/0_6_4.md +21 -0
  25. data/doc/release_notes/0_6_5.md +22 -0
  26. data/doc/release_notes/0_6_6.md +19 -0
  27. data/doc/release_notes/0_6_7.md +5 -0
  28. data/doc/release_notes/0_7_0.md +46 -0
  29. data/doc/release_notes/0_8_0.md +27 -0
  30. data/doc/release_notes/0_8_1.md +8 -0
  31. data/doc/release_notes/0_8_2.md +7 -0
  32. data/doc/release_notes/0_9_0.md +38 -0
  33. data/lib/httpx.rb +2 -0
  34. data/lib/httpx/adapters/faraday.rb +1 -1
  35. data/lib/httpx/chainable.rb +11 -11
  36. data/lib/httpx/connection.rb +23 -31
  37. data/lib/httpx/connection/http1.rb +30 -4
  38. data/lib/httpx/connection/http2.rb +29 -10
  39. data/lib/httpx/domain_name.rb +440 -0
  40. data/lib/httpx/errors.rb +2 -1
  41. data/lib/httpx/extensions.rb +22 -2
  42. data/lib/httpx/headers.rb +2 -2
  43. data/lib/httpx/io/ssl.rb +0 -1
  44. data/lib/httpx/io/tcp.rb +6 -5
  45. data/lib/httpx/io/udp.rb +4 -1
  46. data/lib/httpx/options.rb +5 -1
  47. data/lib/httpx/parser/http1.rb +14 -17
  48. data/lib/httpx/plugins/compression.rb +46 -65
  49. data/lib/httpx/plugins/compression/brotli.rb +10 -14
  50. data/lib/httpx/plugins/compression/deflate.rb +7 -6
  51. data/lib/httpx/plugins/compression/gzip.rb +23 -5
  52. data/lib/httpx/plugins/cookies.rb +21 -60
  53. data/lib/httpx/plugins/cookies/cookie.rb +173 -0
  54. data/lib/httpx/plugins/cookies/jar.rb +74 -0
  55. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
  56. data/lib/httpx/plugins/expect.rb +12 -1
  57. data/lib/httpx/plugins/follow_redirects.rb +20 -2
  58. data/lib/httpx/plugins/h2c.rb +1 -1
  59. data/lib/httpx/plugins/multipart.rb +12 -6
  60. data/lib/httpx/plugins/persistent.rb +6 -1
  61. data/lib/httpx/plugins/proxy.rb +16 -2
  62. data/lib/httpx/plugins/proxy/socks4.rb +14 -14
  63. data/lib/httpx/plugins/rate_limiter.rb +51 -0
  64. data/lib/httpx/plugins/retries.rb +3 -2
  65. data/lib/httpx/plugins/stream.rb +109 -13
  66. data/lib/httpx/pool.rb +14 -17
  67. data/lib/httpx/request.rb +8 -20
  68. data/lib/httpx/resolver.rb +7 -10
  69. data/lib/httpx/resolver/https.rb +22 -24
  70. data/lib/httpx/resolver/native.rb +19 -16
  71. data/lib/httpx/resolver/resolver_mixin.rb +4 -2
  72. data/lib/httpx/resolver/system.rb +2 -2
  73. data/lib/httpx/response.rb +16 -25
  74. data/lib/httpx/selector.rb +11 -18
  75. data/lib/httpx/session.rb +40 -26
  76. data/lib/httpx/transcoder.rb +18 -0
  77. data/lib/httpx/transcoder/chunker.rb +0 -2
  78. data/lib/httpx/transcoder/form.rb +9 -7
  79. data/lib/httpx/transcoder/json.rb +0 -4
  80. data/lib/httpx/utils.rb +45 -0
  81. data/lib/httpx/version.rb +1 -1
  82. data/sig/buffer.rbs +24 -0
  83. data/sig/callbacks.rbs +14 -0
  84. data/sig/chainable.rbs +37 -0
  85. data/sig/connection.rbs +85 -0
  86. data/sig/connection/http1.rbs +66 -0
  87. data/sig/connection/http2.rbs +78 -0
  88. data/sig/domain_name.rbs +17 -0
  89. data/sig/errors.rbs +3 -0
  90. data/sig/headers.rbs +42 -0
  91. data/sig/httpx.rbs +15 -0
  92. data/sig/loggable.rbs +11 -0
  93. data/sig/missing.rbs +12 -0
  94. data/sig/options.rbs +118 -0
  95. data/sig/parser/http1.rbs +50 -0
  96. data/sig/plugins/authentication.rbs +11 -0
  97. data/sig/plugins/basic_authentication.rbs +13 -0
  98. data/sig/plugins/compression.rbs +55 -0
  99. data/sig/plugins/compression/brotli.rbs +21 -0
  100. data/sig/plugins/compression/deflate.rbs +17 -0
  101. data/sig/plugins/compression/gzip.rbs +29 -0
  102. data/sig/plugins/cookies.rbs +26 -0
  103. data/sig/plugins/cookies/cookie.rbs +50 -0
  104. data/sig/plugins/cookies/jar.rbs +27 -0
  105. data/sig/plugins/digest_authentication.rbs +33 -0
  106. data/sig/plugins/expect.rbs +19 -0
  107. data/sig/plugins/follow_redirects.rbs +37 -0
  108. data/sig/plugins/h2c.rbs +26 -0
  109. data/sig/plugins/multipart.rbs +21 -0
  110. data/sig/plugins/persistent.rbs +17 -0
  111. data/sig/plugins/proxy.rbs +47 -0
  112. data/sig/plugins/proxy/http.rbs +14 -0
  113. data/sig/plugins/proxy/socks4.rbs +33 -0
  114. data/sig/plugins/proxy/socks5.rbs +36 -0
  115. data/sig/plugins/proxy/ssh.rbs +18 -0
  116. data/sig/plugins/push_promise.rbs +22 -0
  117. data/sig/plugins/rate_limiter.rbs +11 -0
  118. data/sig/plugins/retries.rbs +48 -0
  119. data/sig/plugins/stream.rbs +39 -0
  120. data/sig/pool.rbs +36 -0
  121. data/sig/registry.rbs +9 -0
  122. data/sig/request.rbs +61 -0
  123. data/sig/resolver.rbs +26 -0
  124. data/sig/resolver/https.rbs +49 -0
  125. data/sig/resolver/native.rbs +60 -0
  126. data/sig/resolver/resolver_mixin.rbs +27 -0
  127. data/sig/resolver/system.rbs +17 -0
  128. data/sig/response.rbs +87 -0
  129. data/sig/selector.rbs +20 -0
  130. data/sig/session.rbs +49 -0
  131. data/sig/timeout.rbs +29 -0
  132. data/sig/transcoder.rbs +18 -0
  133. data/sig/transcoder/body.rbs +18 -0
  134. data/sig/transcoder/chunker.rbs +32 -0
  135. data/sig/transcoder/form.rbs +16 -0
  136. data/sig/transcoder/json.rbs +14 -0
  137. metadata +128 -22
  138. data/lib/httpx/resolver/options.rb +0 -25
@@ -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(ch, error)
107
- ch.emit(:error, error)
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(ch)
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(ch1, ch2)
143
- if ch1.coalescable?(ch2)
144
- ch1.merge(ch2)
145
- @connections.delete(ch2)
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(ch2)
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
@@ -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 = URI(uri.to_s)
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
- # rubocop: disable Lint/UriEscapeUnescape:
64
+ URIParser = URI::DEFAULT_PARSER
65
+
70
66
  def initialize_with_escape(verb, uri, options = {})
71
- initialize_without_escape(verb, URI.escape(uri.to_s), options)
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 << URI.encode_www_form(q)
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
- if @body.respond_to?(:bytesize)
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)
@@ -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, :System
15
- register :native, :Native
16
- register :https, :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"
@@ -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 = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
33
- @_record_types = Hash.new { |types, host| types[host] = RECORD_TYPES.keys.dup }
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.uri)
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 #{connection.origin.host}")
48
+ ex = ResolveError.new("Can't resolve DNS server #{@uri.host}")
48
49
  ex.set_backtrace(caller)
49
- emit(:error, connection, ex)
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 = hostname || @queries.key(connection) || connection.origin.host
98
- type = @_record_types[hostname].first
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
- error = ResolveError.new("Can't resolve #{hostname}: #{e.message}")
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.cache
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.use_get
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\/x\-javascript} # because google...
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 = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
56
- @nameserver = @resolver_options.nameserver
57
- @_timeouts = Array(@resolver_options.timeouts)
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.record_types.dup }
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.packet_size)
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("Can't resolve #{connection.origin.host}: no nameserver")
112
+ ex = ResolveError.new("No available nameserver")
114
113
  ex.set_backtrace(caller)
115
- emit(:error, connection, ex)
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.packet_size)
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.cache
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 = hostname || @queries.key(connection) || connection.origin.host
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.cache && Resolver.cached_lookup(hostname)) ||
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 = Resolver::Options.new(@options.resolver_options)
17
+ @resolver_options = @options.resolver_options
18
18
  @state = :idle
19
- resolv_options = @resolver_options.to_h
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)