î
Z:†S&  ã               @   s&  d  Z  d d l 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
 m Z d d l m Z m Z d d l m Z m Z Gd d „  d e j ƒ Z Gd	 d
 „  d
 e j ƒ Z d d d „ Z e d k r"Gd d „  d e ƒ Z e d d d ƒ Z e j ƒ  n  d S)a   yoton.clientserver.py

Yoton comes with a small framework to setup a request-reply pattern
using a client-server model (over a non-persistent connection), 
similar to telnet. This allows one process to easily ask small pieces 
of information from another process.

To create a server, create a class that inherits from 
yoton.RequestServer and implement its handle_request() method.

A client process can simply use the yoton.do_request function. 
Example: ``yoton.do_request('www.google.com:80', 'GET http/1.1\r\n')``

The client server model is implemented using one function and one class:
yoton.do_request and yoton.RequestServer.

Details
-------

The server implements a request/reply pattern by listening at a socket. 
Similar to telnet, each request is handled using a connection 
and the socket is closed after the response is send. 

The request server can setup to run in the main thread, or can be started 
using its own thread. In the latter case, one can easily create multiple
servers in a single process, that listen on different ports.

é    N)Úselect)Ú
basestringÚbytesÚstrÚlong)Úsplit_addressÚgetErrorMsg)Úsend_allÚrecv_allc               @   sp   e  Z d  Z d Z d d d d „ Z d d „  Z d d	 „  Z d
 d „  Z d d „  Z d d „  Z	 d d „  Z
 d S)ÚRequestServera^   RequestServer(address, async=False, verbose=0)
    
    Setup a simple server that handles requests similar to a telnet server, 
    or asyncore. Starting the server using run() will run the server in
    the calling thread. Starting the server using start() will run the
    server in a separate thread.
    
    To create a server, subclass this class and re-implement the 
    handle_request method. It accepts a request and should return a 
    reply. This server assumes utf-8 encoded messages.
    
    Parameters
    ----------
    address : str
        Should be of the shape hostname:port. 
    async : bool
        If True, handles each incoming connection in a separate thread.
        This might be advantageous if a the handle_request() method 
        takes a long time to execute.
    verbose : bool
        If True, print a message each time a connection is accepted.
    
    Notes on hostname
    -----------------
    The hostname can be:
      * The IP address, or the string hostname of this computer. 
      * 'localhost': the connections is only visible from this computer. 
        Also some low level networking layers are bypassed, which results
        in a faster connection. The other context should also connect to
        'localhost'.
      * 'publichost': the connection is visible by other computers on the 
        same network. 
    
    Fr   c             C   sž   t  j j |  ƒ | |  _ | |  _ t | ƒ \ } } } t j t j t j ƒ } | j	 t j
 t j d ƒ | j | | f ƒ | |  _ d |  _ |  j d ƒ d  S)Né   FT)Ú	threadingÚThreadÚ__init__Ú_asyncÚ_verboser   ÚsocketÚAF_INETÚSOCK_STREAMÚ
setsockoptÚ
SOL_SOCKETÚSO_REUSEADDRÚbindÚ_bsd_socketÚ_stop_meÚ	setDaemon)ÚselfÚaddressZasyncÚverboseÚprotocolÚhostÚportÚs© r#   úM/Applications/pyzo2014a/lib/python3.4/site-packages/iep/yoton/clientserver.pyr   R   s    				zRequestServer.__init__c             C   s   d |  _  t j j |  ƒ d S)z@ start()
        Start the server in a separate thread.
        FN)r   r   r   Ústart)r   r#   r#   r$   r%   q   s    	zRequestServer.startc             C   s   d |  _  d S)z) stop()
        Stop the server.
        TN)r   )r   r#   r#   r$   Ústopy   s    zRequestServer.stopc             C   s)  |  j  } | j ƒ  \ } } |  j r; t d | | f ƒ n  | j d ƒ |  j  j d ƒ x¡ |  j sû y |  j  j ƒ  \ } } Wn& t j	 k
 r” Yq[ t
 k
 r¥ Yq[ X|  j rÆ t d t | ƒ ƒ n  |  j rë t |  | ƒ } | j ƒ  q[ |  j | ƒ q[ Wy |  j  j ƒ  Wn t j k
 r$Yn Xd S)z0 run()
        The server's main loop. 
        zYoton: hosting at %s, port %ir   g      Ð?zhandling request from: N)r   Úgetsocknamer   ÚprintÚlistenÚ
settimeoutr   Úacceptr   ÚtimeoutÚInterruptedErrorr   r   ÚSocketHandlerr%   Ú_handle_connectionÚcloseÚerror)r   r"   Úhostnamer!   ÚaddrZrhr#   r#   r$   Úrun€   s.    				zRequestServer.runc             C   sA   y |  j  | ƒ Wn) t k
 r< t d ƒ t t ƒ  ƒ Yn Xd S)zG _handle_connection(s)
        Handle an incoming connection. 
        zError handling request:N)Ú_really_handle_connectionÚ	Exceptionr(   r   )r   r"   r#   r#   r$   r/   ­   s
    
