
    ei                      U d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZ ddlmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZm Z m!Z!m"Z"m#Z#m$Z$ ddl%m&Z&m'Z' ddl(m)Z)m*Z*m+Z+m,Z,m-Z- ddl.m/Z/m0Z0 ddl1m2Z2 ddl3m4Z4m5Z5 ddl6m7Z7 ddl1m8Z8m9Z9m:Z: erddl;m<Z< ddl(m=Z= ddl.m>Z> ddl?m@Z@ ddlAmBZB ddlCmDZDmEZEmFZF ddlGmHZHmIZI  e"d          ZJed eKe>gdf         ZLed eKeMz  gdf         ZNed e0gdf         ZOed gdf         ZPed eQeMgdf         ZReSeKeQf         ZTeSeKe-f         ZU eed!"          ZVd#eWd$<   e G d% d&                      ZX G d' d(e          ZY G d) d*e0          ZZ G d+ d,eZe4          Z[ G d- d.eZe5          Z\d<d3Z] G d4 d5          Z^e!d6         Z_ G d7 d e^          Z`e$ G d8 d9e^                      Zae$ G d: d;                      ZbdS )=z0
The Curl CFFI WebSocket client implementation.
    )annotationsN)InvalidStateError)	AwaitableCallable	Generator)suppress)	dataclassfield)IntEnum)partialdumpsloads)uniform)select)TYPE_CHECKINGClassVarLiteralTypeVarcastfinal   )CURL_SOCKET_BADget_selector)	CurlECode
CurlFollowCurlInfoCurlOpt
CurlWsFlag)Curl	CurlError)CurlCffiWarning   )SessionClosedTimeout)Response)NOT_SET
NotSetTypeset_curl_options)Self)CurlHttpVersion)CurlWsFrame)CookieTypes)HeaderTypes)BrowserTypeLiteralExtraFingerprintsExtraFpDict)AsyncSession	ProxySpecT	WebSocket),:)
separatorszpartial[str]dumps_partialc                  b    e Zd ZU dZdZded<   dZded<   dZd	ed
<    ed           Z	ded<   dS )WebSocketRetryStrategyuQ  Configurable WebSocket policy for retrying failed message receives.

    When enabled, each failed receive attempt will use exponential backoff with
    jitter.

    Calculation: ``delay * 2^(count - 1) ± 10%``

    Args:
        retry: Enable or disable WebSocket message receive retry policy.
        delay: The base value (seconds) to compute the retry delay from.
        count: How many times to retry a receive operation before giving up.
        codes: Set of ``CurlECode`` values for which the receive operation
            should be retried. Default is ``CurlECode.RECV_ERROR``.
    Fboolretryg333333?floatdelay   intcountc                     t           j        hS N)r   
RECV_ERROR     H/usr/local/lib/python3.11/dist-packages/curl_cffi/requests/websockets.py<lambda>zWebSocketRetryStrategy.<lambda>L   s    9;O:P rH   )default_factoryzset[CurlECode]codesN)
__name__
__module____qualname____doc__r>   __annotations__r@   rC   r
   rL   rG   rH   rI   r<   r<   8   so           EEENNNN!E2P2PQQQEQQQQQQrH   r<   c                  Z    e 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dZdZdS )WsCloseCodez?See: https://www.iana.org/assignments/websocket/websocket.xhtml  i  i  i  i  i  i  i  i  i  i  i  i  i  i  i  i  i  N)rM   rN   rO   rP   OK
