o
    NK&h                     @   s  d dl Z d dlZd dlZd dlmZmZmZmZ d dlm	Z	m
Z
 d dlmZmZmZ d dlmZmZ zd dlmZmZ d dlmZ W n eyY   d dlmZmZ d dlmZ Y nw d d	lmZ d d
lmZ d dlmZ ddl m!Z!m"Z" ddl#m$Z$ ej%Z%ej&Z&ej'Z'G dd dZ(dS )    N)AsyncIteratorIterableMappingSequence)OptionalUnion)ConnectionClosedConnectionClosedErrorConnectionClosedOK)FrameOpcode)EventState)ServerProtocol)ServerConnection)Data)websockets_logger)SanicProtocol   )ServerErrorWebsocketClosed   )WebsocketFrameAssemblerc                   @   s  e Zd ZU eed< ee ed< eej ed< e	ed< e
ed< ee
 ed< ee
 ed< eed< eeejf ed	< ejed
< ejed< eej ed< ejed< eed< eej ed< eej ed< eej ed< eej ed< eej ed< 					d\dee
 dee
 de
fddZedd Zdd Zdd Z	d]dedeej fdd Zd]d!efd"d#Zd$ee d!dfd%d&Zd'ed!dfd(d)Zd^d*d+Zd!efd,d-Zd_d0e	d1e d!efd2d3Z!d`d5d6Z"d^d7d8Z#d^d9d:Z$d`d0e	d1e d!dfd;d<Z%d]d=ee
 d!ee& fd>d?Z'dad!ee& fdAdBZ(d!e)e& fdCdDZ*dEe+e&e,e& f d!dfdFdGZ-d]dHee& d!ejfdIdJZ.dbdHe&d!dfdLdMZ/dNdO Z0dPdQ Z1dRdS Z2dTdU Z3d!ee fdVdWZ4dXdY Z5dZd[ Z6dS )cWebsocketImplProtocolws_protoio_protoloop	max_queueclose_timeoutping_intervalping_timeout	assemblerpings
conn_mutex	recv_lockrecv_cancelprocess_event_mutex	can_pausedata_finished_futpause_frame_futconnection_lost_waiterkeepalive_ping_taskauto_closer_taskN   
   c                 C   s   || _ d | _d | _|| _|| _|| _|| _t| | _i | _	t
 | _t
 | _d | _t
 | _d | _d| _d | _d | _d | _d | _d S )NT)r   r   r   r   r   r   r    r   r!   r"   asyncioLockr#   r$   r%   r&   r(   r'   r)   r+   r,   r*   )selfr   r   r   r    r   r    r2   O/var/www/html/venv/lib/python3.10/site-packages/sanic/server/websockets/impl.py__init__A   s&   	




zWebsocketImplProtocol.__init__c                 C   s   | j jS N)r   subprotocolr1   r2   r2   r3   r6   ^   s   z!WebsocketImplProtocol.subprotocolc                 C   s\   | j sdS | jrtd dS | jr| jsdS | jjr!| jj  | j | _td dS )NFz$Websocket connection already paused.zWebsocket connection paused.T)	r'   r)   r   debugr   r   	transportpause_readingcreate_futurer7   r2   r2   r3   pause_framesb   s   

z"WebsocketImplProtocol.pause_framesc                 C   sb   | j s
td dS | jr| jstd dS | jjr!| jj  | j d  d | _ td dS )Nz Websocket connection not paused.FzFWebsocket attempting to resume reading frames, but connection is gone.zWebsocket connection unpaused.T)r)   r   r8   r   r   r9   resume_reading
set_resultr7   r2   r2   r3   resume_framesp   s   

z#WebsocketImplProtocol.resume_framesc                    s   |szt |d}W n ty   t }Y nw |std| jr%td|| _|| _| j | _	t
| j | _| jrEt|  | _t|  | _d S )Nr   z)Connection received with no asyncio loop.zECannot call connection_made more than once on a websocket connection.)getattrAttributeErrorr/   get_event_loopr   r,   r   r   r;   r*   shieldr(   r   create_taskkeepalive_pingr+   auto_close_connection)r1   r   r   r2   r2   r3   connection_made   s0   
z%WebsocketImplProtocol.connection_madereturnc                    sZ   | j sdS | j  rdS ztt| j |I dH  W dS  tjy,   | j   Y S w )a  
        Wait until the TCP connection is closed or ``timeout`` elapses.
        If timeout is None, wait forever.
        Recommend you should pass in self.close_timeout as timeout

        Return ``True`` if the connection is closed and ``False`` otherwise.

        FTN)r*   doner/   wait_forrC   TimeoutError)r1   timeoutr2   r2   r3   wait_for_connection_lost   s   	

