Module: HTTPX::Resolver

Defined in:
lib/httpx/resolver.rb

Defined Under Namespace

Classes: HTTPS, Multi, Native, Resolver, System

Constant Summary collapse

RESOLVE_TIMEOUT =
[2, 3].freeze

Class Method Summary collapse

Class Method Details

.cached_lookup(hostname) ⇒ Object



54
55
56
57
58
59
# File 'lib/httpx/resolver.rb', line 54

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

.cached_lookup_set(hostname, family, entries) ⇒ Object



61
62
63
64
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 61

def cached_lookup_set(hostname, family, entries)
  now = Utils.now
  entries.each do |entry|
    entry["TTL"] += now
  end
  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



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/httpx/resolver.rb', line 116

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 = []

  message.each_answer do |question, _, value|
    case value
    when Resolv::DNS::Resource::IN::CNAME
      addresses << {
        "name" => question.to_s,
        "TTL" => value.ttl,
        "alias" => value.name.to_s,
      }
    when Resolv::DNS::Resource::IN::A,
         Resolv::DNS::Resource::IN::AAAA
      addresses << {
        "name" => question.to_s,
        "TTL" => 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



109
110
111
112
113
114
# File 'lib/httpx/resolver.rb', line 109

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



105
106
107
# File 'lib/httpx/resolver.rb', line 105

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

.id_synchronize(&block) ⇒ Object



157
158
159
# File 'lib/httpx/resolver.rb', line 157

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

.ip_resolve(hostname) ⇒ Object



41
42
43
44
# File 'lib/httpx/resolver.rb', line 41

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

.lookup(hostname, lookups, ttl) ⇒ Object

do not use directly!



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/httpx/resolver.rb', line 87

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 address.key?("alias")
      lookup(address["alias"], lookups, ttl)
    else
      IPAddr.new(address["data"])
    end
  end.compact

  ips unless ips.empty?
end

.lookup_synchronizeObject



153
154
155
# File 'lib/httpx/resolver.rb', line 153

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

.nolookup_resolve(hostname) ⇒ Object



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

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

.resolver_for(resolver_type) ⇒ Object



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

def resolver_for(resolver_type)
  case resolver_type
  when :native then Native
  when :system then System
  when :https then HTTPS
  else
    return resolver_type if resolver_type.is_a?(Class) && resolver_type < Resolver

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

.system_resolve(hostname) ⇒ Object



46
47
48
49
50
51
52
# File 'lib/httpx/resolver.rb', line 46

def system_resolve(hostname)
  ips = @system_resolver.getaddresses(hostname)
  return if ips.empty?

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