GOING_AWAYPROTOCOL_ERRORUNSUPPORTED_DATAUNKNOWNABNORMAL_CLOSUREINVALID_DATAPOLICY_VIOLATIONMESSAGE_TOO_BIGMANDATORY_EXTENSIONINTERNAL_ERRORSERVICE_RESTARTTRY_AGAIN_LATERBAD_GATEWAYTLS_HANDSHAKEUNAUTHORIZED	FORBIDDENTIMEOUTrG   rH   rI   rS   rS   O   sr        II	BJNGLONOOKMLIGGGrH   rS   c                  (     e Zd ZdZ	 d
d fd	Z xZS )WebSocketErrorzWebSocket-specific error.r   messagestrcode$WsCloseCode | CurlECode | Literal[0]returnNonec                L    t                                          ||           d S rE   )super__init__)selfri   rk   	__class__s      rI   rq   zWebSocketError.__init__i   s%     	$'''''rH   )r   )ri   rj   rk   rl   rm   rn   )rM   rN   rO   rP   rq   __classcell__rs   s   @rI   rh   rh   f   sO        ## JK( ( ( ( ( ( ( ( ( ( (rH   rh   c                      e Zd ZdZdS )WebSocketClosedzWebSocket is already closed.NrM   rN   rO   rP   rG   rH   rI   rw   rw   o   s         '&&&rH   rw   c                      e Zd ZdZdS )WebSocketTimeoutzWebSocket operation timed out.Nrx   rG   rH   rI   rz   rz   u   s         )(((rH   rz   futasyncio.Future[None]rm   rn   c                T    	 |                      d           dS # t          $ r Y dS w xY w)af  
    Called by the event loop when fd becomes readable/writable.

    We try to set_result() and silently ignore InvalidStateError which is
    raised if the future was already finished/cancelled concurrently.
    This avoids spurious 'Exception in callback' traces in uvloop/asyncio.

    Intentionally using try/except since this is frequently called.
    N)
set_result_InvalidStateError)r{   s    rI   _safe_set_resultr   {   sA    t   s    
''c                      e Zd ZU dZded<   dddddZed d            Zed!d            Zed"d            Z	e
d#d            Ze
d$d            Zd%dZdS )&BaseWebSocket_curl	autoclose_close_code_close_reasondebugclosedztuple[str, ...]	__slots__TF)r   r   curlCurl | NotSetTyper   r=   r   rm   rn   c               Z    || _         || _        d | _        d | _        || _        d| _        d S )NFr   )rr   r   r   r   s       rI   rq   zBaseWebSocket.__init__   s4     )-
('+)- 
!rH   r!   c                x    t          | j        t                    rt          | j                  | _        | j        S )z;Return reference to Curl associated with current WebSocket.)r   )
isinstancer   r)   r!   r   rr   s    rI   r   zBaseWebSocket.curl   s4     dj*-- 	0DJ///DJzrH   
int | Nonec                    | j         S )z<The WebSocket close code, if the connection has been closed.)r   r   s    rI   
close_codezBaseWebSocket.close_code   s     rH   
str | Nonec                    | j         S )z>The WebSocket close reason, if the connection has been closed.)r   r   s    rI   close_reasonzBaseWebSocket.close_reason   s     !!rH   rk   rB   reasonbytesc                2    t          j        d|           |z   S )N!H)structpack)rk   r   s     rI   _pack_close_framezBaseWebSocket._pack_close_frame   s    {4&&//rH   frametuple[int, str]c                   t          |           dk     rt          j        }d}n	 t          j        d|           d         }| dd                                          }nU# t          $ r }t          dt          j                  |d }~wt          $ r }t          dt          j
                  |d }~ww xY w|t          j        k    s|dk     s|dk    rt          d	| t          j
                  ||fS )
Nr    r   r   zInvalid close messagezInvalid close framerT   i  zInvalid close code: )lenrS   rY   r   unpack_fromdecodeUnicodeDecodeErrorrh   r[   	ExceptionrW   )r   rk   r   es       rI   _unpack_close_framez!BaseWebSocket._unpack_close_frame   s   u::>>#+DFF
)$66q9qrr))++%   $+[-E     $);+E 
 {***dTkkTT\\$1411;3M   V|s#   7A 
B.&BB.B))B.c                F    d| _         | j                                         dS )z$Terminate the underlying connection.TN)r   r   closer   s    rI   	terminatezBaseWebSocket.terminate   s!    	rH   N)r   r   r   r=   r   r=   rm   rn   )rm   r!   )rm   r   )rm   r   )rk   rB   r   r   rm   r   )r   r   rm   r   rm   rn   )rM   rN   rO   r   rQ   rq   propertyr   r   r   staticmethodr   r   r   rG   rH   rI   r   r      s        "I     =APU" " " " " "    X       X  " " " X" 0 0 0 \0    \0     rH   r   )openr   datari   errorc                       e Zd ZdZdZefddddddddddp fdZdqdZdrdZdsd!Z	ddddedd"dddddd#ddddddd$dddd%dfdtdRZ
dudTZdvdVZdwdWZedXdxd\Zej        fdydaZdzdbZd{dcZd|ddZeded}diZd~djZdddkZej        dlfddoZ xZS )r6   z)A WebSocket implementation using libcurl.)skip_utf8_validation	_emitterskeep_runningTFN)r   r   r   on_openon_closeon_data
on_messageon_errorr   r   r   r=   r   r   r   ON_OPEN_T | Noner   ON_CLOSE_T | Noner   ON_DATA_T | Noner   ON_MESSAGE_T | Noner   ON_ERROR_T | Nonerm   rn   c                   t                                          |||           || _        d| _        i | _        |r
|| j        d<   |r
|| j        d<   |r
|| j        d<   |r
|| j        d<   |	r|	| j        d<   dS dS )	a|  
        Args:
            autoclose: whether to close the WebSocket after receiving a close frame.
            skip_utf8_validation: whether to skip UTF-8 validation for text frames in
                run_forever().
            debug: print extra curl debug info.

            on_open: open callback, ``def on_open(ws)``
            on_close: close callback, ``def on_close(ws, code, reason)``
            on_data: raw data receive callback, ``def on_data(ws, data, frame)``
            on_message: message receive callback, ``def on_message(ws, message)``
            on_error: error callback, ``def on_error(ws, exception)``
        r   r   r   Fr   r   r   ri   r   N)rp   rq   r   r   r   )rr   r   r   r   r   r   r   r   r   r   rs   s             rI   rq   zWebSocket.__init__   s    4 	diuEEE*>!"'HJ 	-%,DN6" 	/&.DN7# 	-%,DN6" 	3(2DN9% 	/&.DN7###	/ 	/rH   c                2    | j         rt          d          | S )NWebSocket is closedr   rw   r   s    rI   __iter__zWebSocket.__iter__	  s     ; 	9!"7888rH   r   c                `    |                                  \  }}|t          j        z  rt          |S rE   )recvr    CLOSEStopIterationrr   msgflagss      rI   __next__zWebSocket.__next__  s.    YY[[
U:## 	 
rH   
event_typeEventTypeLiteralargs+str | bytes | int | CurlWsFrame | CurlErrorc                $   | j                             |          }|rs	  || g|R  }d S # t          $ rZ}| j                             d          }|r || |          }n&t          j        d| dt
          d           Y d }~d S Y d }~d S d }~ww xY wd S )Nr   zWebSocket callback 'z' failedr   )
stacklevel)r   getr   warningswarnr#   )rr   r   r   callback_r   error_callbacks          rI   _emitzWebSocket._emit  s    
 261C1CJ1O1O 	HT)D)))    ?C~?Q?Q@ @ " &tQ//AAMCzCCC'#$          AAAAA	 	s   	) 
BA	BB   zgzip, deflate, brr   r   urlrj   params\dict[str, object] | list[object] | tuple[str, int | list[str] | dict[str, str | int]] | NoneheadersHeaderTypes | NonecookiesCookieTypes | Noneauthtuple[str, str] | Nonetimeout+float | tuple[float, float] | object | Noneallow_redirectsbool | CurlFollow | strmax_redirectsrB   proxiesProxySpec | Noneproxyr   
proxy_authverifybool | Nonerefereraccept_encodingimpersonateBrowserTypeLiteral | Noneja3akamaiperkextra_fp&ExtraFingerprints | ExtraFpDict | Nonedefault_headersquotestr | Literal[False]http_versionCurlHttpVersion | None	interfacecertstr | tuple[str, str] | Nonemax_recv_speedcurl_optionsdict[CurlOpt, str] | Noner+   c                L   | j         }t          d i d|ddd|dd|gdd|gdd|gd	|d
|d|d|dd|	gd|
d|dd|gd|d|d|d|d|d|d|d|d|d|d|d|d|d|}|                    t          j        d          }|                                 | S )!aG	  Connect to the WebSocket.

        libcurl automatically handles pings and pongs.
        ref: https://curl.se/libcurl/c/libcurl-ws.html

        Args:
            url: url for the requests.
            params: query string for the requests.
            headers: headers to send.
            cookies: cookies to use.
            auth: HTTP basic auth, a tuple of (username, password), only basic auth is
                supported.
            timeout: how many seconds to wait before giving up.
            allow_redirects: whether to allow redirection. Can be a bool, a
                ``CurlFollow`` value, or the string ``"safe"``.
            max_redirects: max redirect counts, default 30, use -1 for unlimited.
            proxies: dict of proxies to use, prefer to use ``proxy`` if they are the
                same. format: ``{"http": proxy_url, "https": proxy_url}``.
            proxy: proxy to use, format: "http://user@pass:proxy_url".
                Can't be used with `proxies` parameter.
            proxy_auth: HTTP basic auth for proxy, a tuple of (username, password).
            verify: whether to verify https certs.
            referer: shortcut for setting referer header.
            accept_encoding: shortcut for setting accept-encoding header.
            impersonate: which browser version to impersonate.
            ja3: ja3 string to impersonate.
            akamai: akamai string to impersonate.
            perk: perk string to impersonate.
            extra_fp: extra fingerprints options, in complement to ja3 and akamai str.
            default_headers: whether to set default browser headers.
            default_encoding: Encoding for decoding content if charset is not found.
                Defaults to "utf-8".
            quote: Set characters to be quoted (percent-encoded). Default safe
                string is ``!#$%&'()*+,/:;=?@[]~``. If set to a string, the characters
                will be removed from the safe string. If set to ``False``, the URL
                is used as-is (you must encode it yourself).
            http_version: Limiting http version, defaults to http2.
            interface: which interface to use.
            cert: a tuple of (cert, key) filenames for client cert.
            max_recv_speed: maximum receive speed, bytes per second.
            curl_options: extra curl options to use.
        r   methodGETr   params_listNheaders_listcookies_listr   r   r   r   proxies_listr   r   verify_listr   r   r   r   r   r   r   r   r   r   r   r  r   r  r   rG   )r   r*   setoptr   CONNECT_ONLYperform)rr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r   r   s                                rI   connectzWebSocket.connect,  s   Z Y 
 
 

5
 
 v	

 
 
 
 G
 ,O
 (-
 
 %
 "z
 v
 G
  ,O!
" $#
$ %
& 6'
( )
* X+
, ,O-
. %/
0 &1
2  i3
4 *>5
6 7
8 &9
@ KK,a00rH   tuple[bytes, CurlWsFrame]c                   | j         rt          d          | j                                        \  }}|j        t
          j        z  rx	 |                     |          \  | _        | _	        n9# t          $ r,}|j        | _        |                     |j                    d}~ww xY w| j        r|                                  ||fS )z2Receive a single curl websocket fragment as bytes.WebSocket is already closedN)r   rw   r   ws_recvr   r    r   r   r   r   rh   rk   r   r   )rr   chunkr   r   s       rI   recv_fragmentzWebSocket.recv_fragment  s     ; 	A!"?@@@y((**u;)) 
	7;7O7OPU7V7V4 $"4"4!    $%6 

16""" ~ 

e|s   "A+ +
B!5'BB!tuple[bytes, int]c                    g }d}| j                             t          j                  }|t          k    rt          dt          j                  	 	 |                                 \  }}|j	        }|
                    |           |j        dk    r|t          j        z  dk    rnGnE# t          $ r8}|j        t          j        k    rt#          |gg g d          \  }}}n Y d}~nd}~ww xY wd                    |          |fS )z
        Receive a frame as bytes. libcurl splits frames into fragments, so we have to
        collect all the chunks for a frame.
        r   Invalid active socketT      ?NrH   )r   getinfor   ACTIVESOCKETr   rh   r   NO_CONNECTION_AVAILABLEr  r   append	bytesleftr    CONTr"   rk   AGAINr   join)rr   chunksr   sock_fdr  r   r   r   s           rI   r   zWebSocket.recv  s    
 !)##H$9::o%% ')J  	#1133ue$$$?a''EJO,Cq,H,H   6Y_,, %gYB<<GAq!! !!!!		  xx&&s   AB# #
C%-.C  C%c                    |                                  \  }}|t          j        z  st          dt          j                  |                    d          S )zReceive a text frame.zNot valid text frameutf-8)r   r    TEXTrh   rS   r[   r   )rr   r   r   s      rI   recv_strzWebSocket.recv_str  sI    iikke
' 	S !79QRRR{{7###rH   r   r   Callable[[str], T]r5   c               @    |                                  } ||          S )zeReceive a JSON frame.

        Args:
            loads: JSON decoder, default is json.loads.
        )r'  )rr   r   r   s      rI   	recv_jsonzWebSocket.recv_json  s     }}uT{{rH   payloadstr | bytesr   r    c                   |t           j        z  rd| _        | j        rt	          d          t          |t                    r|                                }| j        	                    t          j                  }|t          k    rt          dt          j                  d}|t!          |          k     r||d         }	 | j                            ||          }nV# t$          $ rI}|j        t          j        k    r.t+          g |gg d          \  }}	}|	st          d          |Y d}~ d}~ww xY w||z  }|t!          |          k     |S )zuSend a data frame.

        Args:
            payload: data to send.
            flags: flags for the frame.
        Fr  r  r   Nr  zSocket write timeout)r    r   r   r   rw   r   rj   encoder   r  r   r  r   rh   r   r  r   ws_sendr"   rk   r   r   )
rr   r+  r   r#  offsetcurrent_buffern_sentr   r   	writeables
             rI   sendzWebSocket.send  sq    :## 	& %D; 	A!"?@@@ gs## 	'nn&&G)##H$9::o%% ')J   s7||##$VWW-N**>5AA   6Y_,,&,R'B&D&DOAy!$ L,-CDD!KHHHH fF s7||## s   ?C 
D.%>D)(D))D.c                B    |                      |t          j                  S )zVSend a binary frame.

        Args:
            payload: binary data to send.
        r4  r    BINARYrr   r+  s     rI   send_binaryzWebSocket.send_binary       yy*"3444rH   c                B    |                      |t          j                  S )ztSend a binary frame, alias of :meth:`send_binary`.

        Args:
            payload: binary data to send.
        r6  r8  s     rI   
send_byteszWebSocket.send_bytes  r:  rH   c                B    |                      |t          j                  S )zRSend a text frame.

        Args:
            payload: text data to send.
        r4  r    r&  r8  s     rI   send_strzWebSocket.send_str#       yy*/222rH   r   objectr   Callable[..., str]c               >    |                       ||                    S )zSend a JSON frame.

        Args:
            payload: data to send.
            dumps: JSON encoder, default is json.dumps.
        r?  rr   r+  r   s      rI   	send_jsonzWebSocket.send_json+  s     }}UU7^^,,,rH   c                B    |                      |t          j                  S )zMSend a ping frame.

        Args:
            payload: data to send.
        )r4  r    PINGr8  s     rI   pingzWebSocket.ping6  r@  rH   c                `   |r | j         |fi |}| j                            t          j                  }|t
          k    rt          dt          j                  | 	                    d           g }d| _
        | j
        r.	 |                                 \  }}|j        }| 	                    d||           |                    |           |j        dk    r|t          j        z  dk    srd| j        v rd                    |          }	|t          j        z  rz| j        ss	 |	                                }
n_# t,          $ rP}t.          j        | _        |                     t.          j                   t          dt.          j                  |d	}~ww xY w|	}
|t          j        z  s|t          j        z  r| 	                    d|
           g }|t          j        z  r,d
| _
        | 	                    d| j        pd| j        pd           n# t<          $ r}|j        t          j         k    rtC          |gg g d          \  }}}n[| 	                    d|           | j"        s=t.          j#        }tI          |t                    r|j        }|                     |            Y d	}~nd	}~ww xY w| j
        ,d	S d	S )zRun the WebSocket forever. See :meth:`connect` for details on parameters.

        libcurl automatically handles pings and pongs.
        ref: https://curl.se/libcurl/c/libcurl-ws.html
        r  r   Tr   r   ri   rH   zInvalid UTF-8NFr   r   r  r   )%r  r   r  r   r  r   rh   r   r  r   r   r  r   r  r  r    r  r   r!  r&  r   r   r   rS   r[   r   r   r7  r   r   r"   rk   r   r   r   rY   r   )rr   r   kwargsr   r#  r"  r  r   r   r   emit_msgr   rk   s                rI   run_foreverzWebSocket.run_forever>  s     	,S++F++A)##H$9::o%% ')J   	

6 !  /	.#1133u

65%000e$$$1,,1HA1M1M ..((6**C
/ '9R '%47JJLLHH1 % % %/:/GD, JJ{'?@@@"0 /1I# ##$%% $'
 11 8uz7N 8

9h777:++ Y(-D%JJw(8(=At?Q?WUWXXX 
 
 
6Y_,,$gYB<<GAq!!JJw***; )$/$7%a88 *#$6D

4((( !!!!
K  /	 /	 /	 /	 /	sF   A(H *4H D4 3H 4
F>AF		FA6H 
J!BJJ!rH   rk   ri   c                    |                      ||          }|                     |t          j                  }|                                  dS )znClose the connection.

        Args:
            code: close code.
            message: close reason.
        N)r   r4  r    r   r   )rr   rk   ri   r   r   s        rI   r   zWebSocket.close  sE     $$T733IIc:+,,rH   )r   r   r   r=   r   r=   r   r=   r   r   r   r   r   r   r   r   r   r   rm   rn   )rm   r6   rm   r   )r   r   r   r   rm   rn   )6r   rj   r   r   r   r   r   r   r   r   r   r   r   r   r   rB   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r=   r   r   r   r   r   r   r   r   r  rB   r  r  rm   r+   )rm   r  )rm   r  )rm   rj   )r   r(  rm   r5   )r+  r,  r   r    rm   rB   )r+  r   rm   rB   )r+  r   )r+  rj   rm   rB   )r+  rA  r   rB  rm   rB   )r+  r,  rm   rB   )r   )r   rj   rm   rn   )rk   rB   ri   r   )rM   rN   rO   rP   r   r(   rq   r   r   r   r  r  r   r'  
json_loadsr*  r    r7  r4  r9  r<  r?  r:   rF  rI  rM  rS   rU   r   rt   ru   s   @rI   r6   r6      s[       33I #*(/ %*$(&*$(*.&*(/ (/ (/ (/ (/ (/ (/ (/T   
      @ &*&*'+?F37$( -1""&915!;? $&(/3 $-126Ap p p p pd   ,' ' ' '@$ $ $ $ 8B       '-- - - - -^5 5 5 55 5 5 53 3 3 3 ?L	- 	- 	- 	- 	- 	-3 3 3 3E E E E EN !,         rH   c                      e Zd ZU dZdZdZded<   dddd	ddd
dddddddb fd%Zedcd'            Z	eddd(            Z
ded)Zdfd+Zdgd2Zdfd3Zdhd5Zdid8Zdjd9Zd
d:dkd>Zd
d:dld@Zed
dAdmdEZej        d
fdndJZdodKZdodLZdpdMZedNdqdRZdrdTZej         dUdVfdsdYZ!dj fdZZ"djd[Z#djd\Z$dtd^Z%dudvd_Z&dj fd`Z'dwdaZ( xZ)S )xAsyncWebSocketz<
    An asyncio WebSocket implementation using libcurl.
    )session_loop_sock_fd_close_lock_terminate_lock
_read_task_write_task_receive_queue_send_queue_max_send_batch_size_coalesce_frames_recv_time_slice_send_time_slice_terminated_terminated_eventws_retry_transport_exception_max_message_sizedrain_on_error_block_on_recv_queue_fulli   zClassVar[int]_MAX_CURL_FRAME_SIZETF       Ng{Gzt?gMbP?i  @ )r   r   recv_queue_sizesend_queue_sizemax_send_batch_sizecoalesce_framesrb  recv_time_slicesend_time_slicemax_message_sizere  block_on_recv_queue_fullrS  r3   r   r!   r   r=   r   rj  rB   rk  rl  rm  rb  WebSocketRetryStrategy | Nonern  r?   ro  rp  re  rq  rm   rn   c               6   t                                          |||           || _        d| _        d| _        d| _        t          j                    | _        t          j                    | _
        t          j                    | _        d| _        d| _        t          j        |          | _        t          j        |          | _        || _        || _        |	pt)                      | _        |
| _        || _        d| _        || _        || _        || _        dS )a  Initializes an Async WebSocket session.

        Do not instantiate this class directly. Use ``AsyncSession.ws_connect``.

        This class implements an async context manager, closing the connection
        automatically on exit:

        ::

            async with AsyncSession() as session:
                async with session.ws_connect("wss://api.example.com") as ws:
                    await ws.send("Hello")
                    msg = await ws.recv()

        Note:
            Architecture: This uses a decoupled I/O model. Network operations run in
            background tasks. Errors are raised in subsequent calls to send() or recv().

            Performance: The time_slice defaults (5ms read / 1ms write) favor reading
            to compensate for libcurl's overhead. Increase these values to allocate more
            CPU time to I/O operations at the cost of event loop latency.

        Args:
            session (AsyncSession): The parent session object.
            curl (Curl): The underlying Curl handle.
            autoclose (bool): Automatically close on receiving a close frame.
            debug (bool): Enable verbose debug logging.
            recv_queue_size (int): Max number of incoming messages to buffer.
            send_queue_size (int): Max number of outgoing messages to buffer.
            max_send_batch_size (int): Max frames to coalesce per transmission.
            coalesce_frames (bool): Combine small frame payloads to improve throughput.
            ws_retry (WebSocketRetryStrategy): Retry configuration for failed receives.
            recv_time_slice (float): Max seconds to read messages before yielding.
            send_time_slice (float): Max seconds to write messages before yielding.
            max_message_size (int): Max size (bytes) of a single received message.
            drain_on_error (bool): Yield buffered messages before raising errors.
            block_on_recv_queue_full (bool): Behavior when the receive queue is full.
                If True (default), the reader blocks (may cause timeouts).
                If False, the connection fails immediately to prevent data loss.

        See also:
            - https://curl.se/libcurl/c/curl_ws_recv.html
            - https://curl.se/libcurl/c/curl_ws_send.html
        r   NF)maxsize)rp   rq   rS  rT  rU  r`  asyncioLockrV  	threadingrW  Eventra  rX  rY  QueuerZ  r[  r\  r]  r<   rb  r^  r_  rc  rd  re  rf  )rr   rS  r   r   r   rj  rk  rl  rm  rb  rn  ro  rp  re  rq  rs   s                  rI   rq   zAsyncWebSocket.__init__  s   | 	diuEEE/67;
!&)0/8~/?/?07596:>Em#?
 ?
 ?
 <C=#<
 <
 <
 *=!&5080T<R<T<T'6'66:!&6$2/G&&&rH   asyncio.AbstractEventLoopc                h    | j         %t          t          j                              | _         | j         S )z)Get a reference to the running event loop)rT  r   rv  get_running_loopr   s    rI   loopzAsyncWebSocket.loop  s,     :%g&>&@&@AADJzrH   c                4    | j                                         S )z6Returns the current number of items in the send queue.)r[  qsizer   s    rI   rk  zAsyncWebSocket.send_queue_size  s     %%'''rH   c                    | j         s| j        rdS | j        r| j                                        rdS | j        o| j                                         S )a  
        Checks if the background I/O tasks are still running.

        Returns ``False`` if either the read or write task has terminated due
        to an error or a clean shutdown.

        Note: This is a snapshot in time. A return value of ``True`` does not
        guarantee the next network operation will succeed, but ``False``
        definitively indicates the connection is no longer active.
        F)r   r`  rX  donerY  r   s    rI   is_alivezAsyncWebSocket.is_alive  sa     ; 	$* 	5? 	t3355 	5$@)9)>)>)@)@AArH   r+   c                
   K   | S )a@  Enable context manager usage for automatic session management and closure.
        This cannot be used to initiate a WebSocket connection, that must be done
        beforehand using the :meth:`AsyncSession.ws_connect()` factory method.

        Returns:
            Self: The instantiated AsyncWebSocket object.
        rG   r   s    rI   
__aenter__zAsyncWebSocket.__aenter__+  s       rH   exc_typetype[BaseException] | Noneexc_valBaseException | Noneexc_tbobject | Nonec                >   K   |                                   d{V  dS )zQ
        On exiting the context manager, close the WebSocket connection.
        N)r   )rr   r  r  r  s       rI   	__aexit__zAsyncWebSocket.__aexit__5  s.       jjllrH   c                2    | j         rt          d          | S )NzWebSocket has been closedr   r   s    rI   	__aiter__zAsyncWebSocket.__aiter__@  s     ; 	?!"=>>>rH   r   c                   K   	 |                                   d {V \  }}n# t          $ r	 t          d w xY w|t          j        z  rt          |S rE   )r   rw   StopAsyncIterationr    r   r   s      rI   	__anext__zAsyncWebSocket.__anext__E  sp      	/#yy{{******JC 	/ 	/ 	/$$.	/ :## 	%$$
s   " 5excr   c                \    | j         s| j        dS || _        |                                  dS )al  Finalize the connection into a terminal state.

        This method is called for all terminal conditions, including:
        - normal WebSocket closure
        - protocol errors
        - transport errors

        After this method is called, no further messages will be delivered
        and all ``recv()`` calls will fail. ``_finalize_connection()`` is intended
        for event-loop context, but ``terminate()`` is thread-safe.

        Args:
            exc (Exception): The exception object that gets raised. This does not
            have to be an error, enqueuing ``WebSocketClosed`` indicates closure.
        N)r   rc  r   )rr   r  s     rI   _finalize_connectionz#AsyncWebSocket._finalize_connectionO  s9    " ; 	$3?F$'!rH   c                   | j         dS | j        rt          d          t          t          | j                            t          j                            | _	        | j	        t          k    rt          dt          j                  dt          |           d}| j                            |                                 | d          | _         | j                            |                                 | d	          | _        dS )
a  Start the read/write I/O loop tasks.

        NOTE: This should be called only once after object creation by the factory.
        Once started, the tasks cannot be restarted again, this is a one-shot.

        Raises:
            WebSocketError: The WebSocket FD was invalid.
        NzWebSocket already terminatedzInvalid active socket.)rk   z
WebSocket-z#xz-read)namez-write)rX  r`  rw   r   rB   r   r  r   r  rU  r   rh   r   r  idr~  create_task
_read_loop_write_looprY  )rr   ws_ids     rI   _start_io_taskszAsyncWebSocket._start_io_tasksf  s    ?&F  	B!"@AAA S$)"3"3H4I"J"JKK=O++ (y/P   
 0"T((/// )//0A0A5/XX900%5%5%5 1 
 
