Esempio n. 1
0
    def check_body_length(cls, data, start=0, body_end=None):
        """
        Checks the BodyLength tag (9) for the encoded message data provided.

        :param data: An encoded FIX message.
        :param start: Optimization: the position at which to start the search.
        :param body_end: Optimization: the index at which the body terminates in data. If this value
        is not provided then the data byte string will be parsed to look for the Checksum (10) tag,
        which should denote the end of the message body.

        :return: A tuple consisting of the value of the BodyLength tag in encoded byte format, and the
        index at which the tag ends.

        :raises: ParsingError if the BodyLength tag can either not be found, or if the actual body
        length does not match the check value provided by the server.
        """
        try:
            if body_end is None:
                checksum_check, body_end, _ = utils.rindex_tag(10, data)

            body_length, _, tag_end = utils.index_tag(9, data, start=start)
        except TagNotFound as e:
            raise ParsingError(
                f"BodyLength (9) not found in {utils.decode(data)}.") from e

        body_length = int(body_length)
        actual_length = body_end - tag_end - 1
        if actual_length != body_length:
            raise ParsingError(
                f"Message has wrong body length. Expected: {body_length}, actual: {actual_length}."
            )

        return body_length, tag_end
Esempio n. 2
0
    def decode_message(cls, data: bytes) -> FIXMessage:
        """
        Constructs a GenericMessage from the provided data. Also uses the BeginString (8), BodyLength (9),
        and Checksum (10) tags to verify the message before decoding.

        :param data: The raw FIX message (probably received from a FIX server via a StreamReader) as a string of bytes.
        :return: a GenericMessage instance initialised from the raw data.
        """
        # Message MUST start with begin_string
        begin_string, begin_tag_end = cls.check_begin_string(data, start=0)

        # Optimization: Find checksum now so that we can re-use its
        # index in both the body_length and subsequent 'check_checksum' calls.
        checksum_check, checksum_tag_start, _ = utils.rindex_tag(10, data)

        # Body length must match what was sent by server
        body_length, body_length_tag_end = cls.check_body_length(
            data, start=begin_tag_end, body_end=checksum_tag_start
        )

        # MsgType must be the third field in the message
        msg_type, _, msg_type_end_tag = utils.index_tag(
            connection.protocol.Tag.MsgType, data, body_length_tag_end
        )

        # MsgSeqNum must be the fourth field in the message
        msg_seq_num, _, msg_seq_num_end_tag = utils.index_tag(
            connection.protocol.Tag.MsgSeqNum, data, msg_type_end_tag
        )

        checksum, _ = cls.check_checksum(
            data, body_start=0, body_end=checksum_tag_start
        )

        message = RawMessage(
            begin_string,
            body_length=body_length,
            message_type=msg_type,
            message_seq_num=msg_seq_num,
            encoded_body=data[msg_seq_num_end_tag + 1 : checksum_tag_start],
            checksum=checksum,
        )
        return message
Esempio n. 3
0
    def check_checksum(
        cls, data: bytes, body_start: int = 0, body_end: int = None
    ) -> Tuple[int, int]:
        """
        Checks the Checksum tag (10) for the encoded message data provided.

        :param data: An encoded FIX message.
        :param body_start: The index in the encoded message at which the message body starts.
        :param body_end: The index in the encoded message at which the message body ends.
        If this value is not provided, then it will default to the index at which the Checksum tag starts.

        :return: A tuple consisting of the value of the checksum and the index at which the tag ends.

        :raises: ParsingError if the BeginString tag can either not be found, or if it is not the first tag
        in the message.
        """
        try:
            checksum, checksum_start, checksum_end = utils.rindex_tag(10, data)
        except TagNotFound as e:
            raise ParsingError(
                f"Checksum (10) not found in {utils.decode(data)}."
            ) from e

        if len(data) != checksum_end + 1:
            raise ParsingError(
                f"Unexpected bytes following checksum: {data[checksum_start:]}."
            )

        if body_end is None:
            body_end = checksum_start

        checksum = int(checksum)
        calc_checksum = utils.calculate_checksum(data[body_start:body_end])
        if checksum != calc_checksum:
            raise ParsingError(
                f"Checksum check failed. Calculated value of {calc_checksum} does not match {checksum}."
            )

        return checksum, checksum_end
Esempio n. 4
0
def test_rfind(simple_encoded_msg):
    assert utils.rindex_tag(10, simple_encoded_msg) == (b"163", 19, 25)