U
    
W[|N  ã                   @   sä   d 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 ddl
mZmZmZ dd	lmZ G d
d„ deƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZdS )z 
Test for L{twisted.web.proxy}.
é    )ÚTestCase)Ú StringTransportWithDisconnection)ÚMemoryReactor)ÚResource)ÚSite)ÚReverseProxyResourceÚProxyClientFactory)ÚProxyClientÚProxyRequestÚReverseProxyRequest©ÚDummyRequestc                   @   s8   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ ZdS )ÚReverseProxyResourceTestsz,
    Tests for L{ReverseProxyResource}.
    c           
      C   sä   t ƒ }tƒ }tddd|ƒ}| d|¡ t|ƒ}tƒ }| d¡}| |¡ |  |j	d¡ | 
d| d ¡ |  t|jƒd¡ |  |jd	 d	 d¡ |  |jd	 d d¡ |jd	 d
 }	|  |	t¡ |  |	j|¡ |  |	jd d¡ dS )z›
        Check that a request pointing at C{uri} produce a new proxy connection,
        with the path of this request pointing at C{expectedURI}.
        ú	127.0.0.1éÒ  ó   /paths   indexNs   GET s     HTTP/1.1
Accept: text/html

é   r   é   ó   hosts   127.0.0.1:1234)r   r   r   ZputChildr   r   ÚbuildProtocolÚmakeConnectionZ
addCleanupÚconnectionLostÚdataReceivedÚassertEqualÚlenÚ
tcpClientsÚassertIsInstancer   ÚrestÚheaders)
ÚselfÚuriÚexpectedURIÚrootÚreactorÚresourceZsiteÚ	transportÚchannelÚfactory© r(   ú=/usr/lib/python3/dist-packages/twisted/web/test/test_proxy.pyÚ_testRender   s*    

ÿþz%ReverseProxyResourceTests._testRenderc                 C   s   |   dd¡S )z˜
        Test that L{ReverseProxyResource.render} initiates a connection to the
        given server with a L{ProxyClientFactory} as parameter.
        s   /indexr   ©r*   ©r   r(   r(   r)   Útest_render9   s    z%ReverseProxyResourceTests.test_renderc                 C   s   |   dd¡S )zr
        Test that L{ReverseProxyResource.render} passes query parameters to the
        created factory.
        s   /index?foo=bars   /path?foo=barr+   r,   r(   r(   r)   Útest_renderWithQueryA   s    z.ReverseProxyResourceTests.test_renderWithQueryc                 C   sj   t ƒ }tddd|ƒ}| dd¡}|  |t¡ |  |jd¡ |  |jd¡ |  |jd¡ |  |j	|j	¡ dS )a  
        The L{ReverseProxyResource.getChild} method should return a resource
        instance with the same class as the originating resource, forward
        port, host, and reactor values, and update the path value with the
        value passed.
        r   r   r   ó   fooNs	   /path/foo)
r   r   ÚgetChildr   r   ÚpathÚportÚhostZassertIdenticalr#   )r   r#   r$   Úchildr(   r(   r)   Útest_getChildI   s    z'ReverseProxyResourceTests.test_getChildc                 C   s*   t dddƒ}| dd¡}|  |jd¡ dS )zu
        The L{ReverseProxyResource} return by C{getChild} has a path which has
        already been quoted.
        r   r   r   s    /%Ns   /path/%20%2F%25)r   r0   r   r1   )r   r$   r4   r(   r(   r)   Útest_getChildWithSpecial[   s    z2ReverseProxyResourceTests.test_getChildWithSpecialN)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r*   r-   r.   r5   r6   r(   r(   r(   r)   r      s   !r   c                   @   s0   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
S )ÚDummyChannelzÙ
    A dummy HTTP channel, that does nothing but holds a transport and saves
    connection lost.

    @ivar transport: the transport used by the client.
    @ivar lostReason: the reason saved at connection lost.
    c                 C   s   || _ d| _dS )z4
        Hold a reference to the transport.
        N)r%   Ú