z.WebsocketImplProtocol.wait_for_connection_losteventsc              	      s   | j 4 I dH > |D ]/}t|tsq|jtjkr"| |I dH  q|jtjkr1| jr0| j	  q| j
|I dH  qW d  I dH  dS 1 I dH sLw   Y  dS )z4
        Process a list of incoming events.
        N)r&   
isinstancer   opcoder   PONGprocess_pongCLOSEr%   cancelr!   put)r1   rN   eventr2   r2   r3   process_events   s   

.z$WebsocketImplProtocol.process_eventsframec                    sr   |j | jv r5g }| j D ]\}}|| | s |d  ||j kr' nqtd|D ]}| j|= q.d S d S )Nzping_id is not in self.pings)datar"   itemsappendrI   r>   r   )r1   rX   ping_idsping_idpingr2   r2   r3   rR      s   



z"WebsocketImplProtocol.process_pongc              
      s  | j du rdS z:	 t| j I dH  |  I dH }| jdurBzt|| jI dH  W n tjyA   td | 	d Y W dS w q
 tj
yR   td Y dS  ttfyb   td Y dS  ty } ztd tt| W Y d}~dS d}~ww )a@  
        Send a Ping frame and wait for a Pong frame at regular intervals.
        This coroutine exits when the connection terminates and one of the
        following happens:
        - :meth:`ping` raises :exc:`ConnectionClosed`, or
        - :meth:`auto_close_connection` cancels :attr:`keepalive_ping_task`.
        NTz$Websocket timed out waiting for pongi  z,Websocket keepalive ping task was cancelled.z.Websocket closed. Keepalive ping task exiting.z6Unexpected exception in websocket keepalive ping task.)r   r/   sleepr^   r    rJ   rK   r   warningfail_connectionCancelledErrorr8   r   r   	Exceptionstr)r1   ping_waiterer2   r2   r3   rE      sB   




z$WebsocketImplProtocol.keepalive_pingc                 C   s   | j r| j  s| j   | jr| j s| j  d| _| jr-| j s-| j  d| _| jrH| jrH| jjrH| jj  | j	| j
| jjj dS )z
        Internal method used by end_connection and fail_connection
        only when the graceful auto-closer cannot be used
        NT)r,   rI   rT   r(   r+   r   r   r9   close
call_laterr   abortr7   r2   r2   r3   _force_disconnect  s   


z'WebsocketImplProtocol._force_disconnect   codereasonc                 C   s  | j r^| j jr^| j j  | j }| jjtu r^|dv r$| j|| n| j|| z)| j }t	|rS| j rS| j jrS|
d}| j j| t	|rS| j rS| j js<W n	 ty]   Y nw |dkrft| j_| jrv| j sv| j  d| _| jr~| j r|  S dS )aP  
        Fail the WebSocket Connection
        This requires:
        1. Stopping all processing of incoming data, which means cancelling
           pausing the underlying io protocol. The close code will be 1006
           unless a close frame was received earlier.
        2. Sending a close frame with an appropriate code if the opening
           handshake succeeded and the other side is likely to process it.
        3. Closing the connection. :meth:`auto_close_connection` takes care
           of this.
        (The specification describes these steps in the opposite order.)
        )  i  r   rk   NF)r   r9   r:   r   data_to_sendstateOPEN
send_closefaillenpopwriterc   CLOSEDr(   rI   rT   r,   rj   )r1   rm   rn   _rp   
frame_datar2   r2   r3   ra   "  sF   



z%WebsocketImplProtocol.fail_connectionro   c                 C   s   |dks| j r| j js| ||S | j j  | jjtkr_| j }| j|| |	| j  z$t
|rT| j rT| j jrT|d}| j j| t
|rT| j rT| j js=W n	 ty^   Y nw | jro| j so| j  d | _| jrw| j r{|  S dS )Nrk   r   F)r   r9   ra   r:   r   rq   rr   rp   rs   extendru   rv   rw   rc   r(   rI   rT   r,   rj   )r1   rm   rn   rp   rz   r2   r2   r3   end_connectionY  s@   


