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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

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

.id_synchronize(&block) ⇒ Object

[View source]

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

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

.ip_resolve(hostname) ⇒ Object

[View source]

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!

[View source]

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

[View source]

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

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

.nolookup_resolve(hostname) ⇒ Object

[View source]

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

[View source]

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

[View source]

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