lostReason)r   r%   r(   r(   r)   Ú__init__o   s    zDummyChannel.__init__c                 C   s
   || _ dS )z;
        Keep track of the connection lost reason.
        N)r<   )r   Úreasonr(   r(   r)   r   w   s    zDummyChannel.connectionLostc                 C   s
   | j  ¡ S )z:
        Get peer information from the transport.
        )r%   ÚgetPeerr,   r(   r(   r)   r?   ~   s    zDummyChannel.getPeerc                 C   s
   | j  ¡ S )z:
        Get host information from the transport.
        )r%   ÚgetHostr,   r(   r(   r)   r@   …   s    zDummyChannel.getHostN)r7   r8   r9   r:   r=   r   r?   r@   r(   r(   r(   r)   r;   f   s
   r;   c                   @   s”   e Zd ZdZdd„ Zdd„ Zd&d	d
„Zdd„ Zdd„ Zdd„ Z	dd„ Z
d'dd„Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zd d!„ Zd"d#„ Zd$d%„ ZdS )(ÚProxyClientTestsz#
    Tests for L{ProxyClient}.
    c                 C   s:   |  d¡\}}|  d¡}| d¡}|tdd„ |D ƒƒ|fS )aC  
        Parse the headers out of some web content.

        @param content: Bytes received from a web server.
        @return: A tuple of (requestLine, headers, body). C{headers} is a dict
            of headers, C{requestLine} is the first line (e.g. "POST /foo ...")
            and C{body} is whatever is left.
        s   

