o
    ¯bNN  ã                   @   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	   ÚputChildr   r   ÚbuildProtocolÚmakeConnectionÚ
addCleanupÚconnectionLostÚdataReceivedÚ
tcpClientsÚassertEqualÚassertIsInstancer   ÚrestÚheaders)ÚselfÚuriÚexpectedURIÚrootÚreactorÚresourceÚsiteÚ	transportÚchannelÚhostÚportÚfactoryÚ_timeoutÚ
_bind_addr© r,   ú=/usr/lib/python3/dist-packages/twisted/web/test/test_proxy.pyÚ_testRender   s    

z%ReverseProxyResourceTests._testRenderc                 C   ó   |   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   ó   z%ReverseProxyResourceTests.test_renderc                 C   r/   )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/page1r0   r1   r,   r,   r-   Útest_render_subpage?   ó   z-ReverseProxyResourceTests.test_render_subpagec                 C   r/   )zr
        Test that L{ReverseProxyResource.render} passes query parameters to the
        created factory.
        s   /index?foo=bars   /path?foo=barr0   r1   r,   r,   r-   Útest_renderWithQueryG   r3   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'   Ú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	   r8   r   r9   )r   r#   r;   r,   r,   r-   Útest_getChildWithSpecial_   s   z2ReverseProxyResourceTests.test_getChildWithSpecialN)
Ú__name__Ú
__module__Ú__qualname__Ú__doc__r.   r2   r4   r6   r<   r=   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)rC   )r   Úreasonr,   r,   r-   r   y   ó   
zDummyChannel.connectionLostc                 C   ó
   | j  ¡ S )z:
        Get peer information from the transport.
        )r%   ÚgetPeerr1   r,   r,   r-   rH      rF   zDummyChannel.getPeerc                 C   rG   )z:
        Get host information from the transport.
        )r%   ÚgetHostr1   r,   r,   r-   rI   …   rF   zDummyChannel.getHostN)r>   r?   r@   rA   rD   r   rH   rI   r,   r,   r,   r-   rB   i   s    rB   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>)rM   Ú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   r9   r,   r,   r-   ÚmakeRequestŸ   s   zProxyClientTests.makeRequestó   GETNó    c                 C   s,   |du 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)Úpostpathr   )r   ÚrequestÚmethodr   ÚrequestBodyr9   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   ÚproxyClientÚ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.
        )rf   r%   ÚvaluerV   r   )r   rd   rU   r   ÚrequestContentÚreceivedLineÚreceivedHeadersrT   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ó    rL   rY   rK   )ÚstrÚencodeÚappendÚextendÚjoin)	r   ÚcodeÚmessager   rT   ÚlinesrO   Úvaluesrg   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.
        NrY   )	r   ÚresponseCodeÚresponseMessageÚlistÚresponseHeadersÚgetAllRawHeadersÚsortrr   Úwritten)r   r_   rs   rt   r   rT   rj   ÚexpectedHeadersr,   r,   r-   ÚassertForwardsResponseÞ   s   
