Class: HTTPX::Resolver::Cache::Base

Inherits:
Object
  • Object
show all
Includes:
_Cache
Defined in:
lib/httpx/resolver/cache/base.rb,
sig/resolver/cache/base.rbs

Overview

Base class of the Resolver Cache adapter implementations.

While resolver caches are not required to inherit from this class, it nevertheless provides common useful functions for desired functionality, such as singleton object ractor-safe access, or a default #resolve implementation which deals with IPs and the system hosts file.

Direct Known Subclasses

File, Memory

Constant Summary collapse

MAX_CACHE_SIZE =

Returns:

  • (Integer)
512
CACHE_MUTEX =

Returns:

  • (Thread::Mutex)
Thread::Mutex.new
HOSTS =

Returns:

  • (Resolv::Hosts)
Resolv::Hosts.new

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from _Cache

#evict, #get, #set

Class Attribute Details

.hosts_resolverObject (readonly)

Returns the value of attribute hosts_resolver.



20
21
22
# File 'lib/httpx/resolver/cache/base.rb', line 20

def hosts_resolver
  @hosts_resolver
end

Class Method Details

.cache(label) ⇒ instance

returns the singleton instance to be used within the current ractor.

Parameters:

  • label (Symbol)

Returns:

  • (instance)


23
24
25
26
27
28
29
# File 'lib/httpx/resolver/cache/base.rb', line 23

def cache(label)
  return Ractor.store_if_absent(:"httpx_resolver_cache_#{label}") { new } if Utils.in_ractor?

  @cache ||= CACHE_MUTEX.synchronize do
    @cache || new
  end
end

Instance Method Details

#_evict(hostname, ip, lookups, hostnames) ⇒ void

This method returns an undefined value.

Parameters:

  • hostname (String)
  • ip (String)
  • lookups (Hash[String, Array[dns_result]])
  • hostnames (Array[String])


113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/httpx/resolver/cache/base.rb', line 113

def _evict(hostname, ip, lookups, hostnames)
  return unless lookups.key?(hostname)

  entries = lookups[hostname]

  return unless entries

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

  return unless entries.empty?

  lookups.delete(hostname)
  hostnames.delete(hostname)
end

#_get(hostname, lookups, hostnames, ttl) ⇒ Array[Entry]?

not to be used directly!

Parameters:

  • hostname (String)
  • lookups (Hash[String, Array[dns_result]])
  • hostnames (Array[String])
  • ttl (Numeric)

Returns:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/httpx/resolver/cache/base.rb', line 62

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

  entries = lookups[hostname]

  return unless entries

  entries.delete_if do |address|
    address["TTL"] < ttl
  end

  if entries.empty?
    lookups.delete(hostname)
    hostnames.delete(hostname)
  end

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

  ips unless ips.empty?
end

#_set(hostname, family, entries, lookups, hostnames) ⇒ void

This method returns an undefined value.

Parameters:

  • hostname (String)
  • family (ip_family)
  • entries (Array[dns_result])
  • lookups (Hash[String, Array[dns_result]])
  • hostnames (Array[String])


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/httpx/resolver/cache/base.rb', line 89

def _set(hostname, family, entries, lookups, hostnames)
  # lru cleanup
  while lookups.size >= MAX_CACHE_SIZE
    hs = hostnames.shift
    lookups.delete(hs)
  end
  hostnames << hostname

  lookups[hostname] ||= [] # when there's no default proc

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

    _set(name, family, [entry], lookups, hostnames)
  end
end

#hosts_resolve(hostname) ⇒ Array[Entry]?

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

Parameters:

  • hostname (String)

Returns:



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/httpx/resolver/cache/base.rb', line 48

def hosts_resolve(hostname)
  ips = if Utils.in_ractor?
    Ractor.store_if_absent(:httpx_hosts_resolver) { Resolv::Hosts.new }
  else
    HOSTS
  end.getaddresses(hostname)

  return if ips.empty?

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

#ip_resolve(hostname) ⇒ Array[Entry]?

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

Parameters:

  • hostname (String)

Returns:



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

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

#resolve(hostname) ⇒ Object

resolves hostname into an instance of HTTPX::Resolver::Entry if hostname is an IP, or can be found in the cache, or can be found in the system hosts file.



34
35
36
# File 'lib/httpx/resolver/cache/base.rb', line 34

def resolve(hostname)
  ip_resolve(hostname) || get(hostname) || hosts_resolve(hostname)
end