3
¥YðiŒ6  ã               @   s  d Z ddl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 ejr~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eƒZefeeejddœdd„ZG dd„ dƒZG dd„ dƒZG dd„ dƒZG dd„ dƒZG dd„ dƒZdS )a‹  
WSGI Protocol Linter
====================

This module provides a middleware that performs sanity checks on the
behavior of the WSGI server and application. It checks that the
:pep:`3333` WSGI spec is properly implemented. It also warns on some
common HTTP errors such as non-empty responses for 304 status codes.

.. autoclass:: LintMiddleware

:copyright: 2007 Pallets
:license: BSD-3-Clause
é    N)ÚTracebackType)Úurlparse)Úwarné   )ÚHeaders)Úis_entity_header)ÚFileWrapper)ÚStartResponse)ÚWSGIApplication)ÚWSGIEnvironmentc               @   s   e Zd ZdZdS )ÚWSGIWarningz Warning class for WSGI warnings.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r   r   ú</tmp/pip-build-8om4l6ph/Werkzeug/werkzeug/middleware/lint.pyr      s   r   c               @   s   e Zd ZdZdS )ÚHTTPWarningz Warning class for HTTP warnings.N)r   r   r   r   r   r   r   r   r   "   s   r   )ÚcontextÚobjÚneedÚreturnc             C   s8   t |ƒ|k	r4t| ›d|j›dt |ƒj›d�tdd� d S )Nz
 requires z, got Ú.é   )Ú
stacklevel)Útyper   r   r   )r   r   r   r   r   r   Ú
check_type&   s
    r   c               @   sh   e Zd Zeje ddœdd„Zejedœdd„Zejedœdd	„Z	ej
e d
œdd„Zdd
œdd„ZdS )ÚInputStreamN)Ústreamr   c             C   s
   || _ d S )N)Ú_stream)Úselfr   r   r   r   Ú__init__0   s    zInputStream.__init__)Úargsr   c             G   sB   t |ƒdkrtdtdd� nt |ƒdkr6tdtdd� | jj|Ž S )Nr   zŸWSGI does not guarantee an EOF marker on the input stream, thus making calls to 'wsgi.input.read()' unsafe. Conforming servers may never return from this call.r   )r   é   z2Too many parameters passed to 'wsgi.input.read()'.)Úlenr   r   r   Úread)r    r"   r   r   r   r%   3   s    
zInputStream.readc             G   sL   t |ƒdkrtdtdd� n$t |ƒdkr8tdtdd� ntdƒ‚| jj|Ž S )Nr   z_Calls to 'wsgi.input.readline()' without arguments are unsafe. Use 'wsgi.input.read()' instead.r   )r   r#   z~'wsgi.input.readline()' was called with a size hint. WSGI does not support this, although it's available on all major servers.z5Too many arguments passed to 'wsgi.input.readline()'.)r$   r   r   Ú	TypeErrorr   Úreadline)r    r"   r   r   r   r'   D   s    

zInputStream.readline)r   c             C   s6   y
t | jƒS  tk
r0   tdtdd� t f ƒS X d S )Nz'wsgi.input' is not iterable.r   )r   )Úiterr   r&   r   r   )r    r   r   r   Ú__iter__W   s
    
zInputStream.__iter__c             C   s   t dtdd� | jjƒ  d S )Nz(The application closed the input stream!r   )r   )r   r   r   Úclose)r    r   r   r   r*   ^   s    zInputStream.close)r   r   r   ÚtÚIOÚbytesr!   ÚAnyr%   r'   ÚIteratorr)   r*   r   r   r   r   r   /   s
   r   c               @   sd   e Zd Zeje ddœdd„Zeddœdd„Zddœd	d
„Zej	e ddœdd„Z
ddœdd„ZdS )ÚErrorStreamN)r   r   c             C   s
   || _ d S )N)r   )r    r   r   r   r   r!   d   s    zErrorStream.__init__)Úsr   c             C   s   t d|tƒ | jj|ƒ d S )Nzwsgi.error.write())r   Ústrr   Úwrite)r    r1   r   r   r   r3   g   s    zErrorStream.write)r   c             C   s   | j jƒ  d S )N)r   Úflush)r    r   r   r   r4   k   s    zErrorStream.flush)Úseqr   c             C   s   x|D ]}| j |ƒ qW d S )N)r3   )r    r5   Úliner   r   r   Ú
writelinesn   s    
zErrorStream.writelinesc             C   s   t dtdd� | jjƒ  d S )Nz(The application closed the error stream!r   )r   )r   r   r   r*   )r    r   r   r   r*   r   s    zErrorStream.close)r   r   r   r+   r,   r2   r!   r3   r4   ÚIterabler7   r*   r   r   r   r   r0   c   s
   r0   c               @   s@   e Zd Zejegdf eje ddœdd„Zeddœdd„Z	dS )ÚGuardedWriteN)r3   Úchunksr   c             C   s   || _ || _d S )N)Ú_writeÚ_chunks)r    r3   r:   r   r   r   r!   x   s    zGuardedWrite.__init__)r1   r   c             C   s*   t d|tƒ | j|ƒ | jjt|ƒƒ d S )Nzwrite())r   r-   r;   r<   Úappendr$   )r    r1   r   r   r   Ú__call__|   s    
