B
    3\(                 @   s  d Z ddlZddlZddlZddlZddlZyddlmZ W n  ek
r\   ddlmZ Y nX ddl	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mZ dd	lmZ d
dlmZ dd Zdd ZdZG dd deZG dd deeZ G dd de eZ!dS )z/Tornado handlers for WebSocket <-> ZMQ sockets.    N)urlparse)genioloopweb)StreamClosedError)WebSocketHandlerWebSocketClosedError)Session)date_defaultextract_dates)cast_unicode   )IPythonHandlerc             C   s   |   } t| d}tjdk r.dd |D }tj| tdd}|	d| t
|}d|d	  g}x*|d
d D ]}||d t
|  qrW tjdd|d	   |f| }|	d| d|S )a2  serialize a message as a binary blob

    Header:

    4 bytes: number of msg parts (nbufs) as 32b int
    4 * nbufs bytes: offset for each buffer as integer as 32b int

    Offsets are from the start of the buffer, including the header.

    Returns
    -------

    The message serialized to bytes.

    buffers)      c             S   s   g | ]}|  qS  )tobytes).0xr   r   8lib/python3.7/site-packages/notebook/base/zmqhandlers.py
<listcomp>1   s    z,serialize_binary_message.<locals>.<listcomp>)defaultutf8r   r   r   N!I    )copylistpopsysversion_infojsondumpsr
   encodeinsertlenappendstructZpackjoin)msgr   bmsgnbufsoffsetsbufZoffsets_bufr   r   r   serialize_binary_message   s    
r0   c          	   C   s   t d| dd d }tt dd|  | dd|d   }|d g }x8t|dd |dd D ]\}}|| ||  qjW t|d d	}t|d
 |d
< t|d |d< |dd |d< |S )a'  deserialize a message from a binary blog

    Header:

    4 bytes: number of msg parts (nbufs) as 32b int
    4 * nbufs bytes: offset for each buffer as integer as 32b int

    Offsets are from the start of the buffer, including the header.

    Returns
    -------

    message dictionary
    z!iNr   r   r   r   r   r   r   headerZparent_headerr   )	r)   Zunpackr   r(   zipr#   loadsdecoder   )r,   r-   r.   Zbufsstartstopr+   r   r   r   deserialize_binary_message=   s    (
$r7   i0u  c                   sj   e Zd ZdZdZdZdZdZedd Z	edd Z
ddd	Zd
d Z fddZdd Zdd Z  ZS )WebSocketMixinz"Mixin for common websocket optionsNr   c             C   s   | j dtS )zqThe interval for websocket keep-alive pings.
        
        Set ws_ping_interval = 0 to disable pings.
        Zws_ping_interval)settingsgetWS_PING_INTERVAL)selfr   r   r   ping_intervalc   s    zWebSocketMixin.ping_intervalc             C   s   | j dtd| j tS )zIf no ping is received in this many milliseconds,
        close the websocket connection (VPNs, etc. can fail to cleanly close ws connections).
        Default is max of 3 pings or 30 seconds.
        Zws_ping_timeoutr   )r9   r:   maxr=   r;   )r<   r   r   r   ping_timeoutk   s    zWebSocketMixin.ping_timeoutc             C   s   | j dkst| dr |  r dS | jjd}|dkr>|  }|dksN|dkrRdS | }t|j	}||krpdS | j r| j |k}n| j
rt| j
|}nd}|s| jd|| |S )zCheck Origin == Host or Access-Control-Allow-Origin.
        
        Tornado >= 4 calls this method automatically, raising 403 if it returns False.
        *skip_check_originTZHostNFz>Blocking Cross Origin WebSocket Attempt.  Origin: %s, Host: %s)Zallow_originhasattrrA   requestZheadersr:   Z
get_originlowerr   ZnetlocZallow_origin_patboolmatchlogwarning)r<   originZhostZorigin_hostZallowr   r   r   check_originu   s*    

zWebSocketMixin.check_originc             O   s   dS )zmeaningless for websocketsNr   )r<   argskwargsr   r   r   clear_cookie   s    zWebSocketMixin.clear_cookiec                sf   | j d| jj | jdkrTtj }| | _	| j	| _
t| j| j| _| j  tt| j||S )NzOpening websocket %sr   )rG   debugrC   pathr=   r   IOLoopcurrenttime	last_ping	last_pongZPeriodicCallback	send_pingping_callbackr5   superr8   open)r<   rK   rL   Zloop)	__class__r   r   rX      s    



