U
    ˆjLN  ã                   @   sÐ   d Z ddlmZmZ ddlmZ ddlmZmZm	Z	m
Z
mZ ddlmZ ddlmZ ddlmZ G dd	„ d	eƒZG d
d„ dƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZG dd„ dƒZG dd„ deƒZdS )z 
Test for L{twisted.web.proxy}.
é    )ÚMemoryReactorÚ StringTransportWithDisconnection)ÚTestCase)ÚProxyClientÚProxyClientFactoryÚProxyRequestÚReverseProxyRequestÚReverseProxyResource)ÚResource)ÚSite©ÚDummyRequestc                   @   s@   e Zd ZdZd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 ¡ |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

ó   hosts   127.0.0.1:1234)r
   r   r	   ZputChildr   r   ÚbuildProtocolÚmakeConnectionZ
addCleanupÚconnectionLostÚdataReceivedÚ
tcpClientsÚassertEqualÚassertIsInstancer   ÚrestÚheaders)ÚselfÚuriÚexpectedURIÚrootÚreactorÚresourceÚsiteÚ	transportÚchannelÚhostÚportÚfactoryÚ_timeoutZ
_bind_addr© r)   ú?/tmp/pip-unpacked-wheel-j5h_c8_q/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_render8   s    z%ReverseProxyResourceTests.test_renderc                 C   s   |   dd¡S )zÆ
        Test that L{ReverseProxyResource.render} will instantiate a child
        resource that will initiate a connection to the given server
        requesting the apropiate url subpath.
        s   /index/page1s   /path/page1r,   r-   r)   r)   r*   Útest_render_subpage?   s    z-ReverseProxyResourceTests.test_render_subpagec                 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_renderWithQueryG   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   Úpathr&   r%   ZassertIdenticalr    )r   r    r!   Úchildr)   r)   r*   Útest_getChildN   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	   r2   r   r3   )r   r!   r4   r)   r)   r*   Útest_getChildWithSpecial_   s    z2ReverseProxyResourceTests.test_getChildWithSpecialN)
Ú__name__Ú
__module__Ú__qualname__Ú__doc__r+   r.   r/   r0   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__r   s    zDummyChannel.__init__c                 C   s
   || _ dS )z;
        Keep track of the connection lost reason.
        N)r<   )r   Úreasonr)   r)   r*   r   y   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;   i   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   Ú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   r3   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   ÚrequestBodyr3   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   Ú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.
        )r[   r#   ÚvaluerM   r   )r   rZ   rL   r   ZrequestContentZreceivedLineÚreceivedHeadersrK   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   rP   rB   )ÚstrÚencodeÚappendÚextendÚjoin)	r   ÚcodeÚmessager   rK   ÚlinesrF   Úvaluesr\   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.
        NrP   )	r   ÚresponseCodeÚresponseMessageÚlistÚresponseHeadersÚgetAllRawHeadersÚsortre   Úwritten)r   rU   rf   rg   r   rK   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.
        r1   rQ   rR   s    /foo HTTP/1.0ó   close)ó
   connectionrQ   é   N)rN   rX   r^   r   r   rj   rs   r#   ÚloseConnectionZassertFalseÚ	connectedÚfinished)r   rf   rg   r   rK   rV   rW   rw   rU   Ú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
©r{   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.
        r|   r}   r~   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 FoundrP   r�   r-   r)   r)   r*   Útest_statusWithMessage0  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 bazr|   r}   ó   Content-Lengthr_   ©r{   ra   Úlenrb   ©r   Údatar)   r)   r*   Útest_contentLength7  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‡   r|   r}   rˆ   r_   F)rw   r‰   r‹   r)   r)   r*   Útest_losesConnectionA  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.
        rO   ó   /foorT   rR   r1   )rQ   s   proxy-connectionrP   Nrt   ©rQ   ru   )r   r   r   )r   rz   r)   r)   r*   Útest_headersCleanupsO  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.
        rR   s   300ó
   keep-alive)rQ   r’   ru   rt   ru   rO   r�   rT   rP   Ns   GET /foo HTTP/1.0)Úcopyr   r^   )r   r   rr   rz   r)   r)   r*   Útest_keepaliveNotForwardeda  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.
        r1   s   servers   old-bars   dates   old-bazs   content-types   old/quxrQ   rR   )r   r   s
   2010-01-01s   application/x-baz)s   Servers   Dateó   Content-Typer|   r}   rP   N)
rN   rn   ZsetRawHeadersrX   r[   r   rj   Úitemsrs   rm   )r   rU   rz   r   r)   r)   r*   Útest_defaultHeadersOverriddenr  s    

ýz.ProxyClientTests.test_defaultHeadersOverridden)rO   NrP   )rO   rP   T)r7   r8   r9   r:   rM   rN   rX   r[   r^   rj   rs   r{   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.
        r1   rO   r�   rT   rQ   rR   Ú Niõ  s   Gateway errorr•   rP   s   <H1>Could not connect</H1>rv   )r   r   ZclientConnectionFailedr   rk   rl   rm   rn   ro   re   rq   ry   )r   rU   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).
        rO   r�   rT   rQ   rR   s	   Some dataNrt   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}.
    rO   rP   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.comrT   rv   r   úexample.coméP   é   r   ó   example.comN)r   r;   r   r   Ú	gotLengthrŠ   ZhandleContentChunkÚrequestReceivedr   r   r   r   r›   Úversionr   rŒ   r   Zfather)
r   r   r   rV   rŒ   r#   r$   r    rU   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}.
        rP   rS   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   rO   s   http://example.com:1234/foo/barrT   rv   rŸ   r   N)	r   r;   r   r   r£   r¤   r   rŠ   r   )r   r#   r$   r    rU   r)   r)   r*   Útest_processWithPortì  s    
z&ProxyRequestTests.test_processWithPortN)rO   rP   )	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)r%   r&   )r   r%   r&   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   rO   r§   rT   rv   r¡   r   r¢   N)r   r;   r   r   r­   r'   r£   r¤   r   rŠ   r   r   r   r   )r   r#   r$   r    rU   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.internet.testingr   r   Ztwisted.trial.unittestr   Ztwisted.web.proxyr   r   r   r   r	   Ztwisted.web.resourcer
   Ztwisted.web.serverr   Ztwisted.web.test.test_webr   r   r;   rA   r˜   rž   r­   r®   r)   r)   r)   r*   Ú<module>   s   S# |+L
