3
 [ð]§2  ã               @   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
 dd	lmZ ydd
lmZ W n  ek
rˆ   dd
lmZ Y nX G dd„ deƒZG dd„ deƒZddd„ZG dd„ deƒZG dd„ deƒZG dd„ deƒZeG dd„ deƒƒZG dd„ deƒ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
é    )Úwarné   )Úimplements_iterator)ÚPY2)Ústring_types)ÚHeaders)Úis_entity_header)ÚFileWrapper)Úurlparsec               @   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-w1as9q70/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   é   c             C   s(   t |ƒtk	r$td| t |ƒjf tƒ d S )Nz'%s' requires strings, got '%s')ÚtypeÚstrr   r   r   )ÚcontextÚobjÚ
stacklevelr   r   r   Úcheck_string&   s    r   c               @   s4   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ ZdS )ÚInputStreamc             C   s
   || _ d S )N)Ú_stream)ÚselfÚstreamr   r   r   Ú__init__/   s    zInputStream.__init__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   Úargsr   r   r   r!   2   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$   C   s    

zInputStream.readlinec             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__V   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.closeN)r   r   r   r   r!   r$   r&   r'   r   r   r   r   r   .   s
   r   c               @   s4   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ ZdS )ÚErrorStreamc             C   s
   || _ d S )N)r   )r   r   r   r   r   r   c   s    zErrorStream.__init__c             C   s   t d|ƒ | jj|ƒ d S )Nzwsgi.error.write())r   r   Úwrite)r   Úsr   r   r   r)   f   s    
zErrorStream.writec             C   s   | j jƒ  d S )N)r   Úflush)r   r   r   r   r+   j   s    zErrorStream.flushc             C   s   x|D ]}| j |ƒ qW d S )N)r)   )r   ÚseqÚliner   r   r   Ú
writelinesm   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'   q   s    zErrorStream.closeN)r   r   r   r   r)   r+   r.   r'   r   r   r   r   r(   b   s
   r(   c               @   s   e Zd Zdd„ Zdd„ ZdS )ÚGuardedWritec             C   s   || _ || _d S )N)Ú_writeÚ_chunks)r   r)   Úchunksr   r   r   r   w   s    zGuardedWrite.__init__c             C   s*   t d|ƒ | jj|ƒ | jjt|ƒƒ d S )Nzwrite())r   r0   r)   r1   Úappendr    )r   r*   r   r   r   Ú__call__{   s    
zGuardedWrite.__call__N)r   r   r   r   r4   r   r   r   r   r/   v   s   r/   c               @   s4   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ ZdS )ÚGuardedIteratorc             C   s:   || _ trt|ƒj| _nt|ƒj| _d| _|| _|| _d S )NF)	Ú	_iteratorr   r%   ÚnextÚ_nextÚ__next__ÚclosedÚheaders_setr2   )r   Úiteratorr;   r2   r   r   r   r   ƒ   s    zGuardedIterator.__init__c             C   s   | S )Nr   )r   r   r   r   r&   �   s    zGuardedIterator.__iter__c             C   sN   | j rtdtdd� | jƒ }| js0tdtdd� td|ƒ | jjt|ƒƒ |S )Nz Iterated over closed 'app_iter'.r   )r   z8The application returned before it started the response.zapplication iterator items)	r:   r   r   r8   r;   r   r2   r3   r    )r   Úrvr   r   r   r9   �   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”x6|D ].\}}|j	ƒ }|dkrRt
|ƒrRtd| tƒ qRW |røtd	tƒ ndd
|  ko¦dk n  s´|dkrÞ|dkrÊtd| tƒ |røtd| tƒ n|d k	rø||krøtdtƒ d S )NTr'   zcontent-length)r   i0  Úexpiresúcontent-locationz'Entity header %r found in 304 response.z#304 responses must not have a body.éd   éÈ   éÌ   r   z/%r responses must have an empty content length.z"%r responses must not have a body.zGContent-Length and the number of bytes sent to the client do not match.)r>   r?   )r:   Úhasattrr6   r'   r;   Úsumr2   ÚgetÚintÚlowerr   r   r   r   )r   Ústatus_codeÚheadersZ
bytes_sentÚcontent_lengthÚkeyÚ_valuer   r   r   r'   ¡   s8    



 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.)r:   r   r   Ú	Exception)r   r   r   r   Ú__del__È   s    zGuardedIterator.__del__N)r   r   r   r   r&   r9   r'   rN   r   r   r   r   r5   �   s
   