rH   r   r   float | Noner  c               *  K   | j         | j        s| j         	 | j                                        S # t          j        $ r Y nw xY w| j         | j         | j        | j                                        rt          d          t	          j	        | j        
                                          }	 t	          j        || j        ft          j        |           d{V \  }}nu# t          j        $ rc |                                sM|                                }t          t          j                  5  | d{V  ddd           n# 1 swxY w Y    w xY w|su|                                }t          t          j                  5  | d{V  ddd           n# 1 swxY w Y   | j         | j         t!          dt"          j                  ||v r| d{V S |                                }t          t          j                  5  | d{V  ddd           n# 1 swxY w Y   | j        rJt          t          j                  5  | j                                        cddd           S # 1 swxY w Y   | j         | j         t          d          )a  Receive a WebSocket message.

        This method waits for and returns the next complete WebSocket message.

        Args:
            timeout: How many seconds to wait for a message before raising
            a timeout error.

        Returns:
            tuple[bytes, int]: A tuple with the received payload and flags.

        Raises:
            WebSocketTimeout: If the timeout expires.
            WebSocketClosed: If the connection is closed.
            WebSocketError: If a network-level transport error occurs.

        Notes:
            ``WebSocketError`` exceptions may have originated from a prior
            ``send()`` or ``recv()`` operation, since all operations
            share the same transport state once a failure occurs.

            This method does not wait for additional messages after a transport
            error is detected. If ``drain_on_error=True``, subsequent calls to
            ``recv()`` will return any messages that were buffered in the receive
            queue at the time the reader failed, before the connection error is raised.

            Concurrent calls to ``recv()`` are supported and safe; each caller
            awaits the next available message and will receive distinct messages
            in FIFO order.

            If this coroutine is cancelled while a message is being received,
            that message may be dropped. Cancellation is treated as abandoning
            the receive operation.
        Nr   return_whenr   zWebSocket recv() timed outzConnection closed)rc  re  rZ  
