def test_fifo_expire(self): cache = FIFOCache(time_to_live=0.1) cache['a'] = 1 self.assertIn('a', cache) self.assertEqual(1, cache['a']) time.sleep(0.2) self.assertNotIn('a', cache)
class HappyEyeballsTable(object): def __init__(self, max_items=100, time_to_live=600): '''Happy eyeballs connection cache table.''' self._cache = FIFOCache(max_items=max_items, time_to_live=time_to_live) def set_preferred(self, preferred_addr, addr_1, addr_2): '''Set the preferred address.''' if addr_1 > addr_2: addr_1, addr_2 = addr_2, addr_1 self._cache[(addr_1, addr_2)] = preferred_addr def get_preferred(self, addr_1, addr_2): '''Return the preferred address.''' if addr_1 > addr_2: addr_1, addr_2 = addr_2, addr_1 return self._cache.get((addr_1, addr_2))
def test_fifo_size(self): cache = FIFOCache(max_items=2) cache['a'] = 1 cache['b'] = 2 self.assertIn('a', cache) self.assertEqual(1, cache['a']) self.assertIn('b', cache) self.assertEqual(2, cache['b']) cache['c'] = 3 self.assertIn('c', cache) self.assertEqual(3, cache['c']) self.assertNotIn('a', cache) self.assertIn('b', cache) self.assertEqual(2, cache['b'])
def __init__(self, cache_enabled=True, family=PREFER_IPv4, timeout=None, rotate=False): super().__init__() assert family in (socket.AF_INET, socket.AF_INET6, self.PREFER_IPv4, self.PREFER_IPv6, None), \ 'Unknown family {}.'.format(family) if cache_enabled: self._cache = FIFOCache(max_items=100, time_to_live=3600) else: self._cache = None self._family = family self._timeout = timeout self._rotate = rotate self.register_hook('resolve_dns')
def __init__(self, max_items=100, time_to_live=600): '''Happy eyeballs connection cache table.''' self._cache = FIFOCache(max_items=max_items, time_to_live=time_to_live)
def new_cache(cls) -> FIFOCache: '''Return a default cache''' return FIFOCache(max_items=100, time_to_live=3600)
class Resolver(object): '''Asynchronous resolver with cache and timeout. Args: cache_enabled (bool): If True, resolved addresses are cached. families (iterable): A list containing the order of preferences of the IP address families. Only ``IPv4`` and ``IPv6`` are supported. timeout (int): A time in seconds used for timing-out requests. If not specified, this class relies on the underlying libraries. rotate (bool): If True and multiple addresses are resolved, randomly pick one. The cache holds 100 items and items expire after 1 hour. ''' IPv4 = socket.AF_INET '''Constant for IPv4.''' IPv6 = socket.AF_INET6 '''Constant for IPv6.''' global_cache = FIFOCache(max_items=100, time_to_live=3600) '''The cache for resolved addresses.''' def __init__(self, cache_enabled=True, families=(IPv4, IPv6), timeout=None, rotate=False): if cache_enabled: self._cache = self.global_cache else: self._cache = None self._families = families self._timeout = timeout self._rotate = rotate self._tornado_resolver = tornado.netutil.ThreadedResolver() @tornado.gen.coroutine def resolve(self, host, port): '''Resolve the given hostname and port. Args: host (str): The hostname. port (int): The port number. Returns: tuple: A tuple of length 2 where the first item is the family and the second item is address that can be passed to :func:`socket.connect`. Typically in an address, the first item is the IP family and the second item is the IP address. Note that IPv6 returns a tuple containing more items than 2. ''' _logger.debug('Lookup address {0} {1}.'.format(host, port)) addresses = [] for family in self._families: results = self._get_cache(host, port, family) if results is not None: _logger.debug('DNS cache hit.') addresses.extend(results) continue future = self._resolve_tornado(host, port, family) try: results = yield wpull. async .wait_future( future, self._timeout) except wpull. async .TimedOut as error: raise NetworkError('DNS resolve timed out') from error addresses.extend(results) self._put_cache(host, port, family, results) if not addresses: raise DNSNotFound( "DNS resolution for '{0}' did not return any results."\ .format(wpull.string.coerce_str_to_ascii(host)) ) _logger.debug('Resolved addresses: {0}.'.format(addresses)) if self._rotate: address = random.choice(addresses) else: address = addresses[0] _logger.debug('Selected {0} as address.'.format(address)) raise tornado.gen.Return(address)