zGuardedWrite.__call__)
r   r   r   r+   ÚCallabler-   ÚListÚintr!   r>   r   r   r   r   r9   w   s   $r9   c               @   sn   e Zd Zeje ejeef ej	e ddœdd„Z
d dœdd„Zedœdd	„Zddœd
d„Zddœdd„ZdS )ÚGuardedIteratorN)ÚiteratorÚheaders_setr:   r   c             C   s(   || _ t|ƒj| _d| _|| _|| _d S )NF)Ú	_iteratorr(   Ú__next__Ú_nextÚclosedrD   r:   )r    rC   rD   r:   r   r   r   r!   ƒ   s
    zGuardedIterator.__init__)r   c             C   s   | S )Nr   )r    r   r   r   r)   �   s    zGuardedIterator.__iter__c             C   sP   | j rtdtdd� | jƒ }| js0tdtdd� td|tƒ | jjt	|ƒƒ |S )Nz Iterated over closed 'app_iter'.r   )r   z8The application returned before it started the response.zapplication iterator items)
rH   r   r   rG   rD   r   r-   r:   r=   r$   )r    Úrvr   r   r   rF   ’   s    zGuardedIterator.__next__c             C   s  d| _ t| jdƒr| jjƒ  | j�r| j\}}t| jƒ}|jdtd�}|dkršx:|D ]2\}}|j	ƒ }|dkrTt
|ƒrTtd|›d	�tƒ qTW |r˜td
tƒ njd|  ko¬dk n  sº|dkrè|dkrÒt|› d�tƒ |ræt|› d�tƒ n|d k	oö||k�rtdtƒ d S )NTr*   zcontent-length)r   i0  Úexpiresúcontent-locationzEntity header z found in 304 response.z#304 responses must not have a body.éd   éÈ   éÌ   r   z- responses must have an empty content length.z  responses must not have a body.zGContent-Length and the number of bytes sent to the client do not match.)rJ   rK   )rH   ÚhasattrrE   r*   rD   Úsumr:   ÚgetrA   Úlowerr   r   r   r   )r    Ústatus_codeÚheadersZ
bytes_sentÚcontent_lengthÚkeyÚ_valuer   r   r   r*   £   s6    



 zGuardedIterator.closec             C   s.   | j s*ytdtƒ W n tk
r(   Y nX d S )Nz4Iterator was garbage collected before it was closed.)rH   r   r   Ú	Exception)r    r   r   r   Ú__del__È   s    zGuardedIterator.__del__)r   r   r   r+   r8   r-   ÚTuplerA   r   r@   r!   r)   rF   r*   rY   r   r   r   r   rB   ‚   s   %rB   c               @   s¸   e Zd ZdZdddœdd„Zdddœd	d
„Zeejej	eef  ej
ej	eje eef  ej	eef dœdd„Zeddœdd„Zeje ddœdd„Zejejeje dœdd„ZdS )ÚLintMiddlewareaœ  Warns about common errors in the WSGI and HTTP behavior of the
    server and wrapped application. Some of the issues it checks are:

    -   invalid status codes
    -   non-bytes sent to the WSGI server
    -   strings returned from the WSGI application
    -   non-empty conditional responses
    -   unquoted etags
    -   relative URLs in the Location header
    -   unsafe calls to wsgi.input
    -   unclosed iterators

    Error information is emitted using the :mod:`warnings` module.

    :param app: The WSGI application to wrap.

    .. code-block:: python

        from werkzeug.middleware.lint import LintMiddleware
        app = LintMiddleware(app)
    r
   N)Úappr   c             C   s
   || _ d S )N)r\   )r    r\   r   r   r   r!   é   s    zLintMiddleware.__init__r   )Úenvironr   c          	   C   sÄ   t |ƒtk	rtdtdd� x*dD ]"}||kr td|›d�tdd� q W |d dkr`tdtdd� |jddƒ}|jddƒ}|rœ|d dkrœtd|›�tdd� |rÀ|d dkrÀtd|›�tdd� d S )Nz/WSGI environment is not a standard Python dict.é   )r   ÚREQUEST_METHODÚSERVER_NAMEÚSERVER_PORTúwsgi.versionú
