B
    ‹æ@\¢/  ã               @   sÞ   d Z ddlmZmZmZmZ ddlZe e¡Z	ddl
Z
ddlmZ ddlmZ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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Z$G dd„ deƒZ%dZ&dS )zA Provide a web socket handler for the Bokeh Server application.

é    )Úabsolute_importÚdivisionÚprint_functionÚunicode_literalsN)Úurlparse)ÚgenÚlocks)ÚStreamClosedErrorÚWebSocketHandlerÚWebSocketClosedErroré   )ÚProtocolHandleré   )ÚProtocol)ÚMessageErrorÚProtocolErrorÚValidationError)ÚMessage)ÚReceiver)Úcheck_session_id_signature)Úsettings)Ú	WSHandlerc                   sÀ   e Zd ZdZ‡ fdd„Zdd„ Zdd„ Zdd	„ Zej	d
d„ ƒZ
ej	dd„ ƒZdd„ Zej	dd„ ƒZej	d"‡ fdd„	ƒZdd„ Zej	dd„ ƒZej	dd„ ƒZej	dd„ ƒZdd„ Zd d!„ Z‡  ZS )#r   zI Implements a custom Tornado WebSocketHandler for the Bokeh Server.

    c                sH   d | _ d | _d | _|d | _d| _t ¡ | _tt	| ƒj
|f|ž|Ž d S )NÚapplication_contextéÿÿÿÿ)ÚreceiverÚhandlerÚ
connectionr   Úlatest_pongr   ZLockÚ
write_lockÚsuperr   Ú__init__)ÚselfZtornado_appÚargsÚkw)Ú	__class__© ú4lib/python3.7/site-packages/bokeh/server/views/ws.pyr    ?   s    

zWSHandler.__init__c             C   s   d S )Nr%   )r!   r   Zbokeh_websocket_pathr%   r%   r&   Ú
initializeL   s    zWSHandler.initializec             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 suplied origin will be compared to the Bokeh server whitelist. 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_whitelistTz³Refusing 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   ZnetlocÚlowerÚapplicationZwebsocket_originsr   Zallowed_ws_originÚsetÚlogÚerror)r!   Úoriginr(   Zparsed_originZorigin_hostZallowed_hostsZallowedr%   r%   r&   Úcheck_originO   s    

zWSHandler.check_originc             C   s¨   t  d¡ | jddd}|dkr0|  ¡  tdƒ‚| jddd}|dkrV|  ¡  tdƒ‚t|| jj| jjds€t  	d	|¡ td
ƒ‚dd„ }|  
||¡}| jj ||¡ dS )zR Initialize a connection to a client.

        Returns:
            None

        zWebSocket connection openedzbokeh-protocol-versionN)Údefaultz#No bokeh-protocol-version specifiedzbokeh-session-idzNo bokeh-session-id specified)ÚsignedÚ
secret_keyz$Session id had invalid signature: %rzInvalid session IDc             S   s    |   ¡ }|d k	rt d|¡ d S )Nz"Failed to fully open connection %r)Z	exceptionr-   Údebug)ÚfutureÚer%   r%   r&   Úon_fully_opened‡   s    z'WSHandler.open.<locals>.on_fully_opened)r-   ÚinfoZget_argumentÚcloser   r   r+   Zsign_sessionsr3   r.   Ú_async_openZio_loopZ
add_future)r!   Úproto_versionÚ
session_idr7   r5   r%   r%   r&   Úopenn   s"    
zWSHandler.openc          
   c   sØ   yt| j  || j¡V  | j  |¡}t|ƒ}t|ƒ| _t d|¡ t	ƒ | _
t d|¡ | j || | j |¡| _t d¡ W n: tk
r® } zt d|¡ |  ¡  |‚W dd}~X Y nX | jj d¡}|  |¡V  t d¡‚dS )a‰   Perform the specific steps needed to open a connection to a Bokeh session

        Sepcifically, this method coordinates:

        * Getting a session for a session ID (creating a new one if needed)
        * Creating a protocol receiver and hander
        * 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

            proto_version (str):
                The protocol version requested by the connecting client.

        Returns:
            None

        zReceiver created for %rzProtocolHandler created for %rzServerConnection createdz/Could not create new server session, reason: %sNZACK)r   Zcreate_session_if_neededZrequestZget_sessionr   r   r   r-   r4   r   r   r+   Znew_connectionr   r8   r   r.   r9   ÚprotocolZcreateÚsend_messager   ÚReturn)r!   r<   r;   Zsessionr>   r6   Úmsgr%   r%   r&   r:   ’   s"    