z'ProxyClientTests.assertForwardsResponseTc                 C   s˜   |   d¡}|  ||ddi|¡}	|  |	|d dddœ¡}
|  |
|¡ |	 |  ||||¡¡ |  |||||¡ |r<|	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.
        r7   rZ   r[   s    /foo HTTP/1.0ó   close)ó
   connectionrZ   é   N)rW   rb   rk   r   r   rw   r€   r%   ÚloseConnectionÚassertFalseÚ	connectedÚfinished)r   rs   rt   r   rT   r`   ra   r„   r_   ÚclientÚ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Š   r1   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   r1   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 FoundrY   r   r1   r,   r,   r-   Útest_statusWithMessage0  ó   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-Lengthrl   ©rŠ   rn   Úlenro   ©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˜   rl   F)r„   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.
        rX   ó   /foor]   r[   r7   )rZ   s   proxy-connectionrY   Nr   ©rZ   r‚   )r   r   r   )r   rˆ   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.
        r[   s   300ó
   keep-alive)rZ   r¢   r‚   r   r‚   rX   rŸ   r]   rY   Ns   GET /foo HTTP/1.0)Úcopyr   rk   )r   r   r   rˆ   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.
        r7   s   servers   old-bars   dates   old-bazs   content-types   old/quxrZ   r[   )r   rŽ   s
   2010-01-01s   application/x-baz)s   Servers   Dateó   Content-Typer‹   rŒ   rY   N)
rW   r{   ÚsetRawHeadersrb   rf   r   rw   Úitemsr€   rz   )r   r_   rˆ   r   r,   r,   r-   Útest_defaultHeadersOverriddenr  s   

ýz.ProxyClientTests.test_defaultHeadersOverridden)rX   NrY   )rX   rY   T)r>   r?   r@   rA   rV   rW   rb   rf   rk   rw   r€   rŠ   r‘   r”   r•   r   rž   r¡   r¤   r¨   r,   r,   r,   r-   rJ   Œ   s*    
	
ø,
	
rJ   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.
        r7   rX   rŸ   r]   rZ   r[   Ú Niõ  s   Gateway errorr¥   rY   s   <H1>Could not connect</H1>rƒ   )r   r   ÚclientConnectionFailedr   rx   ry   rz   r{   r|   rr   r~   r‡   )r   r_   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).
        rX   rŸ   r]   rZ   r[   s	   Some dataNr   r    )	r   r   r   r   r   Úcommandr   rœ   r   )r   r)   Úprotor,   r,   r-   Útest_buildProtocol   s   ÿ
ÿz*ProxyClientFactoryTests.test_buildProtocolN)r>   r?   r@   rA   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}.
    rX   rY   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.comr]   rƒ   r   úexample.coméP   é   r   ó   example.comN)r   rB   r   r   Ú	gotLengthrš   ÚhandleContentChunkÚrequestReceivedr   r   r   r   r­   Úversionr   rœ   r   Úfather)
r   r   r    r`   rœ   r%   r&   r"   r_   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º   r1   r,   r,   r-   Útest_processÑ  s   
zProxyRequestTests.test_processc                 C   r/   )z£
        If the incoming request doesn't contain a slash,
        L{ProxyRequest.process} should add one when instantiating
        L{ProxyClientFactory}.
        rY   r\   r¼   r1   r,   r,   r-   Ú test_processWithoutTrailingSlashÝ  r5   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¼   r1   r,   r,   r-   Útest_processWithDataå  r–   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   rX   s   http://example.com:1234/foo/barr]   rƒ   r±   r   N)	r   rB   r   r   rµ   r·   r   rš   r   )r   r%   r&   r"   r_   r,   r,   r-   Útest_processWithPortì  s   
z&ProxyRequestTests.test_processWithPortN)rX   rY   )	r>   r?   r@   rA   rº   r½   r¾   r¿   rÀ   r,   r,   r,   r-   r°   ²  s    
r°   c                   @   ó   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-   rD     s   
zDummyFactory.__init__N)r>   r?   r@   rA   rD   r,   r,   r,   r-   rÂ   þ  ó    rÂ   c                   @   rÁ   )Ú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   rX   r»   r]   rƒ   r³   r   r´   N)r   rB   r   r   rÂ   r)   rµ   r·   r   rš   r   r   r   r   )r   r%   r&   r"   r_   r)   r,   r,   r-   r½     s   
z%ReverseProxyRequestTests.test_processN)r>   r?   r@   rA   r½   r,   r,   r,   r-   rÄ     rÃ   rÄ   N)rA   Útwisted.test.proto_helpersr   r   Útwisted.trial.unittestr   Útwisted.web.proxyr   r   r   r   r	   Útwisted.web.resourcer
   Útwisted.web.serverr   Útwisted.web.test.test_webr   r   rB   rJ   r©   r°   rÂ   rÄ   r,   r,   r,   r-   Ú<module>   s   S# |+L