z RequestServer._handle_connectionc             C   s†   t  | d ƒ } | d k r d S|  j | ƒ } t | t ƒ sL t d ƒ ‚ n  t | | d ƒ y | j ƒ  Wn t j k
 r� Yn Xd S)zT _really_handle_connection(s)
        Really handle an incoming connection.
        TNz(handle_request() should return a string.)	r
   Úhandle_requestÚ
isinstancer   Ú
ValueErrorr	   r0   r   r1   )r   r"   ÚrequestÚreplyr#   r#   r$   r5   ¸   s    z'RequestServer._really_handle_connectionc             C   s<   | d k r t  j d ƒ n | d k r4 d |  _ n  d | S)a)   handle_request(request)
        
        Return a reply, given the request. Overload this method to create
        a server.
        
        De standard implementation echos the request, waits one second 
        when receiving 'wait' and stop the server when receiving 'stop'.
        
        Úwaitg      ð?r&   TzRequested: )ÚtimeÚsleepr   )r   r:   r#   r#   r$   r7   Ñ   s
    zRequestServer.handle_requestN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r%   r&   r4   r/   r5   r7   r#   r#   r#   r$   r   .   s   "-r   c               @   s.   e  Z d  Z d Z d d „  Z d d „  Z d S)r.   zL SocketHandler(server, s)
    Simple thread that handles a connection. 
    c             C   s&   t  j j |  ƒ | |  _ | |  _ d  S)N)r   r   r   Ú_serverr   )r   Zserverr"   r#   r#   r$   r   é   s    	zSocketHandler.__init__c             C   s   |  j  j |  j ƒ d  S)N)rC   r/   r   )r   r#   r#   r$   r4   î   s    zSocketHandler.runN)r?   r@   rA   rB   r   r4   r#   r#   r#   r$   r.   å   s   r.   r   c             C   sä   t  |  ƒ \ } } } t | t ƒ s3 t d ƒ ‚ n  | d k rH d } n  t j t j t j ƒ } y | j | | f ƒ Wn! t j k
 rš t	 d ƒ ‚ Yn Xt
 | | d ƒ t | | ƒ } y | j ƒ  Wn t j k
 rß Yn X| S)aK   do_request(address, request, timeout=-1)
    
    Do a request at the server at the specified address. The server can
    be a yoton.RequestServer, or any other server listening on a socket
    and following a REQ/REP pattern, such as html or telnet. For example:
    ``html = do_request('www.google.com:80', 'GET http/1.1\r\n')``
    
    Parameters
    ----------
    address : str
        Should be of the shape hostname:port. 
    request : string
        The request to make.
    timeout : float
        If larger than 0, will wait that many seconds for the respons, and
        return None if timed out.
    
    Notes on hostname
    -----------------
    The hostname can be:
      * The IP address, or the string hostname of this computer. 
      * 'localhost': the connections is only visible from this computer. 
        Also some low level networking layers are bypassed, which results
        in a faster connection. The other context should also connect to
        'localhost'.
      * 'publichost': the connection is visible by other computers on the 
        same network.
    
    zrequest should be a string.Nr   z)No server is listening at the given port.Téÿÿÿÿ)r   r8   r   r9   r   r   r   Úconnectr1   ÚRuntimeErrorr	   r
   r0   )r   r:   r,   r   r    r!   r"   r;   r#   r#   r$   Ú
do_requestó   s"     	rG   Ú__main__c               @   s   e  Z d  Z d d „  Z d S)ÚLalac             C   s!   t  d t | ƒ ƒ d t j ƒ  S)NzREQ:zThe current time is %i)r(   Úreprr=   )r   Zreqr#   r#   r$   r7   7  s    zLala.handle_requestN)r?   r@   rA   r7   r#   r#   r#   r$   rI   6  s   rI   zlocalhost:testrD   )rB   ÚosÚsysr=   r   r   r   Ú
yoton.miscr   r   r   r   r   r   Ú
yoton.corer	   r
   r   r   r.   rG   r?   rI   r"   r%   r#   r#   r#   r$   Ú<module>"   s   $"·A