ó   
r   c                 s   s   | ]}|  d ¡V  qdS )ó   : N)Úsplit)Ú.0Úheaderr(   r(   r)   Ú	<genexpr>Ÿ   s     z4ProxyClientTests._parseOutHeaders.<locals>.<genexpr>)rD   ÚpopÚdict)r   Zcontentr   ÚbodyÚrequestLiner(   r(   r)   Ú_parseOutHeaders’   s    	

  ÿz!ProxyClientTests._parseOutHeadersc                 C   s   t |ƒS )z
        Make a dummy request object for the URL path.

        @param path: A URL path, beginning with a slash.
        @return: A L{DummyRequest}.
        r   )r   r1   r(   r(   r)   ÚmakeRequest¢   s    zProxyClientTests.makeRequestó   GETNó    c                 C   s,   |dkrddi}d|j  }t||d|||ƒS )a´  
        Make a L{ProxyClient} object used for testing.

        @param request: The request to use.
        @param method: The HTTP method to use, GET by default.
        @param headers: The HTTP headers to use expressed as a dict. If not
            provided, defaults to {'accept': 'text/html'}.
        @param requestBody: The body of the request. Defaults to the empty
            string.
        @return: A L{ProxyClient}
        Nó   acceptó	   text/htmló   /ó   HTTP/1.0)Zpostpathr	   )r   ÚrequestÚmethodr   ÚrequestBodyr1   r(   r(   r)   ÚmakeProxyClient¬   s    
     ÿz ProxyClientTests.makeProxyClientc                 C   s   t ƒ }||_| |¡ |S )z»
        Connect a proxy client to a L{StringTransportWithDisconnection}.

        @param proxyClient: A L{ProxyClient}.
        @return: The L{StringTransportWithDisconnection}.
        )r   Zprotocolr   )r   ÚproxyClientZclientTransportr(   r(   r)   ÚconnectProxyÀ   s    
zProxyClientTests.connectProxyc                 C   s@   |   |¡ |j ¡ }|  |¡\}}}|  ||¡ |  ||¡ |S )a`  
        Assert that C{proxyClient} sends C{headers} when it connects.

        @param proxyClient: A L{ProxyClient}.
        @param requestLine: The request line we expect to be sent.
        @param headers: A dict of headers we expect to be sent.
        @return: If the assertion is successful, return the request body as
            bytes.
        )rY   r%   ÚvaluerL   r   )r   rX   rK   r   ZrequestContentZreceivedLineÚreceivedHeadersrJ   r(   r(   r)   ÚassertForwardsHeadersÍ   s    


ÿ
z&ProxyClientTests.assertForwardsHeadersc           	      C   s^   dt |ƒ d¡ d | g}|D ]$\}}|D ]}| |d | ¡ q,q | d|g¡ d |¡S )Ns	   HTTP/1.0 Úasciió    rC   rO   rB   )ÚstrÚencodeÚappendÚextendÚjoin)	r   ÚcodeÚmessager   rJ   ÚlinesrF   ÚvaluesrZ   r(   r(   r)   ÚmakeResponseBytesà   s    z"ProxyClientTests.makeResponseBytesc                 C   sj   |   |j|¡ |   |j|¡ t|j ¡ ƒ}| ¡  |dd… }| ¡  |   ||¡ |   d |j¡|¡ dS )aK  
        Assert that C{request} has forwarded a response from the server.

        @param request: A L{DummyRequest}.
        @param code: The expected HTTP response code.
        @param message: The expected HTTP message.
        @param headers: The expected HTTP headers.
        @param body: The expected response body.
        NrO   )	r   ÚresponseCodeÚresponseMessageÚlistÚresponseHeadersÚgetAllRawHeadersÚsortrc   Úwritten)r   rT   rd   re   r   rJ   r[   ÚexpectedHeadersr(   r(   r)   ÚassertForwardsResponseé   s    
z'ProxyClientTests.assertForwardsResponseTc                 C   s˜   |   d¡}|  ||ddi|¡}	|  |	|d dddœ¡}
|  |
|¡ |	 |  ||||¡¡ |  |||||¡ |rx|	j ¡  |  	|	jj
¡ |  |jd¡ dS )	z‹
        Build a fake proxy connection, and send C{data} over it, checking that
        it's forwarded to the originating request.
        r/   rP   rQ   s    /foo HTTP/1.0ó   close)ó
   connectionrP   r   N)rM   rW   r\   r   r   rh   rq   r%   ÚloseConnectionZassertFalseZ	connectedÚfinished)r   rd   re   r   rJ   rU   rV   rt   rT   ÚclientZreceivedBodyr(   r(   r)   Ú_testDataForwardý   s*    
   ÿ þÿ
z!ProxyClientTests._testDataForwardc                 C   s   |   dddddgfgd¡S )zÃ
        When connected to the server, L{ProxyClient} should send the saved
        request, with modifications of the headers, and then forward the result
        to the parent request.
        éÈ   ó   OKó   Fooó   bars   bazó   Some data
©rw   r,   r(   r(   r)   Útest_forward   s       ÿzProxyClientTests.test_forwardc                 C   s   |   ddddgfgddd¡S )z~
        Try to post content in the request, and check that the proxy client
        forward the body of the request.
        rx   ry   rz   r{   r|   ó   POSTó   Some contentr}   r,   r(   r(   r)   Útest_postData*  s      
   ÿzProxyClientTests.test_postDatac                 C   s   |   ddg d¡S )z’
        If the response contains a status with a message, it should be
        forwarded to the parent request with all the information.
        i”  s	   Not FoundrO   r}   r,   r(   r(   r)   Útest_statusWithMessage3  s       ÿz'ProxyClientTests.test_statusWithMessagec                 C   s*   d}|   dddtt|ƒƒ d¡gfg|¡S )z
        If the response contains a I{Content-Length} header, the inbound
        request object should still only have C{finish} called on it once.
        ó   foo bar bazrx   ry   ó   Content-Lengthr]   ©rw   r_   r   r`   ©r   Údatar(   r(   r)   Útest_contentLength<  s    üz#ProxyClientTests.test_contentLengthc                 C   s.   d}| j dddtt|ƒƒ d¡gfg|ddS )zŸ
        If the response contains a I{Content-Length} header, the outgoing
        connection is closed when all response body data has been received.
        rƒ   rx   ry   r„   r]   F)rt   r…   r†   r(   r(   r)   Útest_losesConnectionI  s    ûz%ProxyClientTests.test_losesConnectionc                 C   s0   t ddddddœddƒ}|  |jdd	d
œ¡ dS )z®
        The headers given at initialization should be modified:
        B{proxy-connection} should be removed if present, and B{connection}
        should be added.
        rN   ó   /foorS   rQ   r/   )rP   s   proxy-connectionrO   Nrr   ©rP   rs   )r	   r   r   )r   rv   r(   r(   r)   Útest_headersCleanupsW  s      ÿÿz%ProxyClientTests.test_headersCleanupsc                 C   sF   ddddœ}|  ¡ }d|d< |d= tddd	|d
