Esempio n. 1
0
class FileStreamClient(StreamClient):

    # TODO change this to a config file
    BUFFER = 1000000
    INTERVAL = 1

    def __init__(self):
        super(FileStreamClient, self).__init__()
        self.stream  = None
        self.reader = None

    def open_file(self, filename):
        self.stream = open(filename, 'rb')
        self.reader = TwistedRepeater(self._read_file, self.INTERVAL)
        self.reader.start_later()

    def _stop_reader(self):
        if self.reader is not None:
            self.reader.stop()

    def _read_file(self):
        if not self.stream:
            self._stop_reader()
            return

        data = self.stream.read(self.BUFFER)

        if len(data) > 0:
            self.on_stream_received.call(data)

        else:
            self.on_stream_end.call()
            self.stream.close()
            self._stop_reader()
Esempio n. 2
0
    def __init__(self, ip, port, tracker_url):
        """
        Inits the Peer application.

        :param ip: The IP address of the peer.
        :param port: The port to listen.
        :param tracker_url: The URL of the tracker.
        """

        self.port = port
        self.ip = ip

        self.peer_id = self._generate_peer_id()
        self.tracker_peers = PeerDatabase()

        self.piece_manager = None
        self.connection_manager = None
        self.tracker_manager = None
        self.joiner = None
        self.stream_server = None

        self._create_piece_manager()
        self._create_utility_manager()
        self._create_connection_manager()
        self._create_tracker_manager(tracker_url)
        self._create_joiner()

        # FIXME: seconds hardcoded
        self.logic_repeater = TwistedRepeater(self._peer_logic, 2)
        self.logic_repeater.start_later()
Esempio n. 3
0
    def __init__(self, tracker_url, peer):
        """
        Initializes the manager.
        """

        self._announce_repeater = TwistedRepeater(self.announce,
                                                  self.DEFAULT_ANNOUNCE)
        self._utility_repeater = TwistedRepeater(self.report_utility,
                                                 self.DEFAULT_ANNOUNCE)

        self.tracker_url = tracker_url
        self.peer = peer
        self.utility_by_peer = {}

        self.peer_list = []
        self.status = TrackerStatus(TrackerStatus.STANDBY, 'Ready to connect')

        self.on_updated = Event()
        self.on_status_changed = Event()
Esempio n. 4
0
class PeerService(object):
    """
    Controls every aspect of the peer application.
    """

    # TODO: Refactor this. Use specific methods.
    def __init__(self, ip, port, tracker_url):
        """
        Inits the Peer application.

        :param ip: The IP address of the peer.
        :param port: The port to listen.
        :param tracker_url: The URL of the tracker.
        """

        self.port = port
        self.ip = ip

        self.peer_id = self._generate_peer_id()
        self.tracker_peers = PeerDatabase()

        self.piece_manager = None
        self.connection_manager = None
        self.tracker_manager = None
        self.joiner = None
        self.stream_server = None

        self._create_piece_manager()
        self._create_utility_manager()
        self._create_connection_manager()
        self._create_tracker_manager(tracker_url)
        self._create_joiner()

        # FIXME: seconds hardcoded
        self.logic_repeater = TwistedRepeater(self._peer_logic, 2)
        self.logic_repeater.start_later()

    @property
    def pieces(self):
        return self.piece_manager.own_sequences

    def partner_got_piece(self, partner_id, piece):
        self.piece_manager.partner_got_piece(partner_id, piece)

    def partner_bitset(self, partner_id, pieces):
        self.piece_manager.partner_got_pieces(partner_id, pieces)

    def receive_request(self, partner_id, sequence):
        self.piece_manager.partner_requested_piece(partner_id, sequence)

    def receive_packet(self, packet, sender_id):
        self.joiner.push_packet(packet)
        self.piece_manager.add_new_piece(packet.sequence, packet.data)
        if sender_id is not None:
            self.utility_manager.add_peer_utility(sender_id, len(packet.data))

        for connection in self.connection_manager.all_connections:
            connection.send_got_piece(packet.sequence)

    def _peer_logic(self):
        logging.info('Executing Peer Logic')
        self._refresh_connections()
        self._contact_peers(self.tracker_peers)
        self._request_needed_pieces()
        self._send_requested_pieces()

        utility = self.utility_manager.utility_by_peer
        self.tracker_manager.utility_by_peer.update(utility)

    def _request_needed_pieces(self):

        missing_pieces = self.piece_manager.get_pieces_to_request()

        for piece in missing_pieces:
            partner_id = self.piece_manager.best_partner_for_piece(piece)
            if partner_id is None:
                logging.error('No partner for {0}'.format(piece))
                continue
            connection = self.connection_manager.get_connection(partner_id)
            if connection is None:
                logging.error('No connection for {0}'.format(partner_id))
                continue

            if self.piece_manager.can_request_piece(partner_id, piece):
                connection.send_request_packet(piece)
                self.piece_manager.mark_piece_as_requested(partner_id, piece)

    def _send_requested_pieces(self):
        for partner_id, sequence in self.piece_manager.get_pieces_to_send():
            data = self.piece_manager.get_piece_data(sequence)
            if data is None:
                logging.error('Dont have piece {0}'.format(sequence))
                return
            connection = self.connection_manager.get_connection(partner_id)
            if connection == None:
                logging.error('Dont have connection {0}'.format(partner_id))
                return
            connection.send_data_packet(sequence, data)
            self.piece_manager.mark_piece_as_sent(partner_id, sequence)
            logging.info('Sending data {0} to {1}'.format(sequence, partner_id))

    def _update_peers(self, sender, peer_list):
        self.tracker_peers.update_peers(peer_list)
        self.tracker_peers.remove_peer(self.peer_id)

        peers = '|'.join(str(p) for p in self.tracker_manager.peer_list)
        logging.debug('Tracker updated: {0}'.format(peers))

    def _contact_peers(self, peers):
        self.connection_manager.connect_to_peers(peers)

    def _refresh_connections(self):
        self.connection_manager.heartbeat()
        self.connection_manager.check_heartbeats()

    def _data_joined(self, joiner):
        if self.stream_server:
            self.stream_server.send_stream(joiner.pop_stream())
        else:
            logging.debug('Data joined without stream_server')

    def _create_piece_manager(self):
        self.piece_manager = PieceManager()

    def _create_connection_manager(self):
        self.connection_manager = ConnectionManager(self)

    def _create_tracker_manager(self, tracker_url):
        peer = Peer(self.peer_id, self.ip, self.port)
        self.tracker_manager = TrackerManager(tracker_url, peer)

        self.tracker_manager.on_updated.add_handler(self._update_peers)

    def _create_joiner(self):
        self.joiner = Joiner()
        self.joiner.on_data_joined.add_handler(self._data_joined)

    def _create_utility_manager(self):
        self.utility_manager = UtilityManager()

    def _generate_peer_id(self):
        id = uuid.uuid4().hex
        id = id[:14]
        ## TESTING
        id = format(self.port, '014d')
        ## END TESTING
        peer_id = 'PX0001' +  id
        logging.debug('Generated Peer ID: "{0}"'.format(peer_id))
        return peer_id