get_nowaitrv  
QueueEmptyrX  r  rw   r  r   waitFIRST_COMPLETEDCancelledErrorcancelr   rz   r   OPERATION_TIMEDOUT)rr   r   queue_waiterr  r   s        rI   r   zAsyncWebSocket.recv  s     H $09L0++	&11333! 	 	 	D	 $0++?"do&:&:&<&<"!"7888 7>6I##%%7
 7
	#Lt/#3        GD!! % 	 	 	$$&& ' ''))g455 ' '&&&&&&&&' ' ' ' ' ' ' ' ' ' ' ' ' ' '	  	##%%A'011 # #""""""""# # # # # # # # # # # # # # # (4//",i.J  
 4%%%%%%%% !!g,-- 	 		 	 	 	 	 	 	 	 	 	 	 	 	 	 	  	8',-- 8 8*55778 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 $0++1222sv   2 AA01C" "AE2	E;EE	EE	E	FF"F	HH #H I--I14I1rj   c                 K   |                      |           d{V \  }}|t          j        z  st          dt          j                  	 |                    d          S # t          $ r }t          dt          j                  |d}~ww xY w)zmReceive a text frame.

        Args:
            timeout: how many seconds to wait before giving up.
        r  NzNot a valid text framer%  zInvalid UTF-8 in text frame)r   r    r&  rh   rS   r[   r   r   )rr   r   r   r   r   s        rI   r'  zAsyncWebSocket.recv_str  s       !IIgI66666666e