'r5   c               @   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 )ÚLintMiddlewarea¡  Warns about common errors in the WSGI and HTTP behavior of the
    server and wrapped application. Some of the issues it check are:

    -   invalid status codes
    -   non-bytestrings 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)
    c             C   s
   || _ d S )N)Úapp)r   rP   r   r   r   r   é   s    zLintMiddleware.__init__c          	   C   s¼   t |ƒtk	rtdtdd� x&dD ]}||kr t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 %r 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: %rz+'PATH_INFO' does not start with a slash: %r)	rR   rS   rT   rU   rV   rW   rX   rY   rZ   )r   r   )r   Údictr   r   rE   )r   ÚenvironrK   Úscript_nameZ	path_infor   r   r   Úcheck_environì   sD            zLintMiddleware.check_environc             C   sj  t d|ƒ |jd dƒd }t|ƒdks0|jƒ  r@ttdƒdd� t|ƒdk sX|d dkrhttd	ƒdd� t|ƒ}|d
k rˆttdƒdd� t|ƒtk	r¤ttdƒdd� x‚|D ]z}t|ƒt	k	sÆt|ƒdkrÖttdƒdd� |\}}t|ƒt
k	søt|ƒt
k	�rttdƒdd� |jƒ dkrªttdƒdd� qªW |d k	�rPt|t	ƒ �rPttdƒdd� t|ƒ}| j|ƒ ||fS )NÚstatusr   r   r   z Status code must be three digits)r   rQ   ú zeInvalid value for status %r.  Valid status strings are three digits, a space and a status explanationr@   zstatus code < 100 detectedzheader list is not a listr   z Headers must tuple 2-item tupleszheader items must be stringszFThe status header is not supported due to conflicts with the CGI spec.zinvalid value for exc_info)r   Úsplitr    Úisdigitr   r   rF   r   ÚlistÚtupler   rG   Ú
isinstancer   Úcheck_headers)r   ra   rI   Úexc_inforH   ÚitemÚnameÚvaluer   r   r   Úcheck_start_response  s<    


z#LintMiddleware.check_start_responsec             C   sª   |j dƒ}|d k	rz|jdƒrB|jdƒr6ttdƒdd� |dd … }|d d… |dd …   kodd	kn  szttd
ƒdd� |j dƒ}|d k	r¦t|ƒjs¦ttdƒdd� d S )NÚetagúW/úw/z%weak etag indicator should be upcase.rQ   )r   r   r   ú"zunquoted etag emitted.Úlocationz*absolute URLs required for location header)ro   rp   éÿÿÿÿ)rE   Ú
startswithr   r   r
   Únetloc)r   rI   rn   rr   r   r   r   rh   H  s     


(

zLintMiddleware.check_headersc             C   s   t |tƒrtdtdd� d S )NzŸThe application returned astring. The response will send one character at a time to the client, which will kill performance. Return a list or iterable instead.r   )r   )rg   r   r   r   )r   Úapp_iterr   r   r   Úcheck_iteratora  s
    
zLintMiddleware.check_iteratorc                s¢   t |ƒdkrtdtdd� |r,tdtdd� |\}‰ˆj|ƒ t|d ƒ|d< t|d ƒ|d< t|d< g ‰g ‰ ‡ ‡‡‡fdd	„}ˆj||ƒ}ˆj|ƒ t	|ˆˆ ƒS )
Nr   zA WSGI app takes two arguments.)r   z+A WSGI app does not take keyword arguments.z
wsgi.inputzwsgi.errorszwsgi.file_wrapperc                 s‚   t | ƒdkr"tdt | ƒ tdd� |r0tdtƒ | d d… \}}t | ƒdkrV| d }nd }ˆj|||ƒˆd d …< tˆ|||ƒˆ ƒS )Nr   r   z1Invalid number of arguments: %s, expected 2 or 3.)r   z1'start_response' does not take keyword arguments.)r   r   )r    r   r   rm   r/   )r"   Úkwargsra   rI   ri   )r2   r;   r   Ústart_responser   r   Úchecking_start_response�  s    


z8LintMiddleware.__call__.<locals>.checking_start_response)
r    r   r   r`   r   r(   r	   rP   rw   r5   )r   r"   rx   r^   rz   rv   r   )r2   r;   r   ry   r   r4   k  s     

zLintMiddleware.__call__N)
r   r   r   r   r   r`   rm   rh   rw   r4   r   r   r   r   rO   Ò   s   ,0
rO   N)r   )r   Úwarningsr   Ú_compatr   r   r   Zdatastructuresr   Úhttpr   Zwsgir	   Úurllib.parser
   ÚImportErrorÚWarningr   r   r   Úobjectr   r(   r/   r5   rO   r   r   r   r   Ú<module>   s(   
4P