Skip to content

hololinked.core.zmq.message.ResponseMessage

A single unit of message from a ZMQ server to client. The message may be parsed and deserialized into header and body.

Message indices:

Index 0 2 3 4
Desc address header data pre encoded data

The header is a JSON with the following (shortened) schema:

{
    "messageType": "string",
    "messageID": "string",
    "payloadContentType": "string",
    "preencodedPayloadContentType": "string"
}

For detailed schema, visit here.

Source code in hololinked\core\zmq\message.py
class ResponseMessage:
    """
    A single unit of message from a ZMQ server to client. 
    The message may be parsed and deserialized into header and body.

    Message indices:

    | Index | 0       |   2    | 3    |     4            |
    |-------|---------|--------|------|------------------|
    | Desc  | address | header | data | pre encoded data |


    The header is a JSON with the following (shortened) schema:

    ```json
    {
        "messageType": "string",
        "messageID": "string",
        "payloadContentType": "string",
        "preencodedPayloadContentType": "string"
    }
    ```

    For detailed schema, visit [here](https://hololinked.readthedocs.io/en/latest/protocols/zmq/response-message-header.json).
    """

    length = Integer(default=4, readonly=True, 
                    doc="length of the message") # type: int

    def __init__(self, msg: typing.List[bytes]):
        self._bytes = msg
        self._header = None
        self._body = None
        self._sender_id = None

    @property
    def byte_array(self) -> typing.List[bytes]:
        """returns the message in bytes"""
        return self._bytes

    @property
    def id(self) -> str:
        """ID of the message"""
        return self.header['messageID']

    @property
    def type(self) -> str:
        """type of the message"""
        return self.header['messageType']

    @property
    def receiver_id(self) -> str:
        """ID of the sender"""
        return self.header['receiverID']

    @property
    def sender_id(self) -> str:
        """ID of the receiver"""
        return self.header['senderID']

    @property
    def header(self) -> JSON:
        """Returns the header of the message"""
        if self._header is None:
            self.parse_header()
        return self._header

    @property
    def body(self) -> typing.Tuple[bytes, bytes, bytes, bytes, bytes]:
        """Returns the body of the message"""
        if self._body is None:
            self.parse_body()
        return self._body

    @property
    def payload(self) -> SerializableData:
        """Returns the payload of the message"""
        return self.body[0]

    @property
    def preserialized_payload(self) -> PreserializedData:
        """Returns the pre-encoded payload of the message"""
        return self.body[1]

    def parse_header(self) -> None:
        """parse the header"""
        if isinstance(self._bytes[INDEX_HEADER], ResponseHeader):
            self._header = self._bytes[INDEX_HEADER]
        elif isinstance(self._bytes[INDEX_HEADER], byte_types):
            self._header = ResponseHeader(**Serializers.json.loads(self._bytes[INDEX_HEADER]))
        else:
            raise ValueError(f"header must be of type ResponseHeader or bytes, not {type(self._bytes[INDEX_HEADER])}")

    def parse_body(self) -> None:
        """parse the body"""
        self._body = [
            SerializableData(self._bytes[INDEX_BODY], content_type=self.header['payloadContentType']),
            PreserializedData(self._bytes[INDEX_PRESERIALIZED_BODY], content_type=self.header['preencodedPayloadContentType'])
        ]

    @classmethod
    def craft_from_arguments(cls, 
                            receiver_id: str,
                            sender_id: str,  
                            message_type: str, 
                            message_id: bytes = b'', 
                            payload: SerializableData = SerializableNone, 
                            preserialized_payload: PreserializedData = PreserializedEmptyByte
                        ) -> "ResponseMessage":
        """
        Crafts an arbitrary response to the client using the method's arguments. 

        Parameters
        ----------
        address: bytes 
            the ROUTER address of the client
        message_type: bytes 
            type of the message, possible values are 'REPLY', 'HANDSHAKE' and 'TIMEOUT' 
        message_id: bytes
            message id of the original client message for which the response is being crafted
        data: Any
            serializable data
        preserialized_payload: bytes
            pre-encoded data, generally used for large or custom data that is already serialized

        Returns
        -------
        message: List[bytes]
            the crafted response with information in the correct positions within the list
        """
        message = ResponseMessage([])
        message._header = ResponseHeader(
            messageType=message_type,
            messageID=message_id,
            receiverID=receiver_id,
            senderID=sender_id,
            payloadContentType=payload.content_type,
            preencodedPayloadContentType=preserialized_payload.content_type
        )
        message._body = [payload, preserialized_payload]
        message._bytes = [
            bytes(receiver_id, encoding='utf-8'),
            Serializers.json.dumps(message._header.json()),
            payload.serialize(),
            preserialized_payload.value
        ]
        return message


    @classmethod
    def craft_reply_from_request(cls, 
                        request_message: RequestMessage, 
                        payload: SerializableData = SerializableNone,
                        preserialized_payload: PreserializedData = PreserializedEmptyByte
                    ) -> "ResponseMessage":
        """
        Craft a response with certain data extracted from an originating client message, 
        like the client's address, message id etc. 

        Parameters
        ----------
        original_client_message: List[bytes]
            The message originated by the clieht for which the response is being crafted
        data: Any
            serializable data 
        preserialized_payload: bytes
            pre-encoded data, generally used for large or custom data that is already serialized

        Returns
        -------
        message: List[bytes]
            the crafted response with information in the correct positions within the list
        """
        message = ResponseMessage([])
        message._header = ResponseHeader(
            messageType=REPLY,
            messageID=request_message.id,
            receiverID=request_message.sender_id,
            senderID=request_message.receiver_id,
            payloadContentType=payload.content_type,
            preencodedPayloadContentType=preserialized_payload.content_type
        )
        message._body = [payload, preserialized_payload]
        message._bytes = [
            bytes(request_message.sender_id, encoding='utf-8'),    
            Serializers.json.dumps(message._header.json()),
            payload.serialize(),
            preserialized_payload.value
        ]
        return message


    @classmethod
    def craft_with_message_type(cls,
                        request_message: RequestMessage,
                        message_type: str,
                        payload: SerializableData = SerializableNone,
                        preserialized_payload: PreserializedData = PreserializedEmptyByte
                    ) -> "ResponseMessage": 
        """
        create a plain message with a certain type, for example a handshake message.

        Parameters
        ----------
        receiver_id: str
            id of the server
        message_type: bytes
            message type to be sent
        """
        message = ResponseMessage([])
        message._header = ResponseHeader(
            messageType=message_type,
            messageID=request_message.id,
            receiverID=request_message.sender_id,
            senderID=request_message.receiver_id,
            payloadContentType=payload.content_type,
            preencodedPayloadContentType=preserialized_payload.content_type
        )
        message._body = [payload, preserialized_payload]
        message._bytes = [
            bytes(request_message.sender_id, encoding='utf-8'),
            Serializers.json.dumps(message._header.json()),
            payload.serialize(),
            preserialized_payload.value
        ]
        return message