' 	U !9;;STTT	;;w'''! 	 	 	 -{/G 	s   A! !
B+BB)r   r   r   Callable[[str | bytes], T]r5   c               D  K   |                      |           d{V \  }}|st          dt          j                  	  ||          S # t          $ r }t          dt          j                  |d}~wt
          $ r#}t          d| t          j                  |d}~ww xY w)a  Receive a JSON frame.

        Args:
            loads: JSON decoder, default is :meth:`json.loads`.
            timeout: how many seconds to wait before giving up.

        Raises:
            WebSocketError: Received frame is invalid or failed to decode JSON.
        r  Nz(Received empty frame, cannot decode JSONz Invalid UTF-8 in JSON text framezInvalid JSON payload: )r   rh   rS   r[   r   r   )rr   r   r   r   r   r   s         rI   r*  zAsyncWebSocket.recv_json  s       		'	22222222a 	 :K<T  		5;;! 	 	 	 2K4L   	 	 	 ,,,k.F 	s#   
A
 

BA//B<BBr+  $str | bytes | bytearray | memoryviewr   r    c                @  K   | j         | j         | j        rt          d          | j        (| j                                        rt          d          t          |t                    r|                    d          }n,t          |t          t          z            rt          |          }	 | j                            ||f           dS # t          j        $ r}| j        rt          d          || j         | j         ||^	 t          j        | j                            ||f          |           d{V  nI# t          j        $ r}t'          d          |d}~ww xY w| j                            ||f           d{V  | j         | j         |Y d}~dS d}~ww xY w)a7  Send a data frame.

        Large payloads are automatically split into fragments but arrive as a
        single logical message.

        Args:
            payload: Data to send (``str``/``bytes``/``bytearray``/``memoryview``).
            flags: Frame type flags (e.g., ``CurlWsFlag.TEXT``).
            timeout: Max seconds to wait if the send queue is full.

        Warning:
            This method is non-blocking. It queues the message for background
            transmission. Use ``await ws.flush()`` to ensure data is sent to
            the socket.
        Nr   z(WebSocket writer terminated; cannot sendr%  z"WebSocket connection is terminatedz>Send queue full (network slow) - hit timeout enqueuing message)rc  r   rw   rY  r  r   rj   r.  	bytearray
memoryviewr   r[  
put_nowaitrv  	QueueFullr`  wait_forputTimeoutErrorrz   )rr   r+  r   r   r  r   s         rI   r4  zAsyncWebSocket.send#  s     * $0++; 	9!"7888 'D,<,A,A,C,C'!"LMMM gs## 	%nnW--GGZ!788 	%GnnG	9''%(899999  	9 	9 	9 U%&JKKQTT (4/S8"!*(,,gu-=>>          +   *X 
 &**GU+;<<<<<<<<< (4/S8 544444+	9s<   .C F(F5D:9F:E	EE4FFc                R   K   |                      |t          j                   d{V S )zSend a binary frame.

        Args:
            payload: binary data to send.

        For more info, see the docstring for :meth:`send()`
        Nr6  r8  s     rI   r9  zAsyncWebSocket.send_binaryb  1       YYw
(9:::::::::rH   c                R   K   |                      |t          j                   d{V S )zSend a binary frame, alias of :meth:`send_binary`.

        Args:
            payload: binary data to send.

        For more info, see the docstring for :meth:`send()`
        Nr6  r8  s     rI   r<  zAsyncWebSocket.send_bytesl  r  rH   c                R   K   |                      |t          j                   d{V S )zSend a text frame.

        Args:
            payload: text data to send.

        For more info, see the docstring for :meth:`send()`
        Nr>  r8  s     rI   r?  zAsyncWebSocket.send_strv  s0       YYw
888888888rH   r   rA  r   rB  c               N   K   |                       ||                     d{V S )zSend a JSON frame.

        Args:
            payload: data to send.
            dumps: JSON encoder, default is :meth:`json.dumps()`.

        For more info, see the docstring for :meth:`send()`
        NrD  rE  s      rI   rF  zAsyncWebSocket.send_json  s4       ]]55>>222222222rH   r,  c                X  K   t          |t                    r|                    d          }nt          |          }t	          |          t          dd          vr*t          dt	          |           t          j                  | 	                    |t          j                   d{V S )zSend a ping frame.

        Args:
            payload: data to send.

        Raises:
            WebSocketError: The payload length is outside specification.

        For more info, see the docstring for :meth:`send()`
        r%  r   ~   zPing frame has invalid length: N)r   rj   r.  r   r   rangerh   r   	TOO_LARGEr4  r    rH  )rr   r+  payload_bytess      rI   rI  zAsyncWebSocket.ping  s       gs## 	+#NN733MM!'NNM}U1c]]22 F#m2D2DFF#  
 YY}jo>>>>>>>>>rH   rH   g      @rk   ri   c           	       K   | j         4 d{V  | j        r	 ddd          d{V  dS d| _        	 | j        r| j                                        sx| j        q|                     ||          }t          j        | j        	                    |t          j        f          |           d{V  |                     |           d{V  n# t          j        t          f$ r Y nw xY w|                                  t!          t          j                  5  t          j        | j                                        |           d{V }ddd           n# 1 swxY w Y   n|# |                                  t!          t          j                  5  t          j        | j                                        |           d{V }ddd           w # 1 swxY w Y   w xY wddd          d{V  dS # 1 d{V swxY w Y   dS )a  
        Performs a graceful WebSocket closing handshake and terminates the connection.

        This method sends a WebSocket close frame to the peer, waits for queued
        outgoing messages to be sent, and then shuts down the connection.

        Args:
            code (int): Close code. Defaults to ``WsCloseCode.OK``.
            message (bytes): Close reason. Defaults to ``b""``.
            timeout (float): How long (in seconds) to wait for the connection to close
                gracefully before force-terminating.
        NTr  )rV  r   rY  r  rc  r   rv  r  r[  r  r    r   flushr  rh   r   r   ra  r  )rr   rk   ri   r   close_framer   s         rI   r   zAsyncWebSocket.close  s      # 	W 	W 	W 	W 	W 	W 	W 	W{ 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W DKW$. ,1133. 19 *.)?)?g)N)NK!*(,,k:;K-LMM '          **W---------(.9   
    g233 W W%.t/E/J/J/L/LgVVVVVVVVAW W W W W W W W W W W W W W W    g233 W W%.t/E/J/J/L/LgVVVVVVVVAW W W W W W W W W W W W W W W W5	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	W 	Ws   	G0G0BCE!C&#E!%C&&E!)-G03E	G0E	G0E	G0!.G3G	GGGGGG00
G:=G:c                   | j         5  | j        r	 ddd           dS d| _        | j        }	 t          j                    }n# t
          $ r d}Y nw xY w	 |t          d          |,||u r(|                    |                                           }n't          j        |                                 |          }n# t          $ r 	 t                                                       | j        r&| j        j        s| j                            d           | j                                         n# | j                                         w xY wY nw xY wddd           dS # 1 swxY w Y   dS )a5  
        Immediately terminates the connection without a graceful handshake.

        This method is a forceful shutdown that cancels all background I/O tasks
        and cleans up resources. It should be used for final cleanup or after an
        unrecoverable error. Unlike ``close()``, it does not attempt to send a close
        frame or wait for pending messages. It schedules the cleanup to run on the
        event loop and returns immediately. It does not wait for cleanup completion.

        This method is thread-safe, task-safe, and idempotent.
        NTzEvent loop not available)rW  r`  rT  rv  r}  RuntimeErrorr  _terminate_helperrun_coroutine_threadsafer   rp   r   rS  _closed	push_curlra  set)rr   r~  current_loopr   rs   s       rI   r   zAsyncWebSocket.terminate  s     ! !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1  $D59ZD$,..    $ $ $#$1<&'ABBB  +0D0D(()?)?)A)ABBAA89O9O9Q9QSWXXA  1 1 11GG%%'''| 5DL,@ 5..t444*..0000D*..00000015!	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1 !	1sw   	EEAEAEAEA&B<;E<
