o
    ia                     @   s  d Z ddlZddlZddlZddlmZ ddlmZ dZdZ	dZ
dZdZd	Zd
ZdZdZdZdZdZdZdZdZdZdZdZe
eeeeeeeeeeeeeeeiZdd e D ZdZdZdZd	Z d
Z!dZ"dZ#dZ$dZ%dZ&dZ'dZ(dZ)e*e)Z+dZ,G dd  d Z-e-Z.	d7d!e/d"e/d#e0d$e1d%e1f
d&d'Z2d%e1fd(d)Z3d%e1fd*d+Z4d%e1fd,d-Z5d%e1fd.d/Z6	d8d0e7d1e/d%e1fd2d3Z8d9d0e7d4e7d%e1fd5d6Z9dS ):a6  
Dirty Worker Binary Protocol

Binary message framing over Unix sockets, inspired by OpenBSD msgctl/msgsnd.
Replaces JSON protocol for efficient binary data transfer.

Header Format (16 bytes):
+--------+--------+--------+--------+--------+--------+--------+--------+
|  Magic (2B)     | Ver(1) | MType  |        Payload Length (4B)        |
+--------+--------+--------+--------+--------+--------+--------+--------+
|                       Request ID (8 bytes)                            |
+--------+--------+--------+--------+--------+--------+--------+--------+

- Magic: 0x47 0x44 ("GD" for Gunicorn Dirty)
- Version: 0x01
- MType: Message type (REQUEST, RESPONSE, ERROR, CHUNK, END)
- Length: Payload size (big-endian uint32, max 64MB)
- Request ID: uint64 (replaces UUID string)

Payload is TLV-encoded (see tlv.py).
    N   )DirtyProtocolError)
TLVEncoders   GD                     requestresponseerrorchunkendstashstatusmanagec                 C   s   i | ]\}}||qS  r   ).0kvr   r   K/home/ubuntu/.local/lib/python3.10/site-packages/gunicorn/dirty/protocol.py
<dictcomp>H   s    r            	   
   z>2sBBIQi   c                   @   s  e Zd ZdZeZeZeZeZ	e
ZeZeZeZeZeZededededefddZededefd	d
Ze	d7dedededededefddZededefddZededefddZ ededefddZ!ededefddZ"ededefddZ#ed8dedededefdd Z$e	d9deded!edefd"d#Z%ededefd$d%Z&ed&e'j(defd'd(Z)ed)e'j*d*eddfd+d,Z+ed-e,j,d.edefd/d0Z-ed-e,j,defd1d2Z.ed-e,j,d*eddfd3d4Z/ed*edefd5d6Z0dS ):BinaryProtocolz-Binary message protocol for dirty worker IPC.msg_type
request_idpayload_lengthreturnc                 C   s   t ttt| ||S )a,  
        Encode the 16-byte message header.

        Args:
            msg_type: Message type (MSG_TYPE_REQUEST, etc.)
            request_id: Unique request identifier (uint64)
            payload_length: Length of the TLV-encoded payload

        Returns:
            bytes: 16-byte header
        )structpackHEADER_FORMATMAGICVERSION)r    r!   r"   r   r   r   encode_headerr   s   zBinaryProtocol.encode_headerdatac                 C   s   t | tk rtdt |  dt | dtt| dt \}}}}}|tkr7td|dt| dd d|tkrKtd| dt | dd d|tvr]td	|d
| dd d|t	krltd| dt	 d|||fS )z
        Decode the 16-byte message header.

        Args:
            data: 16 bytes of header data

        Returns:
            tuple: (msg_type, request_id, payload_length)

        Raises:
            DirtyProtocolError: If header is invalid
        zHeader too short:  bytes, expected raw_dataNzInvalid magic: z, expected    zUnsupported protocol version: zUnknown message type: 0x02xzMessage too large: z bytes (max: ))
lenHEADER_SIZEr   r$   unpackr&   r'   r(   MSG_TYPE_TO_STRMAX_MESSAGE_SIZE)r*   magicversionr    lengthr!   r   r   r   decode_header   s8   