Functions

__init__

__init__(msg: typing.List[bytes])
Source code in hololinked\core\zmq\message.py
def __init__(self, msg: typing.List[bytes]):
    self._bytes = msg
    self._header = None
    self._body = None
    self._sender_id = None

header

header() -> JSON

Returns the header of the message

Source code in hololinked\core\zmq\message.py
@property
def header(self) -> JSON:
    """Returns the header of the message"""
    if self._header is None:
        self.parse_header()
    return self._header

body

body() -> typing.Tuple[bytes, bytes, bytes, bytes, bytes]

Returns the body of the message

Source code in hololinked\core\zmq\message.py
@property
def body(self) -> typing.Tuple[bytes, bytes, bytes, bytes, bytes]:
    """Returns the body of the message"""
    if self._body is None:
        self.parse_body()
    return self._body

craft_from_arguments classmethod

craft_from_arguments(receiver_id: str, sender_id: str, message_type: str, message_id: bytes = b'', payload: SerializableData = SerializableNone, preserialized_payload: PreserializedData = PreserializedEmptyByte) -> ResponseMessage

Crafts an arbitrary response to the client using the method's arguments.

Parameters:

Name Type Description Default

address

the ROUTER address of the client

required

message_type

str

type of the message, possible values are 'REPLY', 'HANDSHAKE' and 'TIMEOUT'

