lib/httpx/resolver/https.rb in httpx-0.18.7 vs lib/httpx/resolver/https.rb in httpx-0.19.0

- old
+ new

@@ -4,36 +4,27 @@ require "uri" require "cgi" require "forwardable" module HTTPX - class Resolver::HTTPS + class Resolver::HTTPS < Resolver::Resolver extend Forwardable - include Resolver::ResolverMixin using URIExtensions + using StringExtensions NAMESERVER = "https://1.1.1.1/dns-query" - RECORD_TYPES = { - "A" => Resolv::DNS::Resource::IN::A, - "AAAA" => Resolv::DNS::Resource::IN::AAAA, - }.freeze - DEFAULTS = { uri: NAMESERVER, use_get: false, - record_types: RECORD_TYPES.keys, }.freeze def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close - attr_writer :pool - - def initialize(options) - @options = HTTPX::Options.new(options) + def initialize(_, options) + super @resolver_options = DEFAULTS.merge(@options.resolver_options) - @_record_types = Hash.new { |types, host| types[host] = @resolver_options[:record_types].dup } @queries = {} @requests = {} @connections = [] @uri = URI(@resolver_options[:uri]) @uri_addresses = nil @@ -42,62 +33,62 @@ end def <<(connection) return if @uri.origin == connection.origin.to_s - @uri_addresses ||= ip_resolve(@uri.host) || system_resolve(@uri.host) || @resolver.getaddresses(@uri.host) + @uri_addresses ||= HTTPX::Resolver.nolookup_resolve(@uri.host) || @resolver.getaddresses(@uri.host) if @uri_addresses.empty? ex = ResolveError.new("Can't resolve DNS server #{@uri.host}") ex.set_backtrace(caller) throw(:resolve_error, ex) end - early_resolve(connection) || resolve(connection) + resolve(connection) end def closed? true end def empty? true end - private - def resolver_connection @resolver_connection ||= @pool.find_connection(@uri, @options) || begin @building_connection = true connection = @options.connection_class.new("ssl", @uri, @options.merge(ssl: { alpn_protocols: %w[h2] })) @pool.init_connection(connection, @options) - emit_addresses(connection, @uri_addresses) + emit_addresses(connection, @family, @uri_addresses) @building_connection = false connection end end + private + def resolve(connection = @connections.first, hostname = nil) return if @building_connection + return unless connection hostname ||= @queries.key(connection) if hostname.nil? hostname = connection.origin.host log { "resolver: resolve IDN #{connection.origin.non_ascii_hostname} as #{hostname}" } if connection.origin.non_ascii_hostname end - type = @_record_types[hostname].first || "A" - log { "resolver: query #{type} for #{hostname}" } + log { "resolver: query #{FAMILY_TYPES[RECORD_TYPES[@family]]} for #{hostname}" } begin - request = build_request(hostname, type) + request = build_request(hostname) request.on(:response, &method(:on_response).curry(2)[request]) request.on(:promise, &method(:on_promise)) @requests[request] = connection resolver_connection.send(request) @queries[hostname] = connection @connections << connection - rescue Resolv::DNS::EncodeError, JSON::JSONError => e + rescue ResolveError, Resolv::DNS::EncodeError, JSON::JSONError => e emit_resolve_error(connection, hostname, e) end end def on_response(request, response) @@ -105,10 +96,11 @@ rescue StandardError => e connection = @requests[request] hostname = @queries.key(connection) emit_resolve_error(connection, hostname, e) else + # @type var response: HTTPX::Response parse(response) ensure @requests.delete(request) end @@ -120,66 +112,64 @@ def parse(response) begin answers = decode_response_body(response) rescue Resolv::DNS::DecodeError, JSON::JSONError => e host, connection = @queries.first - if @_record_types[host].empty? - @queries.delete(host) - emit_resolve_error(connection, host, e) - return - end + @queries.delete(host) + emit_resolve_error(connection, host, e) + return end if answers.nil? || answers.empty? host, connection = @queries.first - @_record_types[host].shift - if @_record_types[host].empty? - @queries.delete(host) - @_record_types.delete(host) - emit_resolve_error(connection, host) - return - end + @queries.delete(host) + emit_resolve_error(connection, host) + return else answers = answers.group_by { |answer| answer["name"] } answers.each do |hostname, addresses| addresses = addresses.flat_map do |address| if address.key?("alias") alias_address = answers[address["alias"]] if alias_address.nil? connection = @queries[hostname] @queries.delete(address["name"]) - resolve(connection, address["alias"]) - return # rubocop:disable Lint/NonLocalExitFromIterator + if catch(:coalesced) { early_resolve(connection, hostname: address["alias"]) } + @connections.delete(connection) + else + resolve(connection, address["alias"]) + return # rubocop:disable Lint/NonLocalExitFromIterator + end else alias_address end else address end end.compact next if addresses.empty? - hostname = hostname[0..-2] if hostname.end_with?(".") + hostname.delete_suffix!(".") if hostname.end_with?(".") connection = @queries.delete(hostname) next unless connection # probably a retried query for which there's an answer @connections.delete(connection) - Resolver.cached_lookup_set(hostname, addresses) if @resolver_options[:cache] - emit_addresses(connection, addresses.map { |addr| addr["data"] }) + Resolver.cached_lookup_set(hostname, @family, addresses) if @resolver_options[:cache] + emit_addresses(connection, @family, addresses.map { |addr| addr["data"] }) end end return if @connections.empty? resolve end - def build_request(hostname, type) + def build_request(hostname) uri = @uri.dup rklass = @options.request_class - payload = Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type]) + payload = Resolver.encode_dns_query(hostname, type: @record_type) if @resolver_options[:use_get] params = URI.decode_www_form(uri.query.to_s) - params << ["type", type] + params << ["type", FAMILY_TYPES[@record_type]] params << ["dns", Base64.urlsafe_encode64(payload, padding: false)] uri.query = URI.encode_www_form(params) request = rklass.new("GET", uri, @options) else request = rklass.new("POST", uri, @options.merge(body: [payload]))