EAD.E.E		EEEEE#&E#c           	     R  -K   | j         j        }| j        j        }| j        j        }| j        }|j        }|j        }|j        }|j	        }| j
        }	 |            |	z   }
| j        j        }| j        j        }| j        j        }t          | j        j                  }t"          j        }t"          j        }t"          j        }t*          j        }t*          j        }t*          j        }||z  }| j        }| j        }d}d}t6          }d}g }d}|j        }|j        }	 | j        s	  |            \  } }!nS# t>          $ rE}"d}#|"j         |k    rd}#n|"j         |k    r?tC          |"          "                                -tG          -fd|D                       rd}#nN|"j         |k    rCtI          dtJ          j&                  }$|"|$_'        d|$_(        | )                    |$           Y d}"~"dS |#r |            }%	  || j*        ||%           |% d{V  n# tV          $ rx}&| )                    tY          d	|& t"          j-                             Y d}&~&| j*        d
k    r"	  || j*                  }'n# tV          $ r Y nw xY wY d}"~"dS Y d}"~"dS Y d}"~"dS d}&~&ww xY w	 | j*        d
k    r"	  || j*                  }'nC# tV          $ r Y n7w xY wn2# | j*        d
k    r"	  || j*                  }'w # tV          $ r Y w w xY ww xY wY d}"~"|rf|"j         |v r]||k     rW|dz  }|d|dz
  z  z  }(|(dz  })|(t]          |) |)          z  }(t_          j0        tc          d|(                     d{V  Y d}"~"H| )                    |"           Y d}"~"dS d}"~"ww xY w|!j2        }*|dk    rd}|*|z  sE|tg          |           z  }||k    r@ |             | )                    tY          d| d| dt"          j4                             dS  ||            |*|z  s|!j5        stg          |          dk    r|d         nd6                    |          }+ |             d}	  ||+|*f           nZ# t^          j7        $ rH |s0| )                    tY          |t"          j8                             Y dS  ||+|*f           d{V  Y nw xY w |            x},|
k    rt_          j0        d           d{V  |,|	z   }
|*|z  r |             	  || |*f           nZ# t^          j7        $ rH |s0| )                    tY          |t"          j8                             Y dS  || |*f           d{V  Y nw xY w| 9                    |            d{V  dS |*|z  ri	  || |*f           nZ# t^          j7        $ rH |s0| )                    tY          |t"          j8                             Y dS  || |*f           d{V  Y nw xY w| j        dS dS # t^          j:        $ r Y dS tV          $ r }"| )                    |"           Y d}"~"dS d}"~"ww xY w)a  
        The main asynchronous task for reading incoming WebSocket frames.

        Attempts to read immediately and only registers an event loop reader if
        the socket returns EAGAIN (empty). It waits for the underlying socket to
        become readable, and upon being woken by the event loop, it drains all
        buffered data from libcurl until it receives an EAGAIN error. This error
        signals that the buffer is empty, and the loop returns to an idle state,
        waiting for the next readability event. This is "optimistic reading".

        To ensure cooperative multitasking during high-volume message streams,
        the loop yields control to the asyncio event loop periodically which
        is tracked using an operation counter.

        If the receive queue becomes full, ``await self._receive_queue.put()`` will
        block the reader loop and stall the socket read task. Thus, appropriate queue
        sizes should be set by the user, to match the speed at which they are expected
        to be consumed. If latency is a factor, a smaller queue size should be used.
        Conversely, a larger queue size provides burst message handling capacity.
        )zerrno 11z resource temporarily unavailablezDReceive queue full; failing connection to preserve message integrityr   FTc              3      K   | ]}|v V  	d S rE   rG   ).0r   err_msgs     rI   	<genexpr>z,AsyncWebSocket._read_loop.<locals>.<genexpr>S  s'      GG#sg~GGGGGGrH   z.Connection closed unexpectedly by server (EOF)NzSocket closed unexpectedly: rt  r$   r   g?g        zMessage too large: z bytes (limit zF bytes). Consider increasing max_message_size or chunking the message.rH   );r   r  rZ  r  r  r~  timecreate_future
add_readerremove_readerr^  rb  r>   rL   rC   r?   r@   r   r   rF   GOT_NOTHINGr    r   r  rH  rd  rf  r   r  clearr   r"   rk   rj   loweranyrw   rS   rZ   	__cause____suppress_context__r  rU  r   rh   r  r   rv  sleepmaxr   r   r  r  r!  r  OUT_OF_MEMORY_handle_close_framer  ).rr   curl_ws_recvqueue_put_nowait	queue_putr~  	loop_timer  r  r  
time_slice
next_yieldretry_on_errorretry_codesmax_retries
retry_basee_again
e_recv_err	e_nothing
close_flag	cont_flag	ping_flagcontrol_maskmax_msg_sizeblock_on_recverrno_11_msgsqueue_full_errset_fut_resultrecv_error_retriesr"  msg_sizechunks_appendchunks_clearr  r   r   should_retry	final_excread_futurer  r   retry_delayjitterr   ri   nowr  s.                                                @rI   r  zAsyncWebSocket._read_loop  s     . AE	@Q* 	 # 	 +/))-	<@<N*./
-1-? 1
%IKK*4
#}2&*m&9=.!$-"566
&_ ) 4
(4	%
O	O	%
2 2"<*

 S 	 BR #$ 17+1<f	)k ^8I#/<>>LE55  F F F).L v(('+ :--'*1vv||~~GGGGGGGGG 0+/L 9,,5DL'86 6	 /0	+9=	611)<<<# !<IMOO)&Jt}nkRRR"---------  ) # # # 55 .$H3$H$H$-$E!" !"   #FFF  $}22!)(5dm(D(DAA'0 !) !) !)$(D!)	  322222$%AAAAA %)DDDDD# .  $}22!)(5dm(D(DAA'0 !) !) !)$(D!)	  3t}22!)(5dm(D(DAA'0 !) !) !)$(D!)	  3 ! '!Fk11.<<*a/* '!0BQ0F*GH $ )4c(9#ww'?'??%mC[,A,ABBBBBBBBB  --a000FFFFFMFP #[%)))*& , -E

*H,..$11*%<( %< %<.:%< %< %<
 !* 3 
 
 
  "M%((( "I- > >),V)9)9F1IIsxx?O?O   %#$