z$WebsocketImplProtocol.end_connectionc                    s  zjz| j r$z| j I dH  td W n tjy#   td Y nw | jr/| j  d| _| jr| jjr| jj	 rtd | jj
  | jr| jddI dH rW W | jr`| j  d| _| jrg| jjsidS | jrr| j rrn| jj ryn| jj  | jszt| jI dH  W n
 tjy   Y nw | jr| jjr| jj  dS dS dS | j| jdI dH rdS td | jr| jjr| jj  dS dS dS W n tjy   Y n ty   td Y nw W | jr| j  d| _| jr| jjsdS | jr| j rn| jj rn| jj  | jsFzt| jI dH  W n tjy0   Y nw | jrB| jjrD| jj  dS dS dS | j| jdI dH rSdS td | jri| jjrk| jj  dS dS dS | jry| j  d| _| jr| jjs   Y dS | jr| j rn| jj rn| jj  | jszt| jI dH  W n tjy   Y nw | jr| jjr| jj  w w w | j| jdI dH r   Y dS td | jr| jjr| jj  w w w )	a  
        Close the WebSocket Connection
        When the opening handshake succeeds, :meth:`connection_open` starts
        this coroutine in a task. It waits for the data transfer phase to
        complete then it closes the TCP connection cleanly.
        When the opening handshake fails, :meth:`fail_connection` does the
        same. There's no data transfer phase in that case.
        Nz0Websocket task finished. Closing the connection.z4Websocket handler cancelled. Closing the connection.z%Websocket half-closing TCP connectionr   rL   z5Timeout waiting for TCP connection to close. Abortingz"Error closing websocket connection)r(   r   r8   r/   rb   r+   rT   r   r9   can_write_eof	write_eofr*   rM   rI   
is_closingrg   r_   r   ri   r`   BaseException	exceptionr7   r2   r2   r3   rF     s  	




z+WebsocketImplProtocol.auto_close_connectionc                 C   s@   | j jtur
td| j D ]}|tdd |  qdS )z
        Raise ConnectionClosed in pending keepalive pings.
        They'll never receive a pong once the connection is closed.
        zWWebscoket about_pings should only be called after connection state is changed to CLOSEDN)	r   rq   rx   r   r"   valuesset_exceptionr	   rT   )r1   r^   r2   r2   r3   abort_pings  s   
z!WebsocketImplProtocol.abort_pingsc              	      s   |dkr|  || dS | j4 I dH 1 | jjtu r:| j|| | j }| |I dH  W d  I dH  dS W d  I dH  dS 1 I dH sKw   Y  dS )a  
        Perform the closing handshake.
        This is a websocket-protocol level close.
        :meth:`close` waits for the other end to complete the handshake and
        for the TCP connection to terminate.
        :meth:`close` is idempotent: it doesn't do anything once the
        connection is closed.
        :param code: WebSocket close code
        :param reason: WebSocket close reason
        rk   N)ra   r#   r   rq   rr   rs   rp   	send_data)r1   rm   rn   rp   r2   r2   r3   rg     s   
.zWebsocketImplProtocol.closerL   c                    s  | j  r
td| j  I dH  | jjtu r!| j   tdd}z[zKt	
 | _t	| j|}| j|f}t	j|t	jdI dH \}}tt|}|| ju r]|D ]}|  qRt	 | j  | W W d| _| j   S  t	jy~   |r}|   w d| _| j   w )a  
        Receive the next message.
        Return a :class:`str` for a text frame and :class:`bytes` for a binary
        frame.
        When the end of the message stream is reached, :meth:`recv` raises
        :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it
        raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal
        connection closure and
        :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol
        error or a network failure.
        If ``timeout`` is ``None``, block until a message is received. Else,
        if no message is received within ``timeout`` seconds, return ``None``.
        Set ``timeout`` to ``0`` to check if a message was already received.
        :raises ~websockets.exceptions.ConnectionClosed: when the
            connection is closed
        :raises asyncio.CancelledError: if the websocket closes while waiting
        :raises ServerError: if two tasks call :meth:`recv` or
            :meth:`recv_streaming` concurrently
        zKcannot call recv while another task is already waiting for the next messageN;Cannot receive from websocket interface after it is closed.return_when)r$   lockedr   acquirer   rq   rx   releaser   r/   Futurer%   rD   r!   getwaitFIRST_COMPLETEDnextiterrT   rb   result)r1   rL   assembler_gettasksrI   pending	done_taskpr2   r2   r3   recv  sH   







zWebsocketImplProtocol.recv   c           
         sb  | j  r
td| j  I dH  | jjtu r!| j   tdg }d}zzad| _	t
 | _	 t
