lib/httpx/resolver/https.rb in httpx-0.19.4 vs lib/httpx/resolver/https.rb in httpx-0.19.5

- old
+ new

@@ -9,10 +9,19 @@ class Resolver::HTTPS < Resolver::Resolver extend Forwardable using URIExtensions using StringExtensions + module DNSExtensions + refine Resolv::DNS do + def generate_candidates(name) + @config.generate_candidates(name) + end + end + end + using DNSExtensions + NAMESERVER = "https://1.1.1.1/dns-query" DEFAULTS = { uri: NAMESERVER, use_get: false, @@ -74,65 +83,72 @@ 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 + + hostname = @resolver.generate_candidates(hostname).each do |name| + @queries[name.to_s] = connection + end.first.to_s + else + @queries[hostname] = connection end log { "resolver: query #{FAMILY_TYPES[RECORD_TYPES[@family]]} for #{hostname}" } + begin request = build_request(hostname) request.on(:response, &method(:on_response).curry(2)[request]) request.on(:promise, &method(:on_promise)) - @requests[request] = connection + @requests[request] = hostname resolver_connection.send(request) - @queries[hostname] = connection @connections << connection rescue ResolveError, Resolv::DNS::EncodeError, JSON::JSONError => e - emit_resolve_error(connection, hostname, e) + @queries.delete(hostname) + emit_resolve_error(connection, connection.origin.host, e) end end def on_response(request, response) response.raise_for_status rescue StandardError => e - connection = @requests[request] - hostname = @queries.key(connection) - emit_resolve_error(connection, hostname, e) + hostname = @requests.delete(request) + connection = @queries.delete(hostname) + emit_resolve_error(connection, connection.origin.host, e) else # @type var response: HTTPX::Response - parse(response) + parse(request, response) ensure @requests.delete(request) end def on_promise(_, stream) log(level: 2) { "#{stream.id}: refusing stream!" } stream.refuse end - def parse(response) + def parse(request, response) begin answers = decode_response_body(response) rescue Resolv::DNS::DecodeError, JSON::JSONError => e host, connection = @queries.first @queries.delete(host) - emit_resolve_error(connection, host, e) + emit_resolve_error(connection, connection.origin.host, e) return end if answers.nil? || answers.empty? - host, connection = @queries.first - @queries.delete(host) - emit_resolve_error(connection, host) + host = @requests.delete(request) + connection = @queries.delete(host) + emit_resolve_error(connection) 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"]) if catch(:coalesced) { early_resolve(connection, hostname: address["alias"]) } @connections.delete(connection) else resolve(connection, address["alias"]) @@ -150,9 +166,13 @@ 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) + + # eliminate other candidates + @queries.delete_if { |_, conn| connection == conn } + 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?