required

message_id

bytes

message id of the original client message for which the response is being crafted

b''

data

serializable data

required

preserialized_payload

PreserializedData

pre-encoded data, generally used for large or custom data that is already serialized

PreserializedEmptyByte

Returns:

Name Type Description
message List[bytes]

the crafted response with information in the correct positions within the list

Source code in hololinked\core\zmq\message.py
@classmethod
def craft_from_arguments(cls, 
                        receiver_id: str,
                        sender_id: str,  
                        message_type: str, 
                        message_id: bytes = b'', 
                        payload: SerializableData = SerializableNone, 
                        preserialized_payload: PreserializedData = PreserializedEmptyByte
                    ) -> "ResponseMessage":
    """
    Crafts an arbitrary response to the client using the method's arguments. 

    Parameters
    ----------
    address: bytes 
        the ROUTER address of the client
    message_type: bytes 
        type of the message, possible values are 'REPLY', 'HANDSHAKE' and 'TIMEOUT' 
    message_id: bytes
        message id of the original client message for which the response is being crafted
    data: Any
        serializable data
    preserialized_payload: bytes
        pre-encoded data, generally used for large or custom data that is already serialized

    Returns
    -------
    message: List[bytes]
        the crafted response with information in the correct positions within the list
    """
    message = ResponseMessage([])
    message._header = ResponseHeader(
        messageType=message_type,
        messageID=message_id,
        receiverID=receiver_id,
        senderID=sender_id,
        payloadContentType=payload.content_type,
        preencodedPayloadContentType=preserialized_payload.content_type
    )
    message._body = [payload, preserialized_payload]
    message._bytes = [
        bytes(receiver_id, encoding='utf-8'),
        Serializers.json.dumps(message._header.json()),
        payload.serialize(),
        preserialized_payload.value
    ]
    return message

craft_reply_from_request classmethod

craft_reply_from_request(request_message: RequestMessage, payload: SerializableData = SerializableNone, preserialized_payload: PreserializedData = PreserializedEmptyByte) -> ResponseMessage

Craft a response with certain data extracted from an originating client message, like the client's address, message id etc.

Parameters:

Name Type Description Default

original_client_message

The message originated by the clieht for which the response is being crafted

required

data

serializable data

required

preserialized_payload

PreserializedData

pre-encoded data, generally used for large or custom data that is already serialized

PreserializedEmptyByte

Returns:

Name Type Description
message List[bytes]

the crafted response with information in the correct positions within the list

Source code in hololinked\core\zmq\message.py
@classmethod
def craft_reply_from_request(cls, 
                    request_message: RequestMessage, 
                    payload: SerializableData = SerializableNone,
                    preserialized_payload: PreserializedData = PreserializedEmptyByte
                ) -> "ResponseMessage":
    """
    Craft a response with certain data extracted from an originating client message, 
    like the client's address, message id etc. 

    Parameters
    ----------
    original_client_message: List[bytes]
        The message originated by the clieht for which the response is being crafted
    data: Any
        serializable data 
    preserialized_payload: bytes
        pre-encoded data, generally used for large or custom data that is already serialized

    Returns
    -------
    message: List[bytes]
        the crafted response with information in the correct positions within the list
    """
    message = ResponseMessage([])
    message._header = ResponseHeader(
        messageType=REPLY,
        messageID=request_message.id,
        receiverID=request_message.sender_id,
        senderID=request_message.receiver_id,
        payloadContentType=payload.content_type,
        preencodedPayloadContentType=preserialized_payload.content_type
    )
    message._body = [payload, preserialized_payload]
    message._bytes = [
        bytes(request_message.sender_id, encoding='utf-8'),    
        Serializers.json.dumps(message._header.json()),
        payload.serialize(),
        preserialized_payload.value
    ]
    return message