>,,gu-=>>>>&0 > > >#0 ' $ 9 9$2(6	8O%& %&!" !" !"
 !'"+)We,<"="==========>  )y{{*z99%mA.........%(:%5
 :%  LNNN	8((%8888", 8 8 8, # 55 .~y?V W W   #FF'i777777777778 225999999999F 9$ 
8	8((%8888", 8 8 8, # 55 .~y?V W W   #FF'i777777777778o k ^8 ^8 ^8 ^8 ^8@ % 	 	 	DD  	) 	) 	)%%a(((((((((	)s  W+  D/ -W+ /M?:B"M:W+ "M:0H
J?
J0JJ?	M:I&%M:&
I30M:2I33M:6W+ <W+ W+ JJ?M:J.-M:.
J;8M::J;;M:?K.KK.
K*	'K.)K*	*K..M:1W+ 7A"M:W+ M:4W+ :M??A.W+ /AW+ Q W+ >R(W+ R(%W+ 'R((AW+ ,S: 9W+ :>U8W+ ;UW+ UW+ 1W+ 7V W+ >WW+ WW+ WW+ +X&=	X&X!!X&c                	  K   t           j        t           j        z  }| j        }| j        j        }| j        j        }| j        j        }| j        }|j	        }| j
        } |            |z   }		 | j        s	  |             d{V \  }
}	  ||
|           d{V s*	  |             | j        s|                                  dS dS |t           j        z  r	  |             n |            x}|	k    rt          j        d           d{V  ||z   }	 |             n#  |             w xY w	  |             d{V \  }
}|
|fg}|t           j        z  s{t!          |          | j        k     rc	  |            \  }
}|                    |
|f           |t           j        z  rn.n# t          j        $ r Y nw xY wt!          |          | j        k     c	 i }|D ]\  }
}||z  rZ|                                D ]\  }} |d                    |          |           d{V sK  t-          t!          |                    D ]} |             	 | j        s|                                  dS dS  |            x}|	k    rt          j        d           d{V  ||z   }	|                                  ||
|           d{V sJ t-          t!          |                    D ]} |             	 | j        s|                                  dS dS  |            x}|	k    rt          j        d           d{V  ||z   }	f|                    |g                               |
           |                                D ]t\  }} |d                    |          |           d{V sJ t-          t!          |                    D ]} |             	 | j        s|                                  dS dS u	 t-          t!          |                    D ]} |             n.# t-          t!          |                    D ]} |             w xY w|d         d         t           j        z  rn8n<# t          j        $ r Y n+t4          $ r}|                     |           Y d}~nd}~ww xY w| j        s|                                  dS dS # | j        s|                                  w w xY w)a  
        The background task responsible for consuming the send queue
        and transmitting frames.

        To maximize performance, this loop hoists the configuration
        check and enters one of two distinct processing strategies:

        1. Standard Mode (No Coalescing):
            The default, low-latency path. Messages are consumed one-by-one
            from the queue and transmitted immediately. This guarantees that one
            ``send()`` call results in exactly one WebSocket message, preserving
            logical message boundaries.

        2. Coalescing Mode:
            An optimized throughput path for chatty streams. The loop greedily gathers
            multiple pending messages from the queue (up to ``max_send_batch_size``
            and merges their payloads into a single transmission if they share the
            same flags (e.g., multiple text frames). This reduces system call
            overhead but does not preserve individual message boundaries.

        Features:
        - Cooperative Multitasking: Yields to the event loop periodically to prevent
            the writer from starving the reader task during high-volume transmission.
        - Control Frame Priority: PING and CLOSE frames are never coalesced; they
            trigger an immediate flush of any pending batched data before being sent.
        - Lifecycle Management: Automatically terminates the connection cleanly upon
            transmitting a CLOSE frame.
        TNr   rH   rt  r$   )r    r   rH  _send_payloadr[  r   r  	task_doner~  r  r_  r]  r   r   rv  r  r   r\  r  r  itemsr!  r  r  
setdefaultr  r   r  )rr   control_frame_flagssend_payload	queue_getqueue_get_nowait
queue_doner~  r  r  r  r+  r   r  batchr   data_to_coalesceframe_grouppayloadsr   r   s                       rI   r  zAsyncWebSocket._write_loop  s     : $.#3jo#E7;7I>B>N>R	:>:J:U)-)9)C
*.))-	 1
%IKK*4
]	!( O%+49;;%6%6%6%6%6%6NGU%%1\'5%A%AAAAAAA #" #
P ; !     ! !c !:#33 "! #
 $-9;;.C:==")-"2"22222222),z)9J #


!%(9+49;;%6%6%6%6%6%6NGU ?Fu=M<NE!J$44 	&!%jj4+DDD&1A1A1C1C %gu-= > > >#(:+;#; !*$)!* $+#5 & & & %& "%jj4+DDD%)JL(.3 W WNGU$':: W=M=S=S=U=U 	!F 	!F$9K1=(+(:(:K2& 2& ,& ,& ,& ,& ,& ,& %/ )/6 "'s5zz!2!2 ) )A&JLLLL)  ; !     ! !Q 09y{{(:z'I'I.5mA.>.>(>(>(>(>(>(>(>58:5E
 0 6 6 8 8 8-9\'5-I-I'I'I'I'I'I'I !+$*" "'s5zz!2!2 ) )A&JLLLL)  ; !     ! != ,59;;$6C:#E#E*1-*:*:$:$:$:$:$:$:$:14z1AJ !1 ; ;E2 F F M Mg V V V V 6F5K5K5M5M ' '1K)5chhx6H6H+)V)V#V#V#V#V#V#V ' & "'s5zz!2!2 ) )A&JLLLL)  ; !     ! !+'' "'s5zz!2!2 ) )A&JLLLL)s5zz!2!2 ) )A&JLLLL) Ry|j&66 s9v % 	 	 	D  	) 	) 	)%%a((((((((	)
 ; !     ! !4; !    !s   *Q" D 
Q" D Q" /D Q" D$$AQ" )3F Q" F0-Q" /F00Q" AP )Q" %AP <)Q" BP  )Q" )P +*Q" +Q  !Q" !R= "R1R= 3	R<RR= RR= =SCurlWsFlag | intc                  K   | j         j        }| j        }|j        }|j        }|j        }|j        }t          }	| j        }
| j	        } |            |z   }| j
        }t          j        }t          j        }d}|| z  }t          |          }t!          |          }d}d}||k     s|dk    r)|dk    r"|||t#          ||z
  |          z            }t!          |          }|}||z   |k     r||z  }	  |||          }|dk    r\|dk    rdS |dk    rN|dz  }||k    r3|                     t'          d| dt          j                             dS t+          d|          |rd}||z  } |            x}|k    rt-          j        d           d	{V  ||z   }n(# t*          $ r}|j        |k    r |            }	  ||
|	|           | d	{V  n{# t2          $ rn}|                     t'          d
| t          j                             Y d	}~|
dk    r	  ||
          }n# t2          $ r Y nw xY wY d	}~dS Y d	}~dS Y d	}~dS d	}~ww xY w	 |
dk    r	  ||
          }n9# t2          $ r Y n-w xY wn(# |
dk    r	  ||
          }w # t2          $ r Y w w xY ww xY wY d	}~|                     |           Y d	}~dS d	}~ww xY w||k     |dk    r|dk    "dS )zF
        Optimized low-level sender with fragmentation logic.
        rA   r   Tr$   zWriter stalled (z attempts).Fz0 bytes sentNz)Socket closed unexpectedly during write: rt  )r   r/  r~  r  r  
add_writerremove_writerr   rU  r_  rg  r   r   r    r  r  r   minr  rh   WRITE_ERRORr"   rv  r  rk   r   r  )rr   r+  r   curl_ws_sendr~  r  r  r  r  r  r#  r  r  max_frame_sizer  r  max_zero_writes
base_flagsviewtotal_bytesr0  write_retriesr  	chunk_lencurrent_flagsr2  r  r   write_futurer  r   s                                  rI   r   zAsyncWebSocket._send_payloadr  s8     
 GKiFW*.))-	<@<N*./