dƒ}|  |d|¡ dS )z¶
        The proxy doesn't really know what to do with keepalive things from
        the remote server, so we stomp over any keepalive header we get from
        the client.
        rQ   s   300ó
   keep-alive)rP   r   rs   rr   rs   rN   rŠ   rS   rO   Ns   GET /foo HTTP/1.0)Úcopyr	   r\   )r   r   rp   rv   r(   r(   r)   Útest_keepaliveNotForwardedc  s    ý  ÿz+ProxyClientTests.test_keepaliveNotForwardedc                 C   s    |   d¡}|j ddg¡ |j ddg¡ |j ddg¡ | j|dd	id
}|  |¡ dgdgdgdœ}| |  dd| ¡ d¡¡ |  |ddt	| ¡ ƒd¡ dS )zÞ
        L{server.Request} within the proxy sets certain response headers by
        default. When we get these headers back from the remote server, the
        defaults are overridden rather than simply appended.
        r/   s   servers   old-bars   dates   old-bazs   content-types   old/quxrP   rQ   )r   r{   s
   2010-01-01s   application/x-baz)s   Servers   Dateó   Content-Typerx   ry   rO   N)
rM   rl   ZsetRawHeadersrW   rY   r   rh   Úitemsrq   rk   )r   rT   rv   r   r(   r(   r)   Útest_defaultHeadersOverriddenv  s(    

ýÿ   
 ÿz.ProxyClientTests.test_defaultHeadersOverridden)rN   NrO   )rN   rO   T)r7   r8   r9   r:   rL   rM   rW   rY   r\   rh   rq   rw   r~   r   r‚   rˆ   r‰   rŒ   r   r’   r(   r(   r(   r)   rA      s,   
  ÿ
	    ÿ
#
		rA   c                   @   s    e Zd ZdZdd„ Zdd„ ZdS )ÚProxyClientFactoryTestsz*
    Tests for L{ProxyClientFactory}.
    c                 C   sŒ   t dgƒ}tdddddid|ƒ}| dd¡ |  |jd	¡ |  |jd
¡ |  t|j ¡ ƒddgfg¡ |  d 	|j
¡d¡ |  |jd¡ dS )zƒ
        Check that L{ProxyClientFactory.clientConnectionFailed} produces
        a B{501} response to the parent request.
        r/   rN   rŠ   rS   rP   rQ   Ú Niõ  s   Gateway errorr   rO   s   <H1>Could not connect</H1>r   )r   r   ZclientConnectionFailedr   ri   rj   rk   rl   rm   rc   ro   ru   )r   rT   r'   r(   r(   r)   Útest_connectionFailed“  s$    
  ÿ
þ
þz-ProxyClientFactoryTests.test_connectionFailedc                 C   sn   t dddddiddƒ}| d¡}|  |t¡ |  |jd¡ |  |jd¡ |  |jd¡ |  |jddd	œ¡ dS )
zŸ
        L{ProxyClientFactory.buildProtocol} should produce a L{ProxyClient}
        with the same values of attributes (with updates on the headers).
        rN   rŠ   rS   rP   rQ   s	   Some dataNrr   r‹   )	r   r   r   r	   r   Úcommandr   r‡   r   )r   r'   Úprotor(   r(   r)   Útest_buildProtocol¨  s     þ
ÿz*ProxyClientFactoryTests.test_buildProtocolN)r7   r8   r9   r:   r•   r˜   r(   r(   r(   r)   r“   Ž  s   r“   c                   @   s:   e Zd ZdZddd„Zdd„ Zdd	„ Zd
d„ Zdd„ ZdS )ÚProxyRequestTestsz$
    Tests for L{ProxyRequest}.
    rN   rO   c           
      C   sþ   t ƒ }t|ƒ}tƒ }t|d|ƒ}| t|ƒ¡ | |¡ | |d| d¡ |  t|j	ƒd¡ |  |j	d d d¡ |  |j	d d d¡ |j	d d }	|  
