Beispiel #1
0
class PeerProxy(object):
    class _States(object):
        (Awaiting_Handshake, Awaiting_Connection, Handshake_Initiated,
         Bitfield_Allowed, Peer_to_Peer, Disconnected) = range(6)

    def __init__(self, client, peer_id, addr, reactor,
                 protocol=None, info_hash=None):
        self._client = client
        self._reactor = reactor
        self._protocol = protocol
        self._info_hash = info_hash
        self._peer_id = peer_id
        self._addr = addr

        self._choked = True
        self._interested = False
        self._peer_choked = True
        self._peer_interested = False

        if len(peer_id) != 20:
            raise ValueError("Peer id must be 20 bytes long")

        if protocol is None:
            if info_hash is None or len(info_hash) != 20:
                raise ValueError("Info hash must be a 20 byte value")

            self._translator = None

            host, port = addr
            d = (TCP4ClientEndpoint(reactor, host, port)
                 .connect(ProtocolAdapterFactory(self)))
            d.addErrback(self.connection_failed)

            self._state = self._States.Awaiting_Connection
        else:
            self._setup_handshake_translator()
            self._state = self._States.Awaiting_Handshake

    def _setup_handshake_translator(self):
        self._translator = HandshakeTranslator(self, self._protocol)

    def _drop_connection(self, notify_client=True):
        if self._translator is not None:
            self._translator.unset_receiver()
            self._translator.unset_readerwriter()
            self._translator = None

        if self._protocol is not None:
            self._protocol.stop()

        self._state = self._States.Disconnected

        if notify_client:
            self._client.peer_unconnected(self)

    def _valid_rx_state(self):
        if self._state != self._States.Peer_to_Peer:
            if self._state == self._States.Bitfield_Allowed:
                self._state = self._States.Peer_to_Peer
            else:
                if self._state != self._States.Disconnected:
                    self._drop_connection()
                return False
        return True

    def _valid_tx_state(self):
        if self._state != self._States.Peer_to_Peer:
            if self._state == self._States.Bitfield_Allowed:
                self._state = self._States.Peer_to_Peer
            else:
                return False
        return True

    def addr(self):
        return self._addr

    def is_interested(self):
        return self._interested

    def is_choked(self):
        return self._choked

    def is_peer_choked(self):
        return self._peer_choked

    def is_peer_interested(self):
        return self._peer_interested

    # Callbacks which result from TCP4ClientEndpoint.connect()

    def connection_complete(self, protocol):
        self._protocol = protocol
        self._setup_handshake_translator()

        self._translator.tx_handshake(0, self._info_hash, self._peer_id)
        self._state = self._States.Handshake_Initiated

    def connection_failed(self, reason):
        self._client.peer_unconnected(self)

    # Translator callbacks

    def connection_lost(self):
        self._drop_connection()

    # HandshakeTranslator callbacks

    def rx_handshake(self, reserved, info_hash, peer_id):
        if self._state == self._States.Handshake_Initiated:
            if info_hash != self._info_hash:
                self._drop_connection()
            else:
                self._translator.unset_receiver()
                self._translator.unset_readerwriter()

                self._translator = PeerWireTranslator(self, self._protocol)
                self._state = self._States.Bitfield_Allowed

                self._translator.tx_bitfield(self._client.get_bitfield())

    def rx_non_handshake(self):
        self._drop_connection()

    # PeerWireTranslator callbacks

    def rx_bitfield(self, bitfield):
        if self._state == self._States.Bitfield_Allowed:
            self._state = self._States.Peer_to_Peer
            self._client.peer_bitfield(self, bitfield)
        else:
            self._drop_connection()

    def rx_keep_alive(self):
        pass

    def rx_choke(self):
        if self._valid_rx_state():
            self._peer_choked = True
            self._client.peer_choked(self)

    def rx_unchoke(self):
        if self._valid_rx_state():
            self._peer_choked = False
            self._client.peer_unchoked(self)

    def rx_interested(self):
        if self._valid_rx_state():
            self._peer_interested = True
            self._client.peer_interested(self)

    def rx_not_interested(self):
        if self._valid_rx_state():
            self._peer_interested = False
            self._client.peer_not_interested(self)

    def rx_have(self, index):
        if self._valid_rx_state():
            self._client.peer_has(self, index)

    def rx_request(self, index, begin, length):
        if self._valid_rx_state():
            self._client.peer_requests(self, index, begin, length)

    def rx_piece(self, index, begin, buf):
        if self._valid_rx_state():
            self._client.peer_sent_block(self, index, begin, buf)

    def rx_cancel(self, index, begin, length):
        if self._valid_rx_state():
            self._client.peer_canceled(self, index, begin, length)

    # Client calls

    def drop_connection(self):
        self._drop_connection(False)

    def choke(self):
        if self._valid_tx_state():
            self._choked = True
            self._translator.tx_choke()

    def unchoke(self):
        if self._valid_tx_state():
            self._choked = False
            self._translator.tx_unchoke()

    def interested(self):
        if self._valid_tx_state():
            self._interested = True
            self._translator.tx_interested()

    def not_interested(self):
        if self._valid_tx_state():
            self._interested = False
            self._translator.tx_not_interested()

    def have(self, index):
        if self._valid_tx_state():
            self._translator.tx_have(index)

    def request(self, index, begin, length):
        if self._valid_tx_state():
            self._translator.tx_request(index, begin, length)

    def piece(self, index, begin, buf, offset):
        if self._valid_tx_state():
            self._translator.tx_piece(index, begin, buf)

    def cancel(self, index, begin, length):
        if self._valid_tx_state():
            self._translator.tx_cancel(index, begin, length)