Example #1
0
    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)
Example #2
0
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))
Example #3
0
    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'])
Example #4
0
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))
Example #5
0
    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')
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
 def new_cache(cls) -> FIFOCache:
     '''Return a default cache'''
     return FIFOCache(max_items=100, time_to_live=3600)
Example #9
0
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)