wsgi.inputúwsgi.errorsúwsgi.multithreadúwsgi.multiprocessúwsgi.run_oncezRequired environment key z
 not foundr   r#   r   z"Environ is not a WSGI 1.0 environ.ZSCRIPT_NAMEÚ Z	PATH_INFOú/z+'SCRIPT_NAME' does not start with a slash: z)'PATH_INFO' does not start with a slash: )	r_   r`   ra   rb   rc   rd   re   rf   rg   )r#   r   )r   Údictr   r   rQ   )r    r]   rV   Úscript_nameZ	path_infor   r   r   Úcheck_environì   sD            
zLintMiddleware.check_environ)ÚstatusrT   Úexc_infor   c       	      C   sd  t d|tƒ |jd dƒd }t|ƒdks2|jƒ  r@tdtdd� t|ƒdk sX|d dkrntd	|›d
�tdd� t|ƒ}|dk rŒtdtdd� t|ƒt	k	r¦tdtdd� x||D ]t}t|ƒt
k	sÈt|ƒdkrÖtdtdd� |\}}t|ƒtk	søt|ƒtk	�rtdtdd� |jƒ dkr¬tdtdd� q¬W |d k	�rJt|t
ƒ �rJtdtdd� t|ƒ}| j|ƒ ||fS )Nrm   r#   r   r   z!Status code must be three digits.)r   r^   ú zInvalid value for status zJ. Valid status strings are three digits, a space and a status explanation.rL   zStatus code < 100 detected.zHeader list is not a list.r   z#Header items must be 2-item tuples.z'Header keys and values must be strings.zFThe status header is not supported due to conflicts with the CGI spec.zInvalid value for exc_info.)r   r2   Úsplitr$   Úisdigitr   r   rA   r   ÚlistÚtuplerR   Ú
isinstancer   Úcheck_headers)	r    rm   rT   rn   Zstatus_code_strrS   ÚitemÚnameÚvaluer   r   r   Úcheck_start_response  s>    


z#LintMiddleware.check_start_response)rT   r   c             C   s¤   |j dƒ}|d k	rv|jdƒr@|jdƒr4tdtdd� |dd … }|d d… |dd …   kobd	kn  svtd
tdd� |j dƒ}|d k	r t|ƒjs tdtdd� d S )NÚetagúW/úw/z)Weak etag indicator should be upper case.r^   )r   r   r#   ú"zUnquoted etag emitted.Úlocationz+Absolute URLs required for location header.)r{   r|   éÿÿÿÿ)rQ   Ú
startswithr   r   r   Únetloc)r    rT   rz   r~   r   r   r   ru   N  s$    


(

zLintMiddleware.check_headers)Úapp_iterr   c             C   s   t |tƒrtdtdd� d S )Nz¤The application returned a bytestring. The response will send one character at a time to the client, which will kill performance. Return a list or iterable instead.r   )r   )rt   r-   r   r   )r    r‚   r   r   r   Úcheck_iteratori  s
    
zLintMiddleware.check_iterator)r"   Úkwargsr   c                sÞ   t |ƒdkrtdtdd� |r,tdtdd� |d }|d ‰ˆj|ƒ t|d ƒ|d< t|d ƒ|d< t|d	< g ‰g ‰ tjtjtj	t
gd f d
œ‡ ‡‡‡fdd„}ˆj|tjd|ƒƒ}ˆj|ƒ t|tjtjttf ˆƒˆ ƒS )Nr   zA WSGI app takes two arguments.)r   z+A WSGI app does not take keyword arguments.r   r#   z
wsgi.inputzwsgi.errorszwsgi.file_wrapper)r"   r„   r   c                 s„   t | ƒd	kr&tdt | ƒ› d�tdd� |r4tdtƒ | d }| d }t | ƒdkrX| d nd }ˆj|||ƒˆd d …< tˆ|||ƒˆ ƒS )
Nr   r   zInvalid number of arguments: z, expected 2 or 3.)r   z1'start_response' does not take keyword arguments.r   r#   >   r   r   )r$   r   r   ry   r9   )r"   r„   rm   rT   rn   )r:   rD   r    Ústart_responser   r   Úchecking_start_responseŠ  s    
z8LintMiddleware.__call__.<locals>.checking_start_responser	   )r$   r   r   rl   r   r0   r   r+   r.   r?   r-   r\   Úcastrƒ   rB   rZ   rA   r   )r    r"   r„   r]   r†   r‚   r   )r:   rD   r    r…   r   r>   s  s&    
$
zLintMiddleware.__call__)r   r   r   r   r!   rl   r2   r+   r@   rZ   ÚOptionalÚTypeÚBaseExceptionr   rA   r   ry   ru   r8   r-   rƒ   r.   r>   r   r   r   r   r[   Ò   s   ./
r[   ) r   Útypingr+   Útypesr   Úurllib.parser   Úwarningsr   Zdatastructuresr   Úhttpr   Zwsgir   ZTYPE_CHECKINGZ_typeshed.wsgir	   r
   r   ÚWarningr   r   r2   Úobjectr‰   r   r   r0   r9   rB   r[   r   r   r   r   Ú<module>   s&   	4P