示例#1
0
    def test_reactor(self):
        '''It should process 1 event and then stop'''

        my_id = EventID('my_id')
        self.test_value = False

        def my_callback(event_id):
            self.assertEqual(my_id, event_id)
            self.test_value = True

        event_reactor = EventReactor()
        event_reactor.register_handler(my_id, my_callback)
        event_reactor.put(my_id)
        event_reactor.put(EventReactor.STOP_ID)
        event_reactor.start()
        self.assertTrue(self.test_value)
示例#2
0
class Client(threading.Thread):
    '''Client interface.

    :warning: this class is under development.
    '''

    def __init__(self, cache_dir, address=('0.0.0.0', 0), node_id=None,
    known_node_address=None, initial_scan=False, config_dir=None,
    use_port_forwarding=False):
        threading.Thread.__init__(self)
        self.daemon = True
        self.name = '{}.{}'.format(__name__, Client.__name__)
        self._event_reactor = EventReactor()
        self._node_id = node_id or KeyBytes()
        self._network = Network(self._event_reactor, address=address)
        self._cache_table = DatabaseKVPTable(
            os.path.join(cache_dir, 'cache.db'))
        self._shared_files_table = SharedFilesKVPTable(
            os.path.join(cache_dir, 'shared_files.db'))
        self._aggregated_kvp_table = AggregatedKVPTable(self._cache_table,
            [self._cache_table, self._shared_files_table])
        self._known_node_address = known_node_address
        self._upload_slot = FnTaskSlot()
        self._download_slot = FnTaskSlot()
        self._initial_scan = initial_scan
        self._config_dir = config_dir or basedir.config_dir
        self._upnp_client = None
        
        if use_port_forwarding:
            if not miniupnpc:
                warnings.warn(
                    'miniupnpc not found. Port forwarding is unavailable!')
            else:
                self._init_port_forwarding()
                self._hook_port_forwarding_cleanup()

        self._init()

    @property
    def cache_table(self):
        '''The :class:`DatabaseKVPTable`'''
        return self._cache_table

    @property
    def shared_files_table(self):
        '''The :class:`SharedFilesKVPTable`'''

        return self._shared_files_table

    @property
    def upload_slot(self):
        '''The :class:`.FnTaskSlot` which holds :class:`.StoreValueTask`.'''

        return self._upload_slot

    @property
    def download_slot(self):
        '''Download slot.

        :see: :func:`.DHTNetwork.download_slot`
        '''

        return self._download_slot

    @property
    def dht_network(self):
        return self._dht_network

    @property
    def network(self):
        return self._network

    def _init_port_forwarding(self):
        upnp_client = miniupnpc.UPnP()
        self._upnp_client = upnp_client
        upnp_client.discoverdelay = 200

        num_devices = upnp_client.discover()

        _logger.debug('UPnP IGD count=%s', num_devices)

        if not num_devices:
            return

        upnp_client.selectigd()

        port = self._network.server_address[1]

        result = upnp_client.addportmapping(port, 'UDP', upnp_client.lanaddr,
            port, 'Bytestag', '')

        if result:
            _logger.info('UPnP port forwarded %s:%s', upnp_client.lanaddr,
                port)
        else:
            _logger.warning('UPnP port forward failed %s:%s',
                upnp_client.lanaddr, port)

    def _init(self):
        self._dht_network = DHTNetwork(self._event_reactor,
            self._aggregated_kvp_table, self._node_id, self._network,
            self._download_slot)
        self._publisher = Publisher(self._event_reactor, self._dht_network,
            self._aggregated_kvp_table, self._upload_slot)
        self._replicator = Replicator(self._event_reactor, self._dht_network,
            self._aggregated_kvp_table, self._upload_slot)
        self._downloader = Downloader(self._event_reactor, self._config_dir,
            self._dht_network, self._download_slot)

    def _hook_port_forwarding_cleanup(self):
        atexit.register(self._cleanup_port_forwarding)

    def run(self):
        if self._known_node_address:
            self._dht_network.join_network(self._known_node_address)
            # TODO: put warning if join fails, but don't check on
            # the same thread as the event_reactor

        if self._initial_scan:
            self._shared_files_table.hash_directories()

        self._event_reactor.start()

    def stop(self):
        self._event_reactor.put(EventReactor.STOP_ID)

        if self._upnp_client:
            self._cleanup_port_forwarding()

    def _cleanup_port_forwarding(self, *args):
        if self._upnp_client:
            upnp_client = self._upnp_client

            self._upnp_client = None

            upnp_client.deleteportmapping(self._network.server_address[1],
                'UDP')