Exemplo n.º 1
0
    def _compute_crc(self) -> bytes:
        """Computes CRC for the payload.

        Raises:
            exceptions.ProtocolDataError: Could not compute crc: TypeError.

            exceptions.ProtocolDataError: Could not pack header
                fields: crc=0xXX.

        """
        try:
            # raises TypeError for invalid input type.
            crc_key = int(CRC_FUNC(self.payload))
        except TypeError as terr:
            raise exceptions.ProtocolDataError(
                'Could not compute crc: {0}'
                .format(terr))
        parsed_key = None
        try:
            parsed_key = self._HEADER_PARSER.pack(crc_key)
        except struct.error as exc:
            raise exceptions.ProtocolDataError(
                'Could not pack header fields: crc=0x{:x}'
                .format(crc_key)
            ) from exc

        return parsed_key
Exemplo n.º 2
0
    def output(self) -> Optional[bytes]:
        """Produce the next COBS-decoded payload.

        Returns:
            An optional bytestring of the COBS-decoded payload from the next
            available COBS-encoded body in the internal buffer. Returns None if
            there are no bodies available in the internal buffer.

        Raises:
            exceptions.ProtocolDataError: the body is obviously incorrect for
                COBS decoding; note that it is possible that a body may have
                have accumulated byte errors which would silently cause an
                incorrect COBS decoding but would not be detected by the decoder
                and thus would not raise an exception.

        """
        frame = self._buffer.output()
        if frame is None:
            return None

        try:
            decoded: bytes = cobs.decode(frame)
        except cobs.DecodeError as exc:
            raise exceptions.ProtocolDataError(
                'Could not COBS-decode body: {!r}'.format(frame)
            ) from exc
        except TypeError as err:
            raise exceptions.ProtocolDataError(
                'Could not COBS-encode body: required type bytes found type {}'.
                format(type(frame))
            ) from err
        self._logger.debug(decoded)
        return decoded
Exemplo n.º 3
0
    def output(self) -> Optional[bytes]:
        """Produce the next COBS-encoded frame body.

        Returns:
            An optional bytestring of the COBS-encoded body from the next
            available payload in the internal buffer. Returns None if there are
            no payloads available in the internal buffer.

        Raises:
            exceptions.ProtocolDataError: the payload is too long for COBS
                encoding.

        """
        payload = self._buffer.output()
        if payload is None:
            return None

        if len(payload) > FRAME_PAYLOAD_MAX_SIZE:
            raise exceptions.ProtocolDataError(
                'Frame payload for COBS encoding is too long ({} bytes)!'
                .format(len(payload))
            )

        try:
            encoded: bytes = cobs.encode(payload)
        except TypeError as err:
            raise exceptions.ProtocolDataError(
                'Frames payload for COBS encoding should be bytes not {}'
                .format(type(payload))
            ) from err
        self._logger.debug(encoded)
        return encoded
Exemplo n.º 4
0
    def output(self) -> Optional[UpperEvent]:
        """Emit the next output event."""
        event = self._buffer.output()
        if not event:
            return None

        # Add data integrity to the state
        crc_message = None
        self._crc_receiver.input(event.data)
        try:
            crc_message = self._crc_receiver.output()
        except exceptions.ProtocolDataError as err:
            raise exceptions.ProtocolDataError('CRCSender({}):{}'.format(
                event.state_type, str(err)))

        if not crc_message:
            return None
        message = None
        self._message_receiver.input(crc_message)
        try:
            message = self._message_receiver.output()
        except exceptions.ProtocolDataError:
            self._logger.exception('MessageSender:')

        # check whether file name and data type are same
        if event.state_type != type(message).__name__:
            raise exceptions.ProtocolDataError(
                f"The state type {type(message).__name__} data in the "
                f"file does not match the filename {event.state_type}.")
        return message
Exemplo n.º 5
0
    def output(self) -> Optional[bytes]:
        """Produce the next datagram body.

        Returns:
            An optional bytestring of the datagram body from the next available
            datagram payload in the internal buffer. Returns None if there are
            no payloads available in the internal buffer.

        Raises:
            exceptions.ProtocolDataError: The payload is too long to fit in a
                datagram; specifically, it is longer than can be represented by
                the length field of the datagram header.

        """
        payload = self._buffer.output()
        if payload is None:
            return None

        if len(payload) > 255:
            raise exceptions.ProtocolDataError(
                'Datagram payload may not be {} bytes long!'
                .format(len(payload))
            )

        datagram = Datagram(seq=self._seq, payload=payload)
        datagram.update_from_payload()
        self._logger.debug(datagram)
        self._seq = (self._seq + 1) % SEQ_NUM_SPACE
        return datagram.compute_body()
