Class: HTTPX::Resolver

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Loggable
Defined in:
lib/httpx/io/resolver.rb

Constant Summary collapse

MAX_PACKET_SIZE =

Maximum UDP packet we’ll accept

512
DNS_PORT =
53

Constants included from Loggable

Loggable::COLORS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#log

Constructor Details

#initialize(options) ⇒ Resolver

Returns a new instance of Resolver.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/httpx/io/resolver.rb', line 27

def initialize(options)
  @options = Options.new(options)
  # early return for edge case when there are no nameservers configured
  # but we still want to be able to static lookups using #resolve_hostname
  (@nameservers = self.class.nameservers) || return
  server = IPAddr.new(@nameservers.sample)
  @io = UDP.new(server, DNS_PORT)
  @read_buffer = "".b
  @addresses = {}
  @hostnames = []
  @callbacks = []
  @state = :idle
end

Class Method Details

.generate_idObject



18
19
20
# File 'lib/httpx/io/resolver.rb', line 18

def self.generate_id
  @mutex.synchronize { @identifier = (@identifier + 1) & 0xFFFF }
end

.nameserversObject



22
23
24
# File 'lib/httpx/io/resolver.rb', line 22

def self.nameservers
  Resolv::DNS::Config.default_config_hash[:nameserver]
end

Instance Method Details

#callObject



61
62
63
64
65
# File 'lib/httpx/io/resolver.rb', line 61

def call
  return if @state == :closed
  return if @hostnames.empty?
  dread
end

#dread(wsize = MAX_PACKET_SIZE) ⇒ Object



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

def dread(wsize = MAX_PACKET_SIZE)
  loop do
    siz = @io.read(wsize, @read_buffer)
    throw(:close, self) unless siz
    return if siz.zero?
    log { "READ: #{siz} bytes..." }
    addrs = parse(@read_buffer)
    @read_buffer.clear
    next if addrs.empty?

    hostname = @hostnames.shift
    callback = @callbacks.shift
    addr = addrs.index(addrs.rand(addrs.size))
    log { "resolved #{hostname}: #{addr}" }
    @addresses[hostname] = addr
    callback.call(addr)
  end
end

#resolve(hostname, &action) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/httpx/io/resolver.rb', line 45

def resolve(hostname, &action)
  if host = resolve_hostname(hostname)
    unless ip_address = resolve_host(host)
      raise Resolv::ResolvError, "invalid entry in hosts file: #{host}"
    end
    @addresses[hostname] = ip_address
    action.call(ip_address)
  end
  @hostnames << hostname
  @callbacks << action
  query = build_query(hostname).encode
  log { "resolving #{hostname}: #{query.inspect}" }
  siz = @io.write(query)
  log { "WRITE: #{siz} bytes..." }
end

#to_ioObject



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

def to_io
  @io.to_io
end