|	t¡ |  |	j|¡ |  |	jd¡ |  |	jd	d
i¡ |  |	j|¡ |  |	j|¡ |  |	j|¡ dS )z…
        Build a request pointing at C{uri}, and check that a proxied request
        is created, pointing a C{expectedURI}.
        Fs   http://example.comrS   r   r   úexample.coméP   r   r   ó   example.comN)r   r;   r   r
   Ú	gotLengthr   ZhandleContentChunkÚrequestReceivedr   r   r   r   r–   Úversionr   r‡   r   Zfather)
r   r    r!   rU   r‡   r%   r&   r#   rT   r'   r(   r(   r)   Ú_testProcess¿  s(    
ÿzProxyRequestTests._testProcessc                 C   s   |   dd¡S )a  
        L{ProxyRequest.process} should create a connection to the given server,
        with a L{ProxyClientFactory} as connection factory, with the correct
        parameters:
            - forward comment, version and data values
            - update headers with the B{host} value
            - remove the host from the URL
            - pass the request as parent request
        ó   /foo/bar©r    r,   r(   r(   r)   Útest_processÛ  s    
zProxyRequestTests.test_processc                 C   s   |   dd¡S )z£
        If the incoming request doesn't contain a slash,
        L{ProxyRequest.process} should add one when instantiating
        L{ProxyClientFactory}.
        rO   rR   r¢   r,   r(   r(   r)   Ú test_processWithoutTrailingSlashè  s    z2ProxyRequestTests.test_processWithoutTrailingSlashc                 C   s   |   dddd¡S )zl
        L{ProxyRequest.process} should be able to retrieve request body and
        to forward it.
        r¡   r   r€   r¢   r,   r(   r(   r)   Útest_processWithDatañ  s       ÿz&ProxyRequestTests.test_processWithDatac                 C   sz   t ƒ }t|ƒ}tƒ }t|d|ƒ}| d¡ | ddd¡ |  t|jƒd¡ |  |jd d d¡ |  |jd d d¡ d	S )
z˜
        Check that L{ProxyRequest.process} correctly parse port in the incoming
        URL, and create an outgoing connection with this port.
        Fr   rN   s   http://example.com:1234/foo/barrS   r   rš   r   N)	r   r;   r   r
   r   rž   r   r   r   )r   r%   r&   r#   rT   r(   r(   r)   Útest_processWithPortú  s    
ÿz&ProxyRequestTests.test_processWithPortN)rN   rO   )	r7   r8   r9   r:   r    r£   r¤   r¥   r¦   r(   r(   r(   r)   r™   º  s   
		r™   c                   @   s   e Zd ZdZdd„ ZdS )ÚDummyFactoryz>
    A simple holder for C{host} and C{port} information.
    c                 C   s   || _ || _d S )N)r3   r2   )r   r3   r2   r(   r(   r)   r=     s    zDummyFactory.__init__N)r7   r8   r9   r:   r=   r(   r(   r(   r)   r§     s   r§   c                   @   s   e Zd ZdZdd„ ZdS )ÚReverseProxyRequestTestsz+
    Tests for L{ReverseProxyRequest}.
    c                 C   s²   t ƒ }t|ƒ}tƒ }t|d|ƒ}tddƒ|_| d¡ | ddd¡ |  t	|j
ƒd¡ |  |j
d d d¡ |  |j
d d d¡ |j
d d	 }|  |t¡ |  |jd
di¡ dS )a  
        L{ReverseProxyRequest.process} should create a connection to its
        factory host/port, using a L{ProxyClientFactory} instantiated with the
        correct parameters, and particularly set the B{host} header to the
        factory host.
        Frš   r   r   rN   r¡   rS   r   r   r   rœ   N)r   r;   r   r   r§   r'   r   rž   r   r   r   r   r   r   )r   r%   r&   r#   rT   r'   r(   r(   r)   r£     s    
z%ReverseProxyRequestTests.test_processN)r7   r8   r9   r:   r£   r(   r(   r(   r)   r¨     s   r¨   N)r:   Ztwisted.trial.unittestr   Ztwisted.test.proto_helpersr   r   Ztwisted.web.resourcer   Ztwisted.web.serverr   Ztwisted.web.proxyr   r   r	   r
   r   Ztwisted.web.test.test_webr   r   Úobjectr;   rA   r“   r™   r§   r¨   r(   r(   r(   r)   Ú<module>   s"   S'  ,T