zWebSocketMixin.openc          	   C   s   | j dkr"| jdk	r"| j  dS tj  }d|| j  }d|| j  }|d| j	 k r~|| j
kr~| jd| |   dS y| d W n" ttfk
r   | j  dS X || _dS )z'send a ping to keep the websocket aliveNg     @@   z#WebSocket ping timeout after %i ms.r   )ws_connectionrV   r6   r   rP   rQ   rR   rT   rS   r=   r?   rG   rH   closeZpingr   r   )r<   ZnowZsince_last_pongZsince_last_pingr   r   r   rU      s     

zWebSocketMixin.send_pingc             C   s   t j  | _d S )N)r   rP   rQ   rR   rT   )r<   datar   r   r   on_pong   s    zWebSocketMixin.on_pong)N)__name__
__module____qualname____doc__rV   rS   rT   streampropertyr=   r?   rJ   rM   rX   rU   r^   __classcell__r   r   )rY   r   r8   \   s   

'r8   c                   s8   e Zd Zejdk r fddZd	ddZdd Z  ZS )
ZMQStreamHandler)r   r   c                s,   | j d krtt| j|| n
| j   d S )N)rc   rW   r   
send_errorr\   )r<   rK   rL   )rY   r   r   rg      s    
zZMQStreamHandler.send_errorNc             C   sf   t |tr|}n| j|\}}| j|}|r8||d< |d rLt|}|S tj|td}t	|S dS )a  Reserialize a reply message using JSON.

        msg_or_list can be an already-deserialized msg dict or the zmq buffer list.
        If it is the zmq list, it will be deserialized with self.session.
        
        This takes the msg list from the ZMQ socket and serializes the result for the websocket.
        This method should be used by self._on_zmq_reply to build messages that can
        be sent back to the browser.
        
        channelr   )r   N)

isinstancedictsessionZfeed_identitiesZdeserializer0   r#   r$   r
   r   )r<   Zmsg_or_listrh   r+   Zidentsmsg_listr/   Zsmsgr   r   r   _reserialize_reply   s    
z#ZMQStreamHandler._reserialize_replyc          	   C   s   | j d ks| r*| jd |   d S t|dd }y| j||d}W n( tk
rp   | jjd| dd d S X y| j	|t
|td W n, ttfk
r   | jd |   d S X d S )Nz%zmq message arrived on closed channelrh   )rh   zMalformed message: %rT)exc_info)Zbinary)r[   closedrG   rH   r\   getattrrm   	ExceptionZcriticalZwrite_messageri   bytesr   r   )r<   rc   rl   rh   r+   r   r   r   _on_zmq_reply   s     zZMQStreamHandler._on_zmq_reply)N)	r_   r`   ra   tornador"   rg   rm   rs   re   r   r   )rY   r   rf      s   

rf   c                   sB   e Zd Zdd Zdd Zej fddZdd Zd	d
 Z	  Z
S )AuthenticatedZMQStreamHandlerc             C   s   dS )zpUndo the set_default_headers in IPythonHandler
        
        which doesn't make sense for websockets
        Nr   )r<   r   r   r   set_default_headers  s    z1AuthenticatedZMQStreamHandler.set_default_headersc             C   sR   |   dkr"| jd td| ddrBt| d| j_n| jd dS )zRun before finishing the GET request
        
        Extend this method to add logic that should fire before
        the websocket finishes completing.
        Nz*Couldn't authenticate WebSocket connectioni  Z
session_idFzNo session ID specified)Zget_current_userrG   rH   r   Z	HTTPErrorZget_argumentr   rk   )r<   r   r   r   pre_get  s    
z%AuthenticatedZMQStreamHandler.pre_getc             /   s6   |   }t|V  tt| j||}t|V  d S )N)rw   r   Zmaybe_futurerW   ru   r:   )r<   rK   rL   Zres)rY   r   r   r:   "  s    z!AuthenticatedZMQStreamHandler.getc             C   s$   | j d| jj t| jd| _d S )Nz$Initializing websocket connection %s)config)rG   rN   rC   rO   r	   rx   rk   )r<   r   r   r   
initialize+  s    z(AuthenticatedZMQStreamHandler.initializec             C   s   | j dd S )NZwebsocket_compression_options)r9   r:   )r<   r   r   r   get_compression_options/  s    z5AuthenticatedZMQStreamHandler.get_compression_options)r_   r`   ra   rv   rw   r   	coroutiner:   ry   rz   re   r   r   )rY   r   ru   	  s
   	ru   )"rb   osr#   r)   warningsr!   Zurllib.parser   ImportErrorrt   r   r   r   Ztornado.iostreamr   Ztornado.websocketr   r   Zjupyter_client.sessionr	   Zjupyter_client.jsonutilr
   r   Zipython_genutils.py3compatr   Zhandlersr   r0   r7   r;   objectr8   rf   ru   r   r   r   r   <module>   s.    n?