Add some internal documenation
This commit is contained in:
		
							parent
							
								
									44dc2d06c6
								
							
						
					
					
						commit
						464dc23ff0
					
				@ -1,6 +1,6 @@
 | 
				
			|||||||
##########################################################
 | 
					##########################################################
 | 
				
			||||||
# This RNS example demonstrates how to set up a link to  #
 | 
					# This RNS example demonstrates how to set up a link to  #
 | 
				
			||||||
# a destination, and pass structuredmessages over it     #
 | 
					# a destination, and pass structured messages over it    #
 | 
				
			||||||
# using a channel.                                       #
 | 
					# using a channel.                                       #
 | 
				
			||||||
##########################################################
 | 
					##########################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,7 +46,7 @@ class StringMessage(RNS.MessageBase):
 | 
				
			|||||||
    # message arrives over the channel.
 | 
					    # message arrives over the channel.
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # MSGTYPE must be unique across all message types we
 | 
					    # MSGTYPE must be unique across all message types we
 | 
				
			||||||
    # register with the channel. MSGTYPEs >= 0xff00 are
 | 
					    # register with the channel. MSGTYPEs >= 0xf000 are
 | 
				
			||||||
    # reserved for the system.
 | 
					    # reserved for the system.
 | 
				
			||||||
    MSGTYPE = 0x0101
 | 
					    MSGTYPE = 0x0101
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -159,17 +159,36 @@ def client_disconnected(link):
 | 
				
			|||||||
    RNS.log("Client disconnected")
 | 
					    RNS.log("Client disconnected")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def server_message_received(message):
 | 
					def server_message_received(message):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A message handler
 | 
				
			||||||
 | 
					    @param message: An instance of a subclass of MessageBase
 | 
				
			||||||
 | 
					    @return: True if message was handled
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    global latest_client_link
 | 
					    global latest_client_link
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # When a message is received over any active link,
 | 
					    # When a message is received over any active link,
 | 
				
			||||||
    # the replies will all be directed to the last client
 | 
					    # the replies will all be directed to the last client
 | 
				
			||||||
    # that connected.
 | 
					    # that connected.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # In a message handler, any deserializable message
 | 
				
			||||||
 | 
					    # that arrives over the link's channel will be passed
 | 
				
			||||||
 | 
					    # to all message handlers, unless a preceding handler indicates it
 | 
				
			||||||
 | 
					    # has handled the message.
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
    if isinstance(message, StringMessage):
 | 
					    if isinstance(message, StringMessage):
 | 
				
			||||||
        RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")")
 | 
					        RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        reply_message = StringMessage("I received \""+message.data+"\" over the link")
 | 
					        reply_message = StringMessage("I received \""+message.data+"\" over the link")
 | 
				
			||||||
        latest_client_link.get_channel().send(reply_message)
 | 
					        latest_client_link.get_channel().send(reply_message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Incoming messages are sent to each message
 | 
				
			||||||
 | 
					        # handler added to the channel, in the order they
 | 
				
			||||||
 | 
					        # were added.
 | 
				
			||||||
 | 
					        # If any message handler returns True, the message
 | 
				
			||||||
 | 
					        # is considered handled and any subsequent
 | 
				
			||||||
 | 
					        # handlers are skipped.
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##########################################################
 | 
					##########################################################
 | 
				
			||||||
#### Client Part #########################################
 | 
					#### Client Part #########################################
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										109
									
								
								RNS/Channel.py
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								RNS/Channel.py
									
									
									
									
									
								
							@ -14,6 +14,13 @@ TPacket = TypeVar("TPacket")
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ChannelOutletBase(ABC, Generic[TPacket]):
 | 
					class ChannelOutletBase(ABC, Generic[TPacket]):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    An abstract transport layer interface used by Channel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DEPRECATED: This was created for testing; eventually
 | 
				
			||||||
 | 
					    Channel will use Link or a LinkBase interface
 | 
				
			||||||
 | 
					    directly.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    @abstractmethod
 | 
					    @abstractmethod
 | 
				
			||||||
    def send(self, raw: bytes) -> TPacket:
 | 
					    def send(self, raw: bytes) -> TPacket:
 | 
				
			||||||
        raise NotImplemented()
 | 
					        raise NotImplemented()
 | 
				
			||||||
@ -64,6 +71,9 @@ class ChannelOutletBase(ABC, Generic[TPacket]):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CEType(enum.IntEnum):
 | 
					class CEType(enum.IntEnum):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    ChannelException type codes
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    ME_NO_MSG_TYPE      = 0
 | 
					    ME_NO_MSG_TYPE      = 0
 | 
				
			||||||
    ME_INVALID_MSG_TYPE = 1
 | 
					    ME_INVALID_MSG_TYPE = 1
 | 
				
			||||||
    ME_NOT_REGISTERED   = 2
 | 
					    ME_NOT_REGISTERED   = 2
 | 
				
			||||||
@ -73,12 +83,18 @@ class CEType(enum.IntEnum):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ChannelException(Exception):
 | 
					class ChannelException(Exception):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    An exception thrown by Channel, with a type code.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    def __init__(self, ce_type: CEType, *args):
 | 
					    def __init__(self, ce_type: CEType, *args):
 | 
				
			||||||
        super().__init__(args)
 | 
					        super().__init__(args)
 | 
				
			||||||
        self.type = ce_type
 | 
					        self.type = ce_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MessageState(enum.IntEnum):
 | 
					class MessageState(enum.IntEnum):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Set of possible states for a Message
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    MSGSTATE_NEW       = 0
 | 
					    MSGSTATE_NEW       = 0
 | 
				
			||||||
    MSGSTATE_SENT      = 1
 | 
					    MSGSTATE_SENT      = 1
 | 
				
			||||||
    MSGSTATE_DELIVERED = 2
 | 
					    MSGSTATE_DELIVERED = 2
 | 
				
			||||||
@ -86,14 +102,29 @@ class MessageState(enum.IntEnum):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MessageBase(abc.ABC):
 | 
					class MessageBase(abc.ABC):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Base type for any messages sent or received on a Channel.
 | 
				
			||||||
 | 
					    Subclasses must define the two abstract methods as well as
 | 
				
			||||||
 | 
					    the MSGTYPE class variable.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # MSGTYPE must be unique within all classes sent over a
 | 
				
			||||||
 | 
					    # channel. Additionally, MSGTYPE > 0xf000 are reserved.
 | 
				
			||||||
    MSGTYPE = None
 | 
					    MSGTYPE = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @abstractmethod
 | 
					    @abstractmethod
 | 
				
			||||||
    def pack(self) -> bytes:
 | 
					    def pack(self) -> bytes:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Create and return the binary representation of the message
 | 
				
			||||||
 | 
					        @return: binary representation of message
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        raise NotImplemented()
 | 
					        raise NotImplemented()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @abstractmethod
 | 
					    @abstractmethod
 | 
				
			||||||
    def unpack(self, raw):
 | 
					    def unpack(self, raw):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Populate message from binary representation
 | 
				
			||||||
 | 
					        @param raw: binary representation
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        raise NotImplemented()
 | 
					        raise NotImplemented()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,6 +132,10 @@ MessageCallbackType = NewType("MessageCallbackType", Callable[[MessageBase], boo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Envelope:
 | 
					class Envelope:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Internal wrapper used to transport messages over a channel and
 | 
				
			||||||
 | 
					    track its state within the channel framework.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    def unpack(self, message_factories: dict[int, Type]) -> MessageBase:
 | 
					    def unpack(self, message_factories: dict[int, Type]) -> MessageBase:
 | 
				
			||||||
        msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6])
 | 
					        msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6])
 | 
				
			||||||
        raw = self.raw[6:]
 | 
					        raw = self.raw[6:]
 | 
				
			||||||