Exemplo n.º 6
0
    def output(self) -> Optional[ListSegment[_ListElement]]:
        """Emit the next output event."""
        event = self._buffer.output()
        if event is None:
            return None

        if event.next_expected is None:
            return None

        if event.next_expected < self._next_expected:
            raise exceptions.ProtocolDataError(
                'Next expected event id cannot decrease from {} to {}!'.format(
                    self._next_expected, event.next_expected))

        self._next_expected = event.next_expected
        self._advance_next_expected_index()

        output_event = self.segment_type()
        output_event.next_expected = self._next_expected
        output_event.total = len(self._elements)
        output_event.elements = list(
            itertools.islice(self._elements, self._next_expected_index,
                             self._next_expected_index + self.max_segment_len))
        output_event.remaining = len(
            self._elements) - self._next_expected_index
        self._logger.debug('Sending: %s', output_event)
        return output_event
Exemplo n.º 7
0
    def input(self, event: Optional[LowerEvent]) -> None:
        """Handle input events."""
        if not event:
            return

        if not event.has_data():
            raise exceptions.ProtocolDataError("Empty file: {0}".format(
                event.state_type))

        self._buffer.input(event)
Exemplo n.º 8
0
 def update_from_payload(
         self, message_types: Mapping[Type[betterproto.Message],
                                      int]) -> None:
     """Update header fields from the payload."""
     message_type = type(self.payload)
     try:
         self.type = message_types[message_type]
     except KeyError as exc:
         raise exceptions.ProtocolDataError(
             'Message type does not have a code: {}'.format(
                 message_type)) from exc
Exemplo n.º 9
0
    def parse(
            self, buffer: bytes,
            message_classes: Mapping[int, Type[betterproto.Message]]) -> None:
        """Parse message contents from a buffer.

        Args:
            buffer: The message body bytestring from which header values and the
                payload are to be parsed and stored in the message's own
                attributes.
            message_classes: The mapping to look up Protocol Buffer message
                classes from the message type code.

        Raises:
            exceptions.ProtocolDataError: The header cannot be parsed.

        """
        body_header = buffer[:self.HEADER_SIZE]
        try:
            results = self._HEADER_PARSER.unpack(body_header)
        except struct.error as exc:
            raise exceptions.ProtocolDataError(
                'Unparseable header: {!r}'.format(body_header)) from exc

        (self.type, ) = results
        try:
            message_object = message_classes[self.type]()
        except KeyError as exc:
            raise exceptions.ProtocolDataError(
                'Unknown message type code: {}'.format(self.type)) from exc

        body_payload = buffer[self.HEADER_SIZE:]
        try:
            self.payload = message_object.parse(body_payload)
        except Exception as exc:
            # Wrap and re-raise any betterproto error as a ProtocolDataError
            raise exceptions.ProtocolDataError(
                'Unparseable payload: {!r}'.format(body_payload)) from exc
Exemplo n.º 10
0
    def check_integrity(self) -> None:
        """Matches computed CRC with that extracted from message.

            Raises:
                exceptions.ProtocolDataError: The specified CRC of the
                datagram\'s protected section, 0xXX, is inconsistent with the
                actual computed CRC of the received protected section, 0xXX.

        """
        computed_crc = self._compute_crc()
        if self.crc != computed_crc:
            raise exceptions.ProtocolDataError(
                'The specified CRC of the datagram\'s protected section, '
                '{!r}, is inconsistent with the actual computed CRC '
                'of the received protected section, {!r}'
                .format(self.crc, computed_crc)
            )
Exemplo n.º 11
0
    def parse(self, body: bytes) -> None:
        """Extracts CRC and payload from incoming message.
        Args:
            body:data for which crc has to be computed

        Raises:
            exceptions.ProtocolDataError: The size of the packet received
            should be between 4 bytes and 256 bytes but found to be n bytes.

        """
        if len(body) > 256 or len(body) < 4:
            raise exceptions.ProtocolDataError(
                'The size of the packet received should be between 4 bytes '
                'and 256 bytes but found to be {0} bytes.'.format(len(body))
            )
        self.crc = body[:self.HEADER_SIZE]
        self.payload = body[self.HEADER_SIZE:]