Esempio n. 5
0
class TrackerManager(object):
    """
    Manages the connection with the tracker
    """

    DEFAULT_ANNOUNCE = 30

    #TODO: Eliminate tracker_url parameter
    def __init__(self, tracker_url, peer):
        """
        Initializes the manager.
        """

        self._announce_repeater = TwistedRepeater(self.announce,
                                                  self.DEFAULT_ANNOUNCE)
        self._utility_repeater = TwistedRepeater(self.report_utility,
                                                 self.DEFAULT_ANNOUNCE)

        self.tracker_url = tracker_url
        self.peer = peer
        self.utility_by_peer = {}

        self.peer_list = []
        self.status = TrackerStatus(TrackerStatus.STANDBY, 'Ready to connect')

        self.on_updated = Event()
        self.on_status_changed = Event()

    def connect_to_tracker(self):
        self._announce_repeater.start_now()
        self._utility_repeater.start_now()

    def announce(self):
        """Starts connection with the tracker."""

        logging.debug('Announcing to tracker')
        deferred = client.getPage(self._create_announce_url())
        deferred.addCallback(self._on_announce_response)
        deferred.addErrback(self._on_tracker_error)

    def report_utility(self):
        if len(self.utility_by_peer) == 0:
            return
        deferred = client.getPage(self._create_utility_url())
        deferred.addCallback(self._on_utility_response)
        deferred.addErrback(self._on_tracker_error)

    def _create_announce_url(self):
        """Creates a request URL for the tracker with the GET query."""

        query = dict(peer_id = self.peer.id,
                     port = self.peer.port)

        if self.peer.ip is not None:
            query.update(ip = self.peer.ip)

        parts = list(urlparse.urlsplit(self.tracker_url))
        parts[2] += '/announce' # path
        parts[3] = urllib.urlencode(query) # query

        return urlparse.urlunsplit(parts)

    def _create_utility_url(self):
        parts = list(urlparse.urlsplit(self.tracker_url))
        parts[2] += '/utility'
        parts[3] = urllib.urlencode(self.utility_by_peer) # query.

        return urlparse.urlunsplit(parts)

    def _on_tracker_error(self, error):
        logging.error('Unable to contact the tracker: ' + str(error.value))
        self._change_status(TrackerStatus.ERROR,
                            'Unable to contact the tracker')

    def _on_announce_response(self, content):
        try:
            logging.debug('Received tracker announce response')
            response = json.loads(content)

            if 'failure_reason' in response:
                self._on_announce_failure(response['failure_reason'])
                return

            if 'request_interval' in response:
                self._announce_repeater.seconds = response['request_interval']

            if 'peers' in response:
                self._update_peers(response['peers'])

            self._change_status(TrackerStatus.OK, 'OK')

        except Exception as e:
            self._on_announce_failure(str(e))

    def _on_announce_failure(self, error):
        logging.error('Tracker announce failure: ' + error)
        self._change_status(TrackerStatus.ERROR, error)

    def _on_utility_response(self, content):
        logging.debug('Received tracker utility response')
        response = json.loads(content)

        if 'failure_reason' in response:
            self._on_utility_failure(response['failure_reason'])
            return

    def _on_utility_failure(self, error):
        logging.error('Tracker utility failure: ' + error)
        self._change_status(TrackerStatus.ERROR, error)

    def _update_peers(self, peer_list):
        try:
            self.peer_list = []
            for peer_dict in peer_list:
                id = peer_dict['id']
                ip = peer_dict['ip']
                port = peer_dict['port']
                uf = peer_dict['utility_factor']
                self.peer_list.append(Peer(id, ip, port, uf))
            self.on_updated.call(self, self.peer_list)
        except Exception as e:
            raise TrackerManagerError('Unable to convert peer list: ' + str(e))

    def _change_status(self, status, message):
        if self.status.status == status and self.status.message == message:
            return
        self.status = TrackerStatus(status, message)
        self.on_status_changed.call(self, self.status)
Esempio n. 6
0
 def open_file(self, filename):
     self.stream = open(filename, 'rb')
     self.reader = TwistedRepeater(self._read_file, self.INTERVAL)
     self.reader.start_later()