@ -131,6 +166,12 @@ class Envelope:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Channel(contextlib.AbstractContextManager):
 | 
					class Channel(contextlib.AbstractContextManager):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Channel provides reliable delivery of messages over
 | 
				
			||||||
 | 
					    a link. Channel is not meant to be instantiated
 | 
				
			||||||
 | 
					    directly, but rather obtained from a Link using the
 | 
				
			||||||
 | 
					    get_channel() function.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    def __init__(self, outlet: ChannelOutletBase):
 | 
					    def __init__(self, outlet: ChannelOutletBase):
 | 
				
			||||||
        self._outlet = outlet
 | 
					        self._outlet = outlet
 | 
				
			||||||
        self._lock = threading.RLock()
 | 
					        self._lock = threading.RLock()
 | 
				
			||||||
@ -146,10 +187,14 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __exit__(self, __exc_type: Type[BaseException] | None, __exc_value: BaseException | None,
 | 
					    def __exit__(self, __exc_type: Type[BaseException] | None, __exc_value: BaseException | None,
 | 
				
			||||||
                 __traceback: TracebackType | None) -> bool | None:
 | 
					                 __traceback: TracebackType | None) -> bool | None:
 | 
				
			||||||
        self.shutdown()
 | 
					        self._shutdown()
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def register_message_type(self, message_class: Type[MessageBase], *, is_system_type: bool = False):
 | 
					    def register_message_type(self, message_class: Type[MessageBase], *, is_system_type: bool = False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Register a message class for reception over a channel.
 | 
				
			||||||
 | 
					        @param message_class: Class to register. Must extend MessageBase.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            if not issubclass(message_class, MessageBase):
 | 
					            if not issubclass(message_class, MessageBase):
 | 
				
			||||||
                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
					                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
				
			||||||
@ -157,7 +202,7 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            if message_class.MSGTYPE is None:
 | 
					            if message_class.MSGTYPE is None:
 | 
				
			||||||
                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
					                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
				
			||||||
                                       f"{message_class} has invalid MSGTYPE class attribute.")
 | 
					                                       f"{message_class} has invalid MSGTYPE class attribute.")
 | 
				
			||||||
            if message_class.MSGTYPE >= 0xff00 and not is_system_type:
 | 
					            if message_class.MSGTYPE >= 0xf000 and not is_system_type:
 | 
				
			||||||
                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
					                raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
 | 
				
			||||||
                                       f"{message_class} has system-reserved message type.")
 | 
					                                       f"{message_class} has system-reserved message type.")
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
@ -169,20 +214,34 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            self._message_factories[message_class.MSGTYPE] = message_class
 | 
					            self._message_factories[message_class.MSGTYPE] = message_class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_message_handler(self, callback: MessageCallbackType):
 | 
					    def add_message_handler(self, callback: MessageCallbackType):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Add a handler for incoming messages. A handler
 | 
				
			||||||
 | 
					        has the signature (message: MessageBase) -> bool.
 | 
				
			||||||
 | 
					        Handlers are processed in the order they are
 | 
				
			||||||
 | 
					        added. If any handler returns True, processing
 | 
				
			||||||
 | 
					        of the message stops; handlers after the
 | 
				
			||||||
 | 
					        returning handler will not be called.
 | 
				
			||||||
 | 
					        @param callback: Function to call
 | 
				
			||||||
 | 
					        @return:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            if callback not in self._message_callbacks:
 | 
					            if callback not in self._message_callbacks:
 | 
				
			||||||
                self._message_callbacks.append(callback)
 | 
					                self._message_callbacks.append(callback)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def remove_message_handler(self, callback: MessageCallbackType):
 | 
					    def remove_message_handler(self, callback: MessageCallbackType):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Remove a handler
 | 
				
			||||||
 | 
					        @param callback: handler to remove
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self._message_callbacks.remove(callback)
 | 
					            self._message_callbacks.remove(callback)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def shutdown(self):
 | 
					    def _shutdown(self):
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self._message_callbacks.clear()
 | 
					            self._message_callbacks.clear()
 | 
				
			||||||
            self.clear_rings()
 | 
					            self._clear_rings()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def clear_rings(self):
 | 
					    def _clear_rings(self):
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            for envelope in self._tx_ring:
 | 
					            for envelope in self._tx_ring:
 | 
				
			||||||
                if envelope.packet is not None:
 | 
					                if envelope.packet is not None:
 | 
				
			||||||