zBinaryProtocol.decode_headerNapp_pathactionargskwargsc                 C   sB   |||rt |ng |pi d}t|}tt| t|}|| S )al  
        Encode a request message.

        Args:
            request_id: Unique request identifier (uint64)
            app_path: Import path of the dirty app
            action: Action to call on the app
            args: Positional arguments
            kwargs: Keyword arguments

        Returns:
            bytes: Complete message (header + payload)
        )r:   r;   r<   r=   )listr   encoder   r)   MSG_TYPE_REQUESTr1   )r!   r:   r;   r<   r=   payload_dictpayloadheaderr   r   r   encode_request   s   
zBinaryProtocol.encode_requestc                 C   ,   d|i}t |}tt| t|}|| S )a  
        Encode a success response message.

        Args:
            request_id: Request identifier this responds to
            result: Result value (must be TLV-serializable)

        Returns:
            bytes: Complete message (header + payload)
        result)r   r?   r   r)   MSG_TYPE_RESPONSEr1   )r!   rF   rA   rB   rC   r   r   r   encode_response      
zBinaryProtocol.encode_responsec                 C   sr   ddl m} t||r| }nt|tr|}nt|jt|i d}d|i}t	|}t
t| t|}|| S )a  
        Encode an error response message.

        Args:
            request_id: Request identifier this responds to
            error: DirtyError instance, dict, or Exception

        Returns:
            bytes: Complete message (header + payload)
        r   
DirtyError
error_typemessagedetailsr   )errorsrK   
isinstanceto_dictdicttype__name__strr   r?   r   r)   MSG_TYPE_ERRORr1   )r!   r   rK   
error_dictrA   rB   rC   r   r   r   encode_error   s   



zBinaryProtocol.encode_errorc                 C   rE   )a  
        Encode a chunk message for streaming responses.

        Args:
            request_id: Request identifier this chunk belongs to
            data: Chunk data (must be TLV-serializable)

        Returns:
            bytes: Complete message (header + payload)
        r*   )r   r?   r   r)   MSG_TYPE_CHUNKr1   )r!   r*   rA   rB   rC   r   r   r   encode_chunk   rI   zBinaryProtocol.encode_chunkc                 C      t t| d}|S )z
        Encode an end-of-stream message.

        Args:
            request_id: Request identifier this ends

        Returns:
            bytes: Complete message (header + empty payload)
        r   )r   r)   MSG_TYPE_ENDr!   rC   r   r   r   
