U
    /ef4                     @  s0  U d Z ddlmZ ddlZeeZddlZddlZ	ddl
mZmZmZmZmZ ddlmZ ddlmZmZ ddlmZm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"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Z.G dd de-eZ/e)G dd dZ0dZ1de2d< dS )zA Provide a web socket handler for the Bokeh Server application.

    )annotationsN)AnyDictListUnioncast)urlparse)locksweb)WebSocketClosedErrorWebSocketHandler)settings)check_token_signatureget_session_idget_token_payload   )Protocol)MessageErrorProtocolErrorValidationError)Message)Receiver)	dataclass   )ProtocolHandler   )	AuthMixin)	WSHandlerc                      s(  e Zd ZdZdd fddZdd Zdd	d
ddZejddddZ	dddddZ
ddddZdddddZdddddZdddd d!Zd"dd#d$d%Zd<d(d	d	dd) fd*d+Zddd,d-Zdd.dd/d0Zd"d1d#d2d3Zd4dd5d6d7Zddd#d8d9Zddd#d:d;Z  ZS )=r   zI Implements a custom Tornado WebSocketHandler for the Bokeh Server.

    None)returnc                   sf   d | _ d | _d | _|d | _d| _t | _d | _|	dd | _
|	dd | _t j|f|| d S )Napplication_contextcompression_level	mem_level)receiverhandler
connectionr    latest_pongr	   Lock
write_lock_tokenpop_compression_level
_mem_levelsuper__init__)selfZtornado_appargskw	__class__ 9/tmp/pip-unpacked-wheel-f5fndrjf/bokeh/server/views/ws.pyr/   H   s    

zWSHandler.__init__c                 C  s   d S Nr5   )r0   r    Zbokeh_websocket_pathr5   r5   r6   
initializeZ   s    zWSHandler.initializestrbool)originr   c                 C  sf   ddl m} t|}|j }| jj}t r:t	t }|||}|rLdS t
d|||| dS dS )a   Implement a check_origin policy for Tornado to call.

        The supplied origin will be compared to the Bokeh server allowlist. If the
        origin is not allow, an error will be logged and ``False`` will be returned.

        Args:
            origin (str) :
                The URL of the connection origin

        Returns:
            bool, True if the connection is allowed, False otherwise

        r   )check_allowlistTzRefusing websocket connection from Origin '%s';                       use --allow-websocket-origin=%s or set BOKEH_ALLOW_WS_ORIGIN=%s to permit this; currently we allow origins %rFN)utilr<   r   netloclowerapplicationZwebsocket_originsr   Zallowed_ws_originsetlogerror)r0   r;   r<   Zparsed_originorigin_hostZallowed_hostsallowedr5   r5   r6   check_origin]   s     

   zWSHandler.check_originc              
   C  s  t d | j}| jdkr,|   tdn|dkrD|   tdttj	
  }t|}d|krz|   tdnP||d kr|   tdn2t|| jj| jjd	st|}t d
| tdz| jj| j| j W n0 tk
r } zt d| W 5 d}~X Y nX dS )zR Initialize a connection to a client.

        Returns:
            None

        zWebSocket connection openedZbokehz!Subprotocol header is not 'bokeh'Nz'No token received in subprotocol headerZsession_expiryz$Session expiry has not been providedzToken is expired.)signed
secret_keyz*Token for session %r had invalid signaturezInvalid token signaturez"Failed to fully open connection %r)rB   infor*   Zselected_subprotocolcloser   calendartimegmdtdatetimeutcnowutctimetupler   r   r@   Zsign_sessionsrH   r   rC   Zio_loopZadd_callback_async_open	Exceptiondebug)r0   tokennowpayload
session_ider5   r5   r6   open|   s6    




zWSHandler.openz	List[str]z
str | None)subprotocolsr   c                 C  s8   t d t d| t|dks&d S |d | _|d S )NzSubprotocol header receivedz Supplied subprotocol headers: %rr   r   r   )rB   rS   tracelenr*   )r0   rZ   r5   r5   r6   select_subprotocol   s    

zWSHandler.select_subprotocolzDict[str, Any] | Nonec                 C  s0   | j d krd S d| j i}| jd k	r,| j|d< |S )Nr"   r#   )r,   r-   )r0   optionsr5   r5   r6   get_compression_options   s    



z!WSHandler.get_compression_options)rT   r   c              
     s   zt |}| j|| j|I dH  | j|}t }t|| _t	d| t
 | _t	d| | j|| | j|| _td W n: tk
r } ztd| |   |W 5 d}~X Y nX | jjd}| |I dH  dS )a   Perform the specific steps needed to open a connection to a Bokeh session

        Specifically, this method coordinates:

        * Getting a session for a session ID (creating a new one if needed)
        * Creating a protocol receiver and handler
        * Opening a new ServerConnection and sending it an ACK

        Args:
            session_id (str) :
                A session ID to for a session to connect to

                If no session exists with the given ID, a new session is made

        Returns:
            None

        NzReceiver created for %rzProtocolHandler created for %rzServerConnection createdz/Could not create new server session, reason: %sZACK)r   r    Zcreate_session_if_neededrequestZget_sessionr   r   r$   rB   rS   r   r%   r@   Znew_connectionr&   rI   r   rC   rJ   protocolcreatesend_message)r0   rT   rW   sessionra   rX   msgr5   r5   r6   rQ      s$    