@ -191,14 +250,15 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            self._tx_ring.clear()
 | 
					            self._tx_ring.clear()
 | 
				
			||||||
            self._rx_ring.clear()
 | 
					            self._rx_ring.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
 | 
					    def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            i = 0
 | 
					            i = 0
 | 
				
			||||||
            for env in ring:
 | 
					            for existing in ring:
 | 
				
			||||||
                if env.sequence < envelope.sequence:
 | 
					                if existing.sequence > envelope.sequence \
 | 
				
			||||||
 | 
					                   and not existing.sequence // 2 > envelope.sequence:  # account for overflow
 | 
				
			||||||
                    ring.insert(i, envelope)
 | 
					                    ring.insert(i, envelope)
 | 
				
			||||||
                    return True
 | 
					                    return True
 | 
				
			||||||
                if env.sequence == envelope.sequence:
 | 
					                if existing.sequence == envelope.sequence:
 | 
				
			||||||
                    RNS.log(f"Envelope: Emplacement of duplicate envelope sequence.", RNS.LOG_EXTREME)
 | 
					                    RNS.log(f"Envelope: Emplacement of duplicate envelope sequence.", RNS.LOG_EXTREME)
 | 
				
			||||||
                    return False
 | 
					                    return False
 | 
				
			||||||
                i += 1
 | 
					                i += 1
 | 
				
			||||||