encode_end     zBinaryProtocol.encode_endc                 C   r\   )z
        Encode a status query message.

        Args:
            request_id: Request identifier

        Returns:
            bytes: Complete message (header + empty payload)
        r   )r   r)   MSG_TYPE_STATUSr^   r   r   r   encode_status   r`   zBinaryProtocol.encode_statusr   opcountc                 C   s.   ||d}t |}tt| t|}|| S )a4  
        Encode a worker management message.

        Args:
            request_id: Request identifier
            op: Management operation (MANAGE_OP_ADD or MANAGE_OP_REMOVE)
            count: Number of workers to add/remove

        Returns:
            bytes: Complete message (header + payload)
        )rc   rd   )r   r?   r   r)   MSG_TYPE_MANAGEr1   )r!   rc   rd   rA   rB   rC   r   r   r   encode_manage/  s   
zBinaryProtocol.encode_managetablec           	      C   s^   ||d}|dur||d< |dur||d< |dur||d< t |}tt| t|}|| S )a  
        Encode a stash operation message.

        Args:
            request_id: Unique request identifier (uint64)
            op: Stash operation code (STASH_OP_*)
            table: Table name
            key: Optional key for put/get/delete operations
            value: Optional value for put operation
            pattern: Optional pattern for keys operation

        Returns:
            bytes: Complete message (header + payload)
        )rc   rg   Nkeyvaluepattern)r   r?   r   r)   MSG_TYPE_STASHr1   )	r!   rc   rg   rh   ri   rj   rA   rB   rC   r   r   r   encode_stashE  s   
zBinaryProtocol.encode_stashc              
   C   s   t | \}}}t| t| k r$tdt|  dt|  | dd d|dkr+i }n0| tt|  }zt|}W n  tyB     tyZ } ztd| |dd dd}~ww t| }|||fS )a  
        Decode a complete message (header + payload).

        Args:
            data: Complete message bytes

        Returns:
            tuple: (msg_type_str, request_id, payload_dict)
                   msg_type_str is the string name (e.g., "request")
                   payload_dict is the decoded TLV payload as a dict

        Raises:
            DirtyProtocolError: If message is malformed
        zIncomplete message: expected z bytes, got N2   r,   r   Failed to decode TLV payload: )	r   r9   r1   r2   r   r   decode_full	Exceptionr4   )r*   r    r!   r8   rA   payload_dataemsg_type_strr   r   r   decode_messagef  s0   


zBinaryProtocol.decode_messagereaderc           
   
      s>  z
|  tI dH }W n% tjy0 } zt|jdkr tdt|j dt |jdd}~ww t|\}}}|dkrz
|  |I dH }W n tjyd } ztdt|j d| |jdd}~ww zt	
|}W n" tyt     ty } ztd| |dd dd}~ww i }t| }||d	}	|	| |	S )
ap  
        Read a complete binary message from async stream.

        Args:
            reader: asyncio StreamReader

        Returns:
            dict: Message dict with 'type', 'id', and payload fields

        Raises:
            DirtyProtocolError: If read fails or message is malformed
            asyncio.IncompleteReadError: If connection closed mid-read
        Nr   zIncomplete header: got r+   r,   zIncomplete payload: got rn   rm   rT   id)readexactlyr2   asyncioIncompleteReadErrorr1   partialr   r   r9   r   ro   rp   r4   update)
ru   rC   rr   r    r!   r8   rq   rA   rs   rF   r   r   r   read_message_async  sV   



z!BinaryProtocol.read_message_asyncwriterrN   c                    s(   t |}| | |  I dH  dS )a[  
        Write a message to async stream.

        Accepts dict format for backwards compatibility.

        Args:
            writer: asyncio StreamWriter
            message: Message dict with 'type', 'id', and payload fields

        Raises:
            DirtyProtocolError: If encoding fails
            ConnectionError: If write fails
        N)r   _encode_from_dictwritedrain)r~   rN   r*   r   r   r   write_message_async  s   

z"BinaryProtocol.write_message_asyncsocknc                 C   sn   d}t ||k r5| |t | }|s+t |dkrtdtdt | d| |d||7 }t ||k s|S )a  
        Receive exactly n bytes from a socket.

        Args:
            sock: Socket to read from
            n: Number of bytes to read

        Returns:
            bytes: Received data

        Raises:
            DirtyProtocolError: If read fails or connection closed
            r   zConnection closedzConnection closed after r+   r,   )r1   recvr   )r   r   r*   r   r   r   r   _recv_exactly  s   
zBinaryProtocol._recv_exactlyc           
   
   C   s   t | t}t |\}}}|dkr@t | |}zt|}W n" ty'     ty? } ztd| |dd dd}~ww i }t| }||d}	|		| |	S )a   
        Read a complete message from socket (sync).

        Args:
            sock: Socket to read from

        Returns:
            dict: Message dict with 'type', 'id', and payload fields

        Raises:
            DirtyProtocolError: If read fails or message is malformed
        r   rn   Nrm   r,   rv   )
r   r   r2   r9   r   ro   r   rp   r4   r|   )
r   rC   r    r!   r8   rq   rA   rr   rs   rF   r   r   r   read_message  s(   


zBinaryProtocol.read_messagec                 C   s   t |}| | dS )a  
        Write a message to socket (sync).

        Args:
            sock: Socket to write to
            message: Message dict with 'type', 'id', and payload fields

        Raises:
            DirtyProtocolError: If encoding fails
            OSError: If write fails
        N)r   r   sendall)r   rN   r*   r   r   r   write_message/  s   
zBinaryProtocol.write_messagec              
   C   sd  |  d}|  dd}t|trt|d@ }t |}|du r&td| |tkrAt||  dd|  d	d|  d
|  dS |t	krNt
||  dS |tkr\t||  di S |tkrit||  dS |tkrrt|S |tkrt||  d|  dd|  d|  d|  dS |tkrt|S |tkrt||  d|  ddS td| )a  
        Encode a message dict to binary format.

        Supports the old dict-based API for backwards compatibility.

        Args:
            message: Message dict with 'type', 'id', and payload fields

        Returns:
            bytes: Complete encoded message
        rT   rw   r   l    NzUnknown message type: r:    r;   r<   r=   rF   r   r*   rc   rg   rh   ri   rj   rd   r   zUnhandled message type: )getrQ   rV   hashMSG_TYPE_FROM_STRr   r@   r   rD   rG   rH   rW   rY   rZ   r[   r]   r_   rk   rl   ra   rb   re   rf   )rN   rs   r!   r    r   r   r   r   ?  sd   









z BinaryProtocol._encode_from_dictNNr   NNN)1rU   
__module____qualname____doc__r2   r5   MSG_TYPE_REQUEST_STRr@   MSG_TYPE_RESPONSE_STRrG   MSG_TYPE_ERROR_STRrW   MSG_TYPE_CHUNK_STRrZ   MSG_TYPE_END_STRr]   MSG_TYPE_STASH_STRrk   MSG_TYPE_STATUS_STRra   MSG_TYPE_MANAGE_STRre   staticmethodintbytesr)   tupler9   rV   rS   rD   rH   rY   r[   r_   rb   rf   rl   rt   ry   StreamReaderr}   StreamWriterr   socketr   r   r   r   r   r   r   r   r   b   sx    0 0;'r   r:   r;   r<   r=   r#   c                 C   s$   t j| |||rt|ng |pi dS )aU  
    Build a request message dict.

    Args:
        request_id: Unique request identifier (int or str)
        app_path: Import path of the dirty app (e.g., 'myapp.ml:MLApp')
        action: Action to call on the app
        args: Positional arguments
        kwargs: Keyword arguments

    Returns:
        dict: Request message dict
    )rT   rw   r:   r;   r<   r=   )DirtyProtocolr@   r>   )r!   r:   r;   r<   r=   r   r   r   make_request  s   r   c                 C      t j| |dS )z
    Build a success response message dict.

    Args:
        request_id: Request identifier this responds to
        result: Result value

    Returns:
        dict: Response message dict
    )rT   rw   rF   )r   rG   )r!   rF   r   r   r   make_response     r   c                 C   sT   ddl m} t||r| }nt|tr|}nt|jt|i d}tj	| |dS )z
    Build an error response message dict.

    Args:
        request_id: Request identifier this responds to
        error: DirtyError instance or dict with error info

    Returns:
        dict: Error response message dict
    r   rJ   rL   )rT   rw   r   )
rP   rK   rQ   rR   rS   rT   rU   rV   r   rW   )r!   r   rK   rX   r   r   r   make_error_response  s   


r   c                 C   r   )z
    Build a chunk message dict for streaming responses.

    Args:
        request_id: Request identifier this chunk belongs to
        data: Chunk data

    Returns:
        dict: Chunk message dict
    )rT   rw   r*   )r   rZ   )r!   r*   r   r   r   make_chunk_message  r   r   c                 C   s   t j| dS )z
    Build an end-of-stream message dict.

    Args:
        request_id: Request identifier this ends

    Returns:
        dict: End message dict
    rv   )r   r]   )r!   r   r   r   make_end_message  s   r   rc   rg   c                 C   sD   t j| ||d}|dur||d< |dur||d< |dur ||d< |S )a  
    Build a stash operation message dict.

    Args:
        request_id: Unique request identifier (int or str)
        op: Stash operation code (STASH_OP_*)
        table: Table name
        key: Optional key for put/get/delete operations
        value: Optional value for put operation
        pattern: Optional pattern for keys operation

    Returns:
        dict: Stash message dict
    )rT   rw   rc   rg   Nrh   ri   rj   )r   rk   )r!   rc   rg   rh   ri   rj   msgr   r   r   make_stash_message  s   r   rd   c                 C   s   t j| ||dS )a  
    Build a worker management message dict.

    Args:
        request_id: Unique request identifier (int or str)
        op: Management operation (MANAGE_OP_ADD or MANAGE_OP_REMOVE)
        count: Number of workers to add/remove

    Returns:
        dict: Manage message dict
    )rT   rw   rc   rd   )r   re   )r!   rc   rd   r   r   r   make_manage_message  s
   r   r   r   r   ):r   ry   r   r$   rP   r   tlvr   r'   r(   r@   rG   rW   rZ   r]   rk   ra   re   r   r   r   r   r   r   r   r   r4   itemsr   STASH_OP_PUTSTASH_OP_GETSTASH_OP_DELETESTASH_OP_KEYSSTASH_OP_CLEARSTASH_OP_INFOSTASH_OP_ENSURESTASH_OP_DELETE_TABLESTASH_OP_TABLESSTASH_OP_EXISTSMANAGE_OP_ADDMANAGE_OP_REMOVEr&   calcsizer2   r5   r   r   rV   r   rS   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   
    -

