add timeout support in DNS
This commit is contained in:
parent
5b193d6f92
commit
d4458bceb7
2 changed files with 28 additions and 14 deletions
|
@ -32,7 +32,7 @@ import lru_cache
|
||||||
import eventloop
|
import eventloop
|
||||||
|
|
||||||
|
|
||||||
CACHE_SWEEP_INTERVAL = 30
|
CACHE_SWEEP_INTERVAL = 10
|
||||||
|
|
||||||
VALID_HOSTNAME = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
VALID_HOSTNAME = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||||
|
|
||||||
|
@ -267,19 +267,23 @@ class DNSResolver(object):
|
||||||
self._hosts = {}
|
self._hosts = {}
|
||||||
self._hostname_status = {}
|
self._hostname_status = {}
|
||||||
self._hostname_to_cb = {}
|
self._hostname_to_cb = {}
|
||||||
self._cb_to_hostname = {}
|
self._cb_to_hostname = lru_cache.LRUCache(timeout=15,
|
||||||
|
close_callback=self._timedout)
|
||||||
self._cache = lru_cache.LRUCache(timeout=300)
|
self._cache = lru_cache.LRUCache(timeout=300)
|
||||||
self._last_time = time.time()
|
self._last_time = time.time()
|
||||||
self._sock = None
|
self._sock = None
|
||||||
|
self._servers = None
|
||||||
|
self._server_index = 0
|
||||||
self._parse_resolv()
|
self._parse_resolv()
|
||||||
self._parse_hosts()
|
self._parse_hosts()
|
||||||
# TODO monitor hosts change and reload hosts
|
# TODO monitor hosts change and reload hosts
|
||||||
# TODO parse /etc/gai.conf and follow its rules
|
# TODO parse /etc/gai.conf and follow its rules
|
||||||
|
|
||||||
def _parse_resolv(self):
|
def _parse_resolv(self):
|
||||||
|
servers = []
|
||||||
|
self._servers = servers
|
||||||
try:
|
try:
|
||||||
with open('/etc/resolv.conf', 'rb') as f:
|
with open('/etc/resolv.conf', 'rb') as f:
|
||||||
servers = []
|
|
||||||
content = f.readlines()
|
content = f.readlines()
|
||||||
for line in content:
|
for line in content:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
@ -291,12 +295,20 @@ class DNSResolver(object):
|
||||||
if is_ip(server):
|
if is_ip(server):
|
||||||
servers.append(server)
|
servers.append(server)
|
||||||
# TODO support more servers
|
# TODO support more servers
|
||||||
if servers:
|
|
||||||
self._dns_server = (servers[0], 53)
|
|
||||||
return
|
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
self._dns_server = ('8.8.8.8', 53)
|
if not servers:
|
||||||
|
servers.append('8.8.8.8')
|
||||||
|
self._dns_server = (servers[0], 53)
|
||||||
|
|
||||||
|
def _timedout(self, hostname):
|
||||||
|
error = Exception('timed out when resolving %s with DNS %s' %
|
||||||
|
(hostname, self._dns_server))
|
||||||
|
self._server_index += 1
|
||||||
|
self._server_index %= len(self._servers)
|
||||||
|
self._dns_server = (self._servers[self._server_index], 53)
|
||||||
|
self._call_callback(hostname, None, error=error)
|
||||||
|
|
||||||
def _parse_hosts(self):
|
def _parse_hosts(self):
|
||||||
etc_path = '/etc/hosts'
|
etc_path = '/etc/hosts'
|
||||||
|
@ -328,13 +340,13 @@ class DNSResolver(object):
|
||||||
loop.add(self._sock, eventloop.POLL_IN)
|
loop.add(self._sock, eventloop.POLL_IN)
|
||||||
loop.add_handler(self.handle_events)
|
loop.add_handler(self.handle_events)
|
||||||
|
|
||||||
def _call_callback(self, hostname, ip):
|
def _call_callback(self, hostname, ip, error=None):
|
||||||
callbacks = self._hostname_to_cb.get(hostname, [])
|
callbacks = self._hostname_to_cb.get(hostname, [])
|
||||||
for callback in callbacks:
|
for callback in callbacks:
|
||||||
if self._cb_to_hostname.__contains__(callback):
|
if self._cb_to_hostname.__contains__(callback):
|
||||||
del self._cb_to_hostname[callback]
|
del self._cb_to_hostname[callback]
|
||||||
if ip:
|
if ip or error:
|
||||||
callback((hostname, ip), None)
|
callback((hostname, ip), error)
|
||||||
else:
|
else:
|
||||||
callback((hostname, None),
|
callback((hostname, None),
|
||||||
Exception('unknown hostname %s' % hostname))
|
Exception('unknown hostname %s' % hostname))
|
||||||
|
@ -385,6 +397,7 @@ class DNSResolver(object):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now - self._last_time > CACHE_SWEEP_INTERVAL:
|
if now - self._last_time > CACHE_SWEEP_INTERVAL:
|
||||||
self._cache.sweep()
|
self._cache.sweep()
|
||||||
|
self._cb_to_hostname.sweep()
|
||||||
self._last_time = now
|
self._last_time = now
|
||||||
|
|
||||||
def remove_callback(self, callback):
|
def remove_callback(self, callback):
|
||||||
|
|
|
@ -50,13 +50,14 @@ class LRUCache(collections.MutableMapping):
|
||||||
least = self._last_visits[0]
|
least = self._last_visits[0]
|
||||||
if now - least <= self.timeout:
|
if now - least <= self.timeout:
|
||||||
break
|
break
|
||||||
|
if self.close_callback is not None:
|
||||||
|
for key in self._time_to_keys[least]:
|
||||||
|
if self._store.__contains__(key):
|
||||||
|
value = self._store[key]
|
||||||
|
self.close_callback(value)
|
||||||
for key in self._time_to_keys[least]:
|
for key in self._time_to_keys[least]:
|
||||||
heapq.heappop(self._last_visits)
|
heapq.heappop(self._last_visits)
|
||||||
if self._store.__contains__(key):
|
if self._store.__contains__(key):
|
||||||
value = self._store[key]
|
|
||||||
if self.close_callback is not None:
|
|
||||||
self.close_callback(value)
|
|
||||||
|
|
||||||
del self._store[key]
|
del self._store[key]
|
||||||
c += 1
|
c += 1
|
||||||
del self._time_to_keys[least]
|
del self._time_to_keys[least]
|
||||||
|
|
Loading…
Reference in a new issue