@ -206,7 +266,7 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            ring.append(envelope)
 | 
					            ring.append(envelope)
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def prune_rx_ring(self):
 | 
					    def _prune_rx_ring(self):
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            # Implementation for fixed window = 1
 | 
					            # Implementation for fixed window = 1
 | 
				
			||||||
            stale = list(sorted(self._rx_ring, key=lambda env: env.sequence, reverse=True))[1:]
 | 
					            stale = list(sorted(self._rx_ring, key=lambda env: env.sequence, reverse=True))[1:]
 | 
				
			||||||
@ -225,13 +285,13 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            except Exception as ex:
 | 
					            except Exception as ex:
 | 
				
			||||||
                RNS.log(f"Channel: Error running message callback: {ex}", RNS.LOG_ERROR)
 | 
					                RNS.log(f"Channel: Error running message callback: {ex}", RNS.LOG_ERROR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def receive(self, raw: bytes):
 | 
					    def _receive(self, raw: bytes):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            envelope = Envelope(outlet=self._outlet, raw=raw)
 | 
					            envelope = Envelope(outlet=self._outlet, raw=raw)
 | 
				
			||||||
            with self._lock:
 | 
					            with self._lock:
 | 
				
			||||||
                message = envelope.unpack(self._message_factories)
 | 
					                message = envelope.unpack(self._message_factories)
 | 
				
			||||||
                is_new = self.emplace_envelope(envelope, self._rx_ring)
 | 
					                is_new = self._emplace_envelope(envelope, self._rx_ring)
 | 
				
			||||||
                self.prune_rx_ring()
 | 
					                self._prune_rx_ring()
 | 
				
			||||||
            if not is_new:
 | 
					            if not is_new:
 | 
				
			||||||
                RNS.log("Channel: Duplicate message received", RNS.LOG_DEBUG)
 | 
					                RNS.log("Channel: Duplicate message received", RNS.LOG_DEBUG)
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
@ -241,6 +301,10 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
            RNS.log(f"Channel: Error receiving data: {ex}")
 | 
					            RNS.log(f"Channel: Error receiving data: {ex}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_ready_to_send(self) -> bool:
 | 
					    def is_ready_to_send(self) -> bool:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Check if Channel is ready to send.
 | 
				
			||||||
 | 
					        @return: True if ready
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        if not self._outlet.is_usable:
 | 
					        if not self._outlet.is_usable:
 | 
				
			||||||
            RNS.log("Channel: Link is not usable.", RNS.LOG_EXTREME)
 | 
					            RNS.log("Channel: Link is not usable.", RNS.LOG_EXTREME)
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
@ -273,7 +337,7 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
        def retry_envelope(envelope: Envelope) -> bool:
 | 
					        def retry_envelope(envelope: Envelope) -> bool:
 | 
				
			||||||
            if envelope.tries >= self._max_tries:
 | 
					            if envelope.tries >= self._max_tries:
 | 
				
			||||||
                RNS.log("Channel: Retry count exceeded, tearing down Link.", RNS.LOG_ERROR)
 | 
					                RNS.log("Channel: Retry count exceeded, tearing down Link.", RNS.LOG_ERROR)
 | 
				
			||||||
                self.shutdown()  # start on separate thread?
 | 
					                self._shutdown()  # start on separate thread?
 | 
				
			||||||
                self._outlet.timed_out()
 | 
					                self._outlet.timed_out()
 | 
				
			||||||
                return True
 | 
					                return True
 | 
				
			||||||
            envelope.tries += 1
 | 
					            envelope.tries += 1
 | 
				
			||||||
@ -283,13 +347,18 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
        self._packet_tx_op(packet, retry_envelope)
 | 
					        self._packet_tx_op(packet, retry_envelope)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def send(self, message: MessageBase) -> Envelope:
 | 
					    def send(self, message: MessageBase) -> Envelope:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Send a message. If a message send is attempted and
 | 
				
			||||||
 | 
					        Channel is not ready, an exception is thrown.
 | 
				
			||||||
 | 
					        @param message: an instance of a MessageBase subclass to send on the Channel
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        envelope: Envelope | None = None
 | 
					        envelope: Envelope | None = None
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            if not self.is_ready_to_send():
 | 
					            if not self.is_ready_to_send():
 | 
				
			||||||
                raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
 | 
					                raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
 | 
				
			||||||
            envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
 | 
					            envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
 | 
				
			||||||
            self._next_sequence = (self._next_sequence + 1) % 0x10000
 | 
					            self._next_sequence = (self._next_sequence + 1) % 0x10000
 | 
				
			||||||
            self.emplace_envelope(envelope, self._tx_ring)
 | 
					            self._emplace_envelope(envelope, self._tx_ring)
 | 
				
			||||||
        if envelope is None:
 | 
					        if envelope is None:
 | 
				
			||||||
            raise BlockingIOError()
 | 
					            raise BlockingIOError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -304,10 +373,20 @@ class Channel(contextlib.AbstractContextManager):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def MDU(self):
 | 
					    def MDU(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Maximum Data Unit: the number of bytes available
 | 
				
			||||||
 | 
					        for a message to consume in a single send.
 | 
				
			||||||
 | 
					        @return: number of bytes available
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        return self._outlet.mdu - 6  # sizeof(msgtype) + sizeof(length) + sizeof(sequence)
 | 
					        return self._outlet.mdu - 6  # sizeof(msgtype) + sizeof(length) + sizeof(sequence)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LinkChannelOutlet(ChannelOutletBase):
 | 
					class LinkChannelOutlet(ChannelOutletBase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    An implementation of ChannelOutletBase for RNS.Link.
 | 
				
			||||||
 | 
					    Allows Channel to send packets over an RNS Link with
 | 
				
			||||||
 | 
					    Packets.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    def __init__(self, link: RNS.Link):
 | 
					    def __init__(self, link: RNS.Link):
 | 
				
			||||||
        self.link = link
 | 
					        self.link = link
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -464,7 +464,7 @@ class Link:
 | 
				
			|||||||
        for resource in self.outgoing_resources:
 | 
					        for resource in self.outgoing_resources:
 | 
				
			||||||
            resource.cancel()
 | 
					            resource.cancel()
 | 
				
			||||||
        if self._channel:
 | 
					        if self._channel:
 | 
				
			||||||
            self._channel.shutdown()
 | 
					            self._channel._shutdown()
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        self.prv = None
 | 
					        self.prv = None
 | 
				
			||||||
        self.pub = None
 | 
					        self.pub = None
 | 
				
			||||||
@ -801,7 +801,7 @@ class Link:
 | 
				
			|||||||
                            RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
 | 
					                            RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
 | 
				
			||||||
                        else:
 | 
					                        else:
 | 
				
			||||||
                            plaintext = self.decrypt(packet.data)
 | 
					                            plaintext = self.decrypt(packet.data)
 | 
				
			||||||
                            self._channel.receive(plaintext)
 | 
					                            self._channel._receive(plaintext)
 | 
				
			||||||
                            packet.prove()
 | 
					                            packet.prove()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                elif packet.packet_type == RNS.Packet.PROOF:
 | 
					                elif packet.packet_type == RNS.Packet.PROOF:
 | 
				
			||||||
 | 
				
			|||||||
@ -153,7 +153,7 @@ class ProtocolHarness(contextlib.AbstractContextManager):
 | 
				
			|||||||
        self.channel = Channel(self.outlet)
 | 
					        self.channel = Channel(self.outlet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def cleanup(self):
 | 
					    def cleanup(self):
 | 
				
			||||||
        self.channel.shutdown()
 | 
					        self.channel._shutdown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __exit__(self, __exc_type: typing.Type[BaseException], __exc_value: BaseException,
 | 
					    def __exit__(self, __exc_type: typing.Type[BaseException], __exc_value: BaseException,
 | 
				
			||||||
                 __traceback: types.TracebackType) -> bool:
 | 
					                 __traceback: types.TracebackType) -> bool:
 | 
				
			||||||
@ -282,7 +282,7 @@ class TestChannel(unittest.TestCase):
 | 
				
			|||||||
        self.h.channel.add_message_handler(handler2)
 | 
					        self.h.channel.add_message_handler(handler2)
 | 
				
			||||||
        envelope = RNS.Channel.Envelope(self.h.outlet, message, sequence=0)
 | 
					        envelope = RNS.Channel.Envelope(self.h.outlet, message, sequence=0)
 | 
				
			||||||
        raw = envelope.pack()
 | 
					        raw = envelope.pack()
 | 
				
			||||||
        self.h.channel.receive(raw)
 | 
					        self.h.channel._receive(raw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(1, handler1_called)
 | 
					        self.assertEqual(1, handler1_called)
 | 
				
			||||||
        self.assertEqual(0, handler2_called)
 | 
					        self.assertEqual(0, handler2_called)
 | 
				
			||||||
@ -290,7 +290,7 @@ class TestChannel(unittest.TestCase):
 | 
				
			|||||||
        handler1_return = False
 | 
					        handler1_return = False
 | 
				
			||||||
        envelope = RNS.Channel.Envelope(self.h.outlet, message, sequence=1)
 | 
					        envelope = RNS.Channel.Envelope(self.h.outlet, message, sequence=1)
 | 
				
			||||||
        raw = envelope.pack()
 | 
					        raw = envelope.pack()
 | 
				
			||||||
        self.h.channel.receive(raw)
 | 
					        self.h.channel._receive(raw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(2, handler1_called)
 | 
					        self.assertEqual(2, handler1_called)
 | 
				
			||||||
        self.assertEqual(1, handler2_called)
 | 
					        self.assertEqual(1, handler2_called)
 | 
				
			||||||
@ -348,7 +348,7 @@ class TestChannel(unittest.TestCase):
 | 
				
			|||||||
        self.assertFalse(envelope.tracked)
 | 
					        self.assertFalse(envelope.tracked)
 | 
				
			||||||
        self.assertEqual(0, len(decoded))
 | 
					        self.assertEqual(0, len(decoded))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.h.channel.receive(packet.raw)
 | 
					        self.h.channel._receive(packet.raw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(1, len(decoded))
 | 
					        self.assertEqual(1, len(decoded))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user