zWSHandler._async_openzstr | bytes)fragmentr   c              
     s   z|  |I dH }W nB tk
rV } z$tjd||dd | d d}W 5 d}~X Y nX z@|rtdk	rrtj| | |I dH }|r| 	|I dH  W n> tk
r } z tjd||dd | d W 5 d}~X Y nX dS )a   Process an individual wire protocol fragment.

        The websocket RFC specifies opcodes for distinguishing text frames
        from binary frames. Tornado passes us either a text or binary string
        depending on that opcode, we have to look at the type of the fragment
        to see what we got.

        Args:
            fragment (unicode or bytes) : wire fragment to process

        Nz/Unhandled exception receiving a message: %r: %rTexc_infoz server failed to parse a messagez.Handler or its work threw an exception: %r: %rz!server failed to handle a message)
_receiverR   rB   rC   _internal_error_message_test_portreceivedappend_handle	_schedule)r0   rf   messagerX   workr5   r5   r6   
on_message   s"    
zWSHandler.on_messagebytes)datar   c                 C  s`   zt |d| _W nF tk
r8   tjd|dd Y n$ tk
rZ   tjd|dd Y nX d S )Nzutf-8z#received invalid unicode in pong %rTrg   z#received invalid integer in pong %r)intdecoder'   UnicodeDecodeErrorrB   r[   
ValueError)r0   rt   r5   r5   r6   on_pong  s    zWSHandler.on_pongzMessage[Any])rp   r   c                   sL   z(t dk	rt j| || I dH  W n tk
rF   td Y nX dS )z Send a Bokeh Server protocol message to the connected client.

        Args:
            message (Message) : a message to send

        Nz/Failed sending message as connection was closed)rk   sentrm   sendr   rB   warningr0   rp   r5   r5   r6   rc     s    zWSHandler.send_messageFTz!Union[bytes, str, Dict[str, Any]])rp   binarylockedr   c              	     sN   |r6| j  I dH  t ||I dH  W 5 Q R X nt ||I dH  dS )zj Override parent write_message with a version that acquires a
        write lock before writing.

        N)r)   acquirer.   write_message)r0   rp   r~   r   r3   r5   r6   r   "  s     zWSHandler.write_messagec                 C  s.   t d| j| j | jdk	r*| j| j dS )z2 Clean up when the connection is closed.

        z/WebSocket connection closed: code=%s, reason=%rN)rB   rI   Z
close_codeZclose_reasonr&   r@   Zclient_lost)r0   r5   r5   r6   on_close.  s    
zWSHandler.on_closezMessage[Any] | Nonec              
     sZ   z| j |I d H }|W S  tttfk
rT } z| t| W Y d S d }~X Y nX d S r7   )r$   consumer   r   r   _protocol_errorr9   )r0   rf   rp   rX   r5   r5   r6   ri   6  s    zWSHandler._receivez
Any | Nonec              
     s^   z| j || jI d H }|W S  tttfk
rX } z| t| W Y d S d }~X Y nX d S r7   )r%   handler&   r   r   r   rj   r9   )r0   rp   rq   rX   r5   r5   r6   rn   ?  s    zWSHandler._handler   )rq   r   c                   s:   t |tr&| ttt |I d H  n| d| d S )Nzexpected a Message not )
isinstancer   rc   r   r   rj   )r0   rq   r5   r5   r6   ro   H  s    
zWSHandler._schedulec                 C  s   t d| | d| d S )Nz3Bokeh Server internal error: %s, closing connectioni'  rB   rC   rJ   r}   r5   r5   r6   rj   P  s    zWSHandler._internal_errorc                 C  s   t d| | d| d S )Nz3Bokeh Server protocol error: %s, closing connectioni'  r   r}   r5   r5   r6   r   T  s    zWSHandler._protocol_error)FT)__name__
__module____qualname____doc__r/   r8   rF   r
   authenticatedrY   r]   r_   rQ   rr   ry   rc   r   r   ri   rn   ro   rj   r   __classcell__r5   r5   r3   r6   r   D   s*   (,'
   		r   c                   @  s   e Zd ZU ded< ded< dS )MessageTestPortzList[Message[Any]]rz   rl   N)r   r   r   __annotations__r5   r5   r5   r6   r   _  s   
r   zMessageTestPort | Nonerk   )3r   
__future__r   logging	getLoggerr   rB   rK   rN   rM   typingr   r   r   r   r   urllib.parser   Ztornador	   r
   Ztornado.websocketr   r   Zbokeh.settingsr   Zbokeh.util.tokenr   r   r   ra   r   Zprotocol.exceptionsr   r   r   Zprotocol.messager   Zprotocol.receiverr   Zutil.dataclassesr   Zprotocol_handlerr   Z
auth_mixinr   __all__r   r   rk   r   r5   r5   r5   r6   <module>   s2   
  