| jd}| j|f}t
j|t
jdI dH \}}tt|}|| ju rc|D ]}|  qXt
 | }	|	du rln||	 t||krxn	t
dI dH  q0| j  W n t
jy   |r|   w W d| _d| _	| j   |S d| _d| _	| j   w )a  
        Receive the messages which have arrived since last checking.
        Return a :class:`list` containing :class:`str` for a text frame
        and :class:`bytes` for a binary frame.
        When the end of the message stream is reached, :meth:`recv_burst`
        raises :exc:`~websockets.exceptions.ConnectionClosed`. Specifically,
        it raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a
        normal connection closure and
        :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol
        error or a network failure.
        :raises ~websockets.exceptions.ConnectionClosed: when the
            connection is closed
        :raises ServerError: if two tasks call :meth:`recv_burst` or
            :meth:`recv_streaming` concurrently
        zQcannot call recv_burst while another task is already waiting for the next messageNr   FTr   r   )r$   r   r   r   r   rq   rx   r   r   r'   r/   r   r%   rD   r!   r   r   r   r   r   rT   rb   r   r[   ru   r_   )
r1   max_recvmessagesr   r   rI   r   r   r   mr2   r2   r3   
recv_burst:  sd   







z WebsocketImplProtocol.recv_burstc                 C  s   | j  r
td| j  I dH  | jjtu r!| j   tdz7d}t	
 | _d| _| j 2 z3 dH W }| j r@d} n|V  q16 |rKt	 W d| _d| _| j   dS d| _d| _| j   w )af  
        Receive the next message frame by frame.
        Return an iterator of :class:`str` for a text frame and :class:`bytes`
        for a binary frame. The iterator should be exhausted, or else the
        connection will become unusable.
        With the exception of the return value, :meth:`recv_streaming` behaves
        like :meth:`recv`.
        zUCannot call recv_streaming while another task is already waiting for the next messageNr   FT)r$   r   r   r   r   rq   rx   r   r   r/   r   r%   r'   r!   get_iterrI   rb   )r1   	cancelledr   r2   r2   r3   recv_streaming  s<   
	


z$WebsocketImplProtocol.recv_streamingmessagec              	      s  | j 4 I dH n | jjttfv rtd| jr| j r!tdt	|t
r;| j|d | | j I dH  n0t	|tttfrU| j| | | j I dH  nt	|tr^tdt	|trgtdtdW d  I dH  dS 1 I dH s|w   Y  dS )a  
        Send a message.
        A string (:class:`str`) is sent as a `Text frame`_. A bytestring or
        bytes-like object (:class:`bytes`, :class:`bytearray`, or
        :class:`memoryview`) is sent as a `Binary frame`_.
        .. _Text frame: https://tools.ietf.org/html/rfc6455#section-5.6
        .. _Binary frame: https://tools.ietf.org/html/rfc6455#section-5.6
        :meth:`send` also accepts an iterable of strings, bytestrings, or
        bytes-like objects. In that case the message is fragmented. Each item
        is treated as a message fragment and sent in its own frame. All items
        must be of the same type, or else :meth:`send` will raise a
        :exc:`TypeError` and the connection will be closed.
        :meth:`send` rejects dict-like objects because this is often an error.
        If you wish to send the keys of a dict-like object as fragments, call
        its :meth:`~dict.keys` method and pass the result to :meth:`send`.
        :raises TypeError: for unsupported inputs
        Nz7Cannot write to websocket interface after it is closed.z9Cannot write to websocket interface after it is finished.utf-8zdata is a dict-like objectz0Fragmented websocket messages are not supported.z"Websocket data must be bytes, str.)r#   r   rq   rx   CLOSINGr   r(   rI   r   rO   rd   	send_textencoder   rp   bytes	bytearray
memoryviewsend_binaryr   	TypeErrorr   NotImplementedError)r1   r   r2   r2   r3   send  s0   