-1-?AQ} 1
%IKK*4
"7&_ *	   9*,
%g..t99 {""v{{{a7G7G $#kF&:N"K"KKK!E !ZZI /9M"k11*@*l5-@@Q;; A~~#t !1}}%*(O;; 55 .%R%R%R%R$-$9!" !"   $)5'@@@  &$%M&  %9;;&C:55!-*********!$z!1J   6W$$9FL%"
7NLIII********* % % % %11* QC Q Q ) A     %uuu #b==%$1M'$:$:#, % % % $%	 )===== ! !%% + #b==%$1M'$:$:#, % % % $%	 )7b==%$1M'$:$:#, % % % $%	 ) HHHH ))!,,,uuuuu?[ {""v{{{a7G7G\ ts   /F 	AF AF J<"J78GI.
I0II.J7HJ7
H,)J7+H,,J7II.
J7IJ7
I*'J7)I**J7.J6JJ
J	JJ	JJ7J77J<c                  K   | j         dS t          j        | j                                                  }	 t          j        || j         ht          j        |           d{V \  }}|st          d          ||v rg	 |                                sP|	                                }t          t          j                  5  | d{V  ddd           dS # 1 swxY w Y   dS dS | j         |v r	 | j                                          n"# t          $ r}t          d          |d}~ww xY w| j                                        rg	 |                                sP|	                                }t          t          j                  5  | d{V  ddd           dS # 1 swxY w Y   dS dS t          d          	 |                                sP|	                                }t          t          j                  5  | d{V  ddd           dS # 1 swxY w Y   dS dS # |                                sN|	                                }t          t          j                  5  | d{V  ddd           w # 1 swxY w Y   w w xY w)a  Waits until all items in the send queue have been processed.

        This ensures that all messages passed to `send()` have been handed off to the
        underlying socket for transmission. It does not guarantee that the data has
        been received by the remote peer.

        Args:
            timeout (float | None, optional): The maximum number of seconds to wait
            for the queue to drain.

        Raises:
            WebSocketTimeout:  If the send queue is not fully processed within the
            specified ``timeout`` period.
            WebSocketError: If the writer task has already terminated while unsent
            messages remain in the queue.
        Nr  z*Timed out waiting for send queue to flush.z#Writer task crashed while flushing.z0Writer task stopped unexpectedly while flushing.)rY  rv  r  r[  r!  r  r  rz   r  r  r   r  resultr   rh   empty)rr   r   	join_taskr  r   r  s         rI   r  zAsyncWebSocket.flush  s     " #F )0(;D<L<Q<Q<S<S(T(T	 	$#LD,-#3        GD!  U&'STTT D  " >>## $$$&&g455 $ $#OOOOOOO$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $$ $ 4''Y$++----  Y Y Y()NOOUXXY #))++  >>## $$$&&g455 $ $#OOOOOOO$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $$ $	 %%WXXX ( >>## $$$&&g455 $ $#OOOOOOO$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $$ $9>>## $$$&&g455 $ $#OOOOOOO$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $$s   AH!  	CCC%	H! /D	 H! 	
D(D##D((H! 	FF #F +H! <	HHH!AJ#	I8,J8I<<J?I< Jc                Z  K   d | j         | j        fD             }d}	 |D ]}|                                }|rCt          j        ||t          j                   d{V \  }}|D ]}|                                }| j                                        si	 | j                                        }| j        	                                 n# t          j
        t          f$ r Y nw xY w| j                                        i| j        dk    rt          t                    5  | j                            | j                  }ddd           n# 1 swxY w Y   t          t                    5  | j                            | j                  }ddd           n# 1 swxY w Y   d| _        t%                                                       | j        r&| j        j        s| j                            d           | j                                         dS # | j                                         w xY w)z)Utility method for connection terminationc                >    h | ]}||                                 |S rE   )r  )r  ts     rI   	<setcomp>z3AsyncWebSocket._terminate_helper.<locals>.<setcomp>  s1     4
 4
 4
}QVVXX} }}rH   r   )r   r  Nrt  )rX  rY  r  rv  r  ALL_COMPLETEDr[  r  r  r  r  
ValueErrorrU  r   r   r~  r  r  rp   r   rS  r  r  ra  r  )rr   tasks_to_cancelmax_timeoutio_taskr   pendingprs   s          rI   r  z AsyncWebSocket._terminate_helper  s     4
 4
ot'784
 4
 4

 (	)* % %NN$$  ##*<#' ' 5$ $ $      
7 ! # #A

AA &,,.. (3355A$..0000*J7   E	 &,,..  }""i(( ? ?	//>>A? ? ? ? ? ? ? ? ? ? ? ? ? ? ?i(( ? ?	//>>A? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DM GG| -DL$8 -&&t,,, "&&(((((D"&&((((sy   A7H 2C
 	H 
C# H "C##;H  E
>H 
EH EH ) F	H FH FAH H*c                8  K   	 |                      |          \  | _        | _        n## t          $ r}|j        | _        Y d}~nd}~ww xY w| j        r5| j        s.|                     | j        pt          j	                   d{V  dS | 
                                 dS )z<Unpack and handle the closing frame, then initiate shutdown.N)r   r   r   rh   rk   r   r   r   rS   rU   r   )rr   ri   r   s      rI   r  z"AsyncWebSocket._handle_close_frameJ  s      	&373K3KG3T3T0Dd00 	& 	& 	& vD	& > 	$+ 	**T-?@@@@@@@@@@@ NNs   "' 
AAA)rS  r3   r   r!   r   r=   r   r=   rj  rB   rk  rB   rl  rB   rm  r=   rb  rr  rn  r?   ro  r?   rp  rB   re  r=   rq  r=   rm   rn   )rm   r{  )rm   rB   )rm   r=   )rm   r+   )r  r  r  r  r  r  rm   rn   rO  )r  r   rm   rn   r   )r   r  rm   r  )r   r  rm   rj   )r   r  r   r  rm   r5   )r+  r  r   r    r   r  rm   rn   )r+  r   rm   rn   )r+  rj   rm   rn   )r+  rA  r   rB  rm   rn   )r+  r,  rm   rn   )rk   rB   ri   r   r   r?   rm   rn   )r+  r   r   r  rm   r=   rE   )r   r  rm   rn   )ri   r   rm   rn   )*rM   rN   rO   rP   r   rg  rQ   rq   r   r~  rk  r  r  r  r  r  r  r  r   r'  rP  r*  r    r7  r4  r9  r<  r?  r:   rF  rI  rS   rU   r   r   r  r  r   r  r  r  rt   ru   s   @rI   rR  rR    se         I. +0//// !!#% %26!&!& /$)-!VH VH VH VH VH VH VH VHp    X ( ( ( X(B B B B&   	 	 	 	   
      . 
  
  
  
D 59 i3 i3 i3 i3 i3 i3V 9=      & -7 $	     F '- $	=9 =9 =9 =9 =9~; ; ; ;; ; ; ;9 9 9 9 ?L3 3 3 3 3 3? ? ? ?4 &.3QT*W *W *W *W *WX.1 .1 .1 .1 .1 .1`h) h) h) h)TD! D! D! D!Lj j j jX7$ 7$ 7$ 7$ 7$r1) 1) 1) 1) 1) 1)f       rH   rR  c                  6    e Zd ZdZdZddZdd	ZddZddZdS )AsyncWebSocketContextz.Helper to enable simpler context manager usage_coro_objcoroAwaitable[AsyncWebSocket]rm   rn   c                "    || _         d | _        d S rE   r/  )rr   r2  s     rI   rq   zAsyncWebSocketContext.__init__^  s    04
+/			rH   )Generator[object, object, AsyncWebSocket]c                4    | j                                         S rE   )r0  	__await__r   s    rI   r7  zAsyncWebSocketContext.__await__b  s    z##%%%rH   rR  c                8   K   | j          d {V | _        | j        S rE   r/  r   s    rI   r  z AsyncWebSocketContext.__aenter__e  s)      *$$$$$$	yrH   r  r  r  r  tbr  c                Z   K   | j         r!| j                                          d {V  d S d S rE   )r1  r   )rr   r  r  r9  s       rI   r  zAsyncWebSocketContext.__aexit__i  sF       9 	$)//###########	$ 	$rH   N)r2  r3  rm   rn   )rm   r5  )rm   rR  )r  r  r  r  r9  r  rm   rn   )	rM   rN   rO   rP   r   rq   r7  r  r  rG   rH   rI   r.  r.  X  so        88!I0 0 0 0& & & &   $ $ $ $ $ $rH   r.  )r{   r|   rm   rn   )crP   
__future__r   rv  r   rx  r   r   r   collections.abcr   r   r   
contextlibr   dataclassesr	   r
   enumr   	functoolsr   jsonr   
json_dumpsr   rP  randomr   r   typingr   r   r   r   r   r   aior   r   constr   r   r   r   r    r   r!   r"   utilsr#   
exceptionsr%   r&   modelsr'   r(   r)   r*   typing_extensionsr+   r,   r-   r   r.   r   r/   r   r0   r1   r2   rS  r3   r4   r5   r   	ON_DATA_Trj   ON_MESSAGE_T
ON_ERROR_T	ON_OPEN_TrB   
ON_CLOSE_TtupleRECV_QUEUE_ITEMSEND_QUEUE_ITEMr:   rQ   r<   rS   rh   rw   rz   r   r   r   r6   rR  r.  rG   rH   rI   <module>rS     s3     # " " " " "        ; ; ; ; ; ; : : : : : : : : : :       ( ( ( ( ( ( ( (             $ $ $ $ $ $ $ $ $ $ $ $             I I I I I I I I I I I I I I I I / / / / / / / / H H H H H H H H H H H H H H " " " " " " " " # # # # # # . . . . . . . .       8 8 8 8 8 8 8 8 8 8 /&&&&&&''''''""""""$$$$$$$$$$$$OOOOOOOOOO00000000A+uk:D@AI[%#+6<=L;	2D89J+,-I;S1478JE3J'OE:-.O &gjZHHH H H H H R R R R R R R R,    '   .( ( ( ( (Y ( ( (' ' ' ' 'M' ' ') ) ) ) )G) ) )    E E E E E E E EP FG | | | | | | | |~     ]   D& $ $ $ $ $ $ $ $ $ $rH   