Exemplo n.º 12
0
    def compute_body(self) -> bytes:
        """Return the body of the datagram, including the header and payload.

        Raises:
            exceptions.ProtocolDataError: the header fields of the datagram
                could not be packed together into the header section of the
                datagram body.

        """
        try:
            header = self._HEADER_PARSER.pack(self.seq, self.length)
        except struct.error as exc:
            raise exceptions.ProtocolDataError(
                'Could not pack header fields: seq={}, len={}'
                .format(self.seq, self.length)
            ) from exc

        return header + self.payload
Exemplo n.º 13
0
    def pack_protected(self) -> bytes:
        """Return the protected section of the datagram body.

        Raises:
            exceptions.ProtocolDataError: the protected header fields of the
                datagram could not be packed together.

        """
        try:
            header_protected = self._HEADER_PARSER.pack(
                self.seq, self.length
            )
        except struct.error as exc:
            raise exceptions.ProtocolDataError(
                'Could not pack protected header fields: seq={}, len={}'
                .format(self.seq, self.length)
            ) from exc

        return header_protected + self.payload
Exemplo n.º 14
0
    def parse(self, buffer: bytes) -> None:
        """Parse datagram contents from a buffer.

        Args:
            buffer: The datagram body bytestring from which header field values
                and the payload are to be parsed and stored in the datagram's
                own attributes.

        Raises:
            exceptions.ProtocolDataError: The header cannot be parsed.

        """
        try:
            results = self._HEADER_PARSER.unpack(buffer[:self.HEADER_SIZE])
        except struct.error as exc:
            raise exceptions.ProtocolDataError(
                'Unparseable header: {!r}'.format(buffer[:self.HEADER_SIZE])
            ) from exc

        (self.seq, self.length) = results
        self.payload = buffer[self.HEADER_SIZE:]
Exemplo n.º 15
0
    def input(self, event: Optional[UpdateEvent]) -> None:
        """Handle input events."""
        if event is None or not event.has_data():
            return

        if event.time is not None:
            self._logger.debug('Time: %f', event.time)
            self.current_time = event.time
            if self.output_deadline is None:
                self.output_deadline = (self.current_time +
                                        self.output_schedule[0].time)
        if event.pb_message is None:
            return

        self._logger.debug('Received: %s', event.pb_message)
        message_type = type(event.pb_message)
        try:
            self.all_states[message_type] = event.pb_message
        except KeyError as exc:
            raise exceptions.ProtocolDataError(
                'Received message type is not a synchronizable state: {}'.
                format(message_type)) from exc
Exemplo n.º 16
0
    def output(self) -> Optional[bytes]:
        """Produce the next datagram payload.

        Returns:
            An optional bytestring of the datagram payload from the next
            available datagram body in the internal buffer. Returns None if
            there are no bodies available in the internal buffer.

        Raises:
            exceptions.ProtocolDataError: the header fields of the datagram body
                are inconsistent with the payload, or the header cannot be
                parsed from the datagram body.

        """
        body = self._buffer.output()
        if body is None:
            return None

        datagram = Datagram()
        datagram.parse(body)  # may raise ProtocolDataError
        self._logger.debug(datagram)

        if datagram.length != len(datagram.payload):
            raise exceptions.ProtocolDataError(
                'The specified length of the datagram payload, {}, is '
                'inconsistent with the actual received length, {}'
                .format(datagram.length, len(datagram.payload))
            )
        if self.expected_seq is None:
            self._logger.info('Initialized expected seq num from: %s', datagram)
            self.expected_seq = datagram.seq
        elif self.expected_seq != datagram.seq:
            self._logger.warning(
                'Expected datagram with seq num %d, but received: %s',
                self.expected_seq, datagram
            )
            self.expected_seq = datagram.seq
        self.expected_seq = (self.expected_seq + 1) % SEQ_NUM_SPACE
        return datagram.payload
Exemplo n.º 17
0
    def output(self) -> Optional[betterproto.Message]:
        """Emit the next output event."""
        if self.output_deadline is None:
            return None

        if self.current_time < self.output_deadline:
            return None

        output_type = self.output_schedule[0].type
        try:
            output_event = self.all_states[output_type]
        except KeyError as exc:
            raise exceptions.ProtocolDataError(
                'Scheduled message type is not a synchronizable state: {}'.
                format(output_type)) from exc

        self.output_schedule.rotate(-1)
        self.output_deadline = (self.current_time +
                                self.output_schedule[0].time)
        if output_event is None:
            return None

        self._logger.debug('Sending: %s', output_event)
        return output_event