zWSHandler._async_openc          
   c   sÖ   y|   |¡V }W n> tk
rN } z tjd||dd |  d¡ W dd}~X Y nX y8|r†tdk	rjtj |¡ |  |¡V }|r†|  	|¡V  W n> tk
rÆ } z tjd||dd |  d¡ W dd}~X Y nX t
 d¡‚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

        z/Unhandled exception receiving a message: %r: %rT)Úexc_infoz server failed to parse a messageNz.Handler or its work threw an exception: %r: %rz!server failed to handle a message)Ú_receiveÚ	Exceptionr-   r.   Ú_internal_errorÚ_message_test_portZreceivedÚappendÚ_handleÚ	_scheduler   r@   )r!   ÚfragmentÚmessager6   Úworkr%   r%   r&   Ú
on_messageÁ   s     zWSHandler.on_messagec             C   sb   yt t |d¡ƒ| _W nF tk
r:   tjd|dd Y n$ tk
r\   tjd|dd Y nX d S )Nzutf-8z#received invalid unicode in pong %rT)rB   z#received invalid integer in pong %r)ÚintÚcodecsÚdecoder   ÚUnicodeDecodeErrorr-   ZtraceÚ
ValueError)r!   Údatar%   r%   r&   Úon_pongè   s    zWSHandler.on_pongc          	   c   sV   y$t dk	rt j |¡ | | ¡V  W n" ttfk
rF   t d¡ Y nX t 	d¡‚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)
rF   ZsentrG   Úsendr   r	   r-   Zwarningr   r@   )r!   rK   r%   r%   r&   r?   ò   s    zWSHandler.send_messageFTc          	   #   sJ   |r2| j  ¡ V  tt| ƒ ||¡V  W dQ R X ntt| ƒ ||¡V  dS )zj Override parent write_message with a version that acquires a
        write lock before writing.

        N)r   Úacquirer   r   Úwrite_message)r!   rK   ZbinaryÚlocked)r$   r%   r&   rW     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)r-   r8   Z
close_codeZclose_reasonr   r+   Zclient_lost)r!   r%   r%   r&   Úon_close  s    
zWSHandler.on_closec          
   c   sb   y| j  |¡V }t |¡‚W n@ tttfk
r\ } z|  t|ƒ¡ t d ¡‚W d d }~X Y nX d S )N)	r   Zconsumer   r@   r   r   r   Ú_protocol_errorÚstr)r!   rJ   rK   r6   r%   r%   r&   rC     s    zWSHandler._receivec          
   c   sf   y | j  || j¡V }t |¡‚W n@ tttfk
r` } z|  t	|ƒ¡ t d ¡‚W d d }~X Y nX d S )N)
r   Zhandler   r   r@   r   r   r   rE   r[   )r!   rK   rL   r6   r%   r%   r&   rH   !  s    zWSHandler._handlec             c   s8   t |tƒr|  |¡V  n|  dt|ƒ ¡ t d ¡‚d S )Nzexpected a Message not )Ú
isinstancer   r?   rE   Úreprr   r@   )r!   rL   r%   r%   r&   rI   +  s    
zWSHandler._schedulec             C   s   t  d|¡ |  d|¡ d S )Nz3Bokeh Server internal error: %s, closing connectioni'  )r-   r.   r9   )r!   rK   r%   r%   r&   rE   4  s    zWSHandler._internal_errorc             C   s   t  d|¡ |  d|¡ d S )Nz3Bokeh Server protocol error: %s, closing connectioni'  )r-   r.   r9   )r!   rK   r%   r%   r&   rZ   8  s    zWSHandler._protocol_error)FT)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r    r'   r0   r=   r   Ú	coroutiner:   rM   rT   r?   rW   rY   rC   rH   rI   rE   rZ   Ú__classcell__r%   r%   )r$   r&   r   ;   s"   $/'


	r   )'ra   Z
__future__r   r   r   r   ZloggingZ	getLoggerr^   r-   rO   Zsix.moves.urllib.parser   Ztornador   r   Ztornado.websocketr	   r
   r   Zprotocol_handlerr   r>   r   Zprotocol.exceptionsr   r   r   Zprotocol.messager   Zprotocol.receiverr   Zbokeh.util.session_idr   Zbokeh.settingsr   Ú__all__r   rF   r%   r%   r%   r&   Ú<module>	   s&   
  	