.zWebsocketImplProtocol.sendrY   c              	      s.  | j 4 I dH  | jjttfv rtd| jr| jjs td|dur:t	|t
r/|d}nt	|ttfr:t|}|| jv rCtd|du sL|| jv r^tdtd}|du sL|| jv sL| jj | j|< | j| | | j I dH  t| j| W  d  I dH  S 1 I dH sw   Y  dS )a  
        Send a ping.
        Return an :class:`~asyncio.Future` that will be resolved when the
        corresponding pong is received. You can ignore it if you don't intend
        to wait.
        A ping may serve as a keepalive or as a check that the remote endpoint
        received all messages up to this point::
            await pong_event = ws.ping()
            await pong_event # only if you want to wait for the pong
        By default, the ping contains four random bytes. This payload may be
        overridden with the optional ``data`` argument which must be a string
        (which will be encoded to UTF-8) or a bytes-like object.
        Nz:Cannot send a ping when the websocket interface is closed.zCCannot send a ping when the websocket has no I/O protocol attached.r   z-already waiting for a pong with the same dataz!I    )r#   r   rq   rx   r   r   r   r   r   rO   rd   r   r   r   r   r"   
ValueErrorstructpackrandomgetrandbitsr;   	send_pingr   rp   r/   rC   r1   rY   r2   r2   r3   r^     s6   

0zWebsocketImplProtocol.ping    c              	      s   | j 4 I dH G | jjttfv r	 W d  I dH  dS t|tr(|d}nt|tt	fr3t
|}| j| | | j I dH  W d  I dH  dS 1 I dH sUw   Y  dS )z
        Send a pong.
        An unsolicited pong may serve as a unidirectional heartbeat.
        The payload may be set with the optional ``data`` argument which must
        be a string (which will be encoded to UTF-8) or a bytes-like object.
        Nr   )r#   r   rq   rx   r   rO   rd   r   r   r   r   	send_pongr   rp   r   r2   r2   r3   pong  s   
.zWebsocketImplProtocol.pongc                    sf   |D ]-}|r| j |I d H  q| jr(| j s(| jr(| j s(| jd  qtj| j dd qd S )N      ?r}   )r   r   r,   rI   r(   r>   r   rg   )r1   rp   rY   r2   r2   r3   r     s   zWebsocketImplProtocol.send_datac                    sR   | j jttfv rt|dkr| |I d H  t|dkr'| |I d H  d S d S Nr   )r   rq   rr   r   ru   r   rW   r1   rp   events_to_processr2   r2   r3   async_data_received-  s   z)WebsocketImplProtocol.async_data_receivedc                 C   sR   | j | | j  }| j  }t|dkst|dkr't| || d S d S r   )r   receive_datarp   events_receivedru   r/   rD   r   )r1   rY   rp   r   r2   r2   r3   data_received5  s   


z#WebsocketImplProtocol.data_receivedc                    s   | j jttfv rt|dkr| |I d H  t|dkr%| |I d H  | jr-| j  | j	rE| j	
 sE| jrE| j
 sE| jd  d S tj| jdd d S )Nr   r   r}   )r   rq   rr   r   ru   r   rW   r%   rT   r,   rI   r(   r>   r   rg   r   r   r2   r2   r3   async_eof_received>  s"   
z(WebsocketImplProtocol.async_eof_receivedc                 C   s4   | j   | j  }| j  }t| || dS )NF)r   receive_eofrp   r   r/   rD   r   r   r2   r2   r3   eof_receivedT  s   



z"WebsocketImplProtocol.eof_receivedc                 C   sD   | j jtks| j jdd t| j _|   | jr | jd dS dS )z5
        The WebSocket Connection is Closed.
        rk   )rm   N)r   rq   rx   rt   r   r*   r>   )r1   excr2   r2   r3   connection_lost]  s   z%WebsocketImplProtocol.connection_lostc                 C  s,   z
	 |   I d H V  q ty   Y d S w r5   )r   r
   r7   r2   r2   r3   	__aiter__k  s   zWebsocketImplProtocol.__aiter__)Nr-   r-   r.   Nr5   )rH   N)rk   rl   )ro   rl   )r   )r   )7__name__
__module____qualname__r   __annotations__r   r   r/   AbstractEventLoopintfloatr   dictr   r   r0   boolTaskr4   propertyr6   r<   r?   rG   rM   r   r   rW   r   rR   rE   rj   rd   ra   r|   rF   r   rg   r   r   r   r   r   r   r   r   r^   r   r   r   r   r   r   r   r   r2   r2   r2   r3   r   (   s   
 





 
0
7
'
X;G$30		r   ))r/   r   r   collections.abcr   r   r   r   typingr   r   websockets.exceptionsr   r	   r
   websockets.framesr   r   websockets.protocolr   r   websockets.serverr   ImportErrorwebsockets.connectionr   websockets.typingr   	sanic.logr   $sanic.server.protocols.base_protocolr   
exceptionsr   r   rX   r   rr   r   rx   r   r2   r2   r2   r3   <module>   s.    