Module: HTTPX::Resolver

Defined in:
lib/httpx/resolver.rb,
lib/httpx/resolver/entry.rb

Defined Under Namespace

Classes: Entry, HTTPS, Multi, Native, Resolver, System

Constant Summary collapse

RESOLVE_TIMEOUT =
[2, 3].freeze

Class Method Summary collapse

Class Method Details

.cached_lookup(hostname) ⇒ Object



58
59
60
61
62
63
# File 'lib/httpx/resolver.rb', line 58

def cached_lookup(hostname)
  now = Utils.now
  lookup_synchronize do |lookups|
    lookup(hostname, lookups, now)
  end
end

.cached_lookup_evict(hostname, ip) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/httpx/resolver.rb', line 86

def cached_lookup_evict(hostname, ip)
  ip = ip.to_s

  lookup_synchronize do |lookups|
    entries = lookups[hostname]

    return unless entries

    lookups.delete_if { |entry| entry["data"] == ip }
  end
end

.cached_lookup_set(hostname, family, entries) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/httpx/resolver.rb', line 65

def cached_lookup_set(hostname, family, entries)
  lookup_synchronize do |lookups|
    case family
    when Socket::AF_INET6
      lookups[hostname].concat(entries)
    when Socket::AF_INET
      lookups[hostname].unshift(*entries)
    end
    entries.each do |entry|
      next unless entry["name"] != hostname

      case family
      when Socket::AF_INET6
        lookups[entry["name"]] << entry
      when Socket::AF_INET
        lookups[entry["name"]].unshift(entry)
      end
    end
  end
end

.decode_dns_answer(payload) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/httpx/resolver.rb', line 128

def decode_dns_answer(payload)
  begin
    message = Resolv::DNS::Message.decode(payload)
  rescue Resolv::DNS::DecodeError => e
    return :decode_error, e
  end

  # no domain was found
  return :no_domain_found if message.rcode == Resolv::DNS::RCode::NXDomain

  return :message_truncated if message.tc == 1

  return :dns_error, message.rcode if message.rcode != Resolv::DNS::RCode::NoError

  addresses = []

  now = Utils.now
  message.each_answer do |question, _, value|
    case value
    when Resolv::DNS::Resource::IN::CNAME
      addresses << {
        "name" => question.to_s,
        "TTL" => (now + value.ttl),
        "alias" => value.name.to_s,
      }
    when Resolv::DNS::Resource::IN::A,
         Resolv::DNS::Resource::IN::AAAA
      addresses << {
        "name" => question.to_s,
        "TTL" => (now + value.ttl),
        "data" => value.address.to_s,
      }
    end
  end

  [:ok, addresses]
end

.encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id) ⇒ Object



121
122
123
124
125
126
# File 'lib/httpx/resolver.rb', line 121

def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id)
  Resolv::DNS::Message.new(message_id).tap do |query|
    query.rd = 1
    query.add_question(hostname, type)
  end.encode
end

.generate_idObject



117
118
119
# File 'lib/httpx/resolver.rb', line 117

def generate_id
  id_synchronize { @identifier = (@identifier + 1) & 0xFFFF }
end

.hosts_resolve(hostname) ⇒ Object

matches hostname to entries in the hosts file, returns <tt>nil</nil> if none is found, or there is no hosts file.



50
51
52
53
54
55
56
# File 'lib/httpx/resolver.rb', line 50

def hosts_resolve(hostname)
  ips = @hosts_resolver.getaddresses(hostname)
  return if ips.empty?

  ips.map { |ip| Entry.new(ip) }
rescue IOError
end

.id_synchronize(&block) ⇒ Object



170
171
172
# File 'lib/httpx/resolver.rb', line 170

def id_synchronize(&block)
  @identifier_mutex.synchronize(&block)
end

.ip_resolve(hostname) ⇒ Object

tries to convert hostname into an IPAddr, returns nil otherwise.



43
44
45
46
# File 'lib/httpx/resolver.rb', line 43

def ip_resolve(hostname)
  [Entry.new(hostname)]
rescue ArgumentError
end

.lookup(hostname, lookups, ttl) ⇒ Object

do not use directly!



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/httpx/resolver.rb', line 99

def lookup(hostname, lookups, ttl)
  return unless lookups.key?(hostname)

  entries = lookups[hostname] = lookups[hostname].select do |address|
    address["TTL"] > ttl
  end

  ips = entries.flat_map do |address|
    if (als = address["alias"])
      lookup(als, lookups, ttl)
    else
      Entry.new(address["data"], address["TTL"])
    end
  end.compact

  ips unless ips.empty?
end

.lookup_synchronizeObject



166
167
168
# File 'lib/httpx/resolver.rb', line 166

def lookup_synchronize
  @lookup_mutex.synchronize { yield(@lookups) }
end

.nolookup_resolve(hostname) ⇒ Object



38
39
40
# File 'lib/httpx/resolver.rb', line 38

def nolookup_resolve(hostname)
  ip_resolve(hostname) || cached_lookup(hostname) || hosts_resolve(hostname)
end

.resolver_for(resolver_type, options) ⇒ Object

Raises:



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/httpx/resolver.rb', line 25

def resolver_for(resolver_type, options)
  case resolver_type
  when Symbol
    meth = :"resolver_#{resolver_type}_class"

    return options.__send__(meth) if options.respond_to?(meth)
  when Class
    return resolver_type if resolver_type < Resolver
  end

  raise Error, "unsupported resolver type (#{resolver_type})"
end