3
e6                 @   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-3irwxpxt/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   zWSGI 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rx:|D ]2\}}|j	 }|dkrTt
|rTtd|d	t qTW |rtd
t njd|  kodk n  s|dkr|dkrt| dt |rt| 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rtd|tdd |r|d dkrt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 rtdtdd t|t	k	rtdtdd x||D ]t}t|t
k	st|dkrtdtdd |\}}t|tk	st|tk	rtdtdd |j dkrtdtdd qW |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	rt|jst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 )NzThe 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