
Á«‘_§2  ã               @   sb  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 Wn" e k
 r® d d
 l m Z Yn XGd d „  d e ƒ Z Gd d „  d e ƒ Z d d d „ Z Gd d „  d e ƒ Z Gd d „  d e ƒ Z Gd d „  d e ƒ Z e Gd d „  d e ƒ ƒ Z Gd 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-5gj8f0j9/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   s6   t  | ƒ t k	 r2 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               @   sL   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   s]   t  | ƒ d k r( t d t d d ƒn% t  | ƒ d k rM t 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 ƒn4 t  | ƒ d k rP t 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   sG   y t  |  j ƒ SWn/ t k
 rB t d t d d ƒt  f  ƒ SYn Xd  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               @   sL   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 Wd  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   s7   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               @   sL   e  Z d  Z d d „  Z d d „  Z d d „  Z d d „  Z d	 d
 „  Z d S)ÚGuardedIteratorc             C   sU   | |  _  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   sk   |  j  r t d t d d ƒ|  j ƒ  } |  j sD t 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   sZ  d |  _  t |  j d ƒ r( |  j j ƒ  |  j rV|  j \ } } t |  j ƒ } | j d d t ƒ} | d k rÒ xI | D]A \ } } | j	 ƒ  } | d k rw t
 | ƒ rw t d | t ƒ qw W| rVt d	 t ƒ n„ d
 | k oé d k  n sú | d k r1| d k rt d | t ƒ | rVt d | t ƒ n% | d  k	 rV| | k rVt d t ƒ d  S)NTr'   zcontent-lengthr   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_sentZcontent_lengthÚkeyÚ_valuer   r   r   r'   ¡   s8    			(zGuardedIterator.closec             C   s3   |  j  s/ y t d t ƒ Wn t k
 r. Yn Xd  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'   rM   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   rO   r   r   r   r   é   s    zLintMiddleware.__init__c          	   C   sþ   t  | ƒ t k	 r% t d t d d ƒx1 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)	rQ   rR   rS   rT   rU   rV   rW   rX   rY   )r   r   )r   Údictr   r   rE   )r   ÚenvironrJ   Úscript_nameZ	path_infor   r   r   Úcheck_environì   sD            
zLintMiddleware.check_environc             C   sô  t  d | ƒ | j d  d ƒ d } t | ƒ d k sB | j ƒ  rX t t d ƒ d d ƒt | ƒ d k  sz | d d k r� t t d	 ƒ d d ƒt | ƒ } | d
 k  r¾ t t d ƒ d d ƒt | ƒ t k	 ræ t t d ƒ d d ƒx¶ | D]® } t | ƒ t	 k	 st | ƒ d k r-t t d ƒ d d ƒ| \ } } t | ƒ t
 k	 s]t | ƒ t
 k	 rst t d ƒ d d ƒ| j ƒ  d k rí t t d ƒ d d ƒqí W| d  k	 rÑt | t	 ƒ rÑt t d ƒ d d ƒt | ƒ } |  j | ƒ | | f S)NÚstatusr   r   r   z Status code must be three digitsr   rP   ú 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   r`   rI   Úexc_inforH   ÚitemÚnameÚvaluer   r   r   Úcheck_start_response  s<    "	$$	z#LintMiddleware.check_start_responsec             C   sé   | j  d ƒ } | d  k	 r¥ | j d ƒ r_ | j d ƒ rO t t d ƒ d d ƒ| d d  … } | d  d … | d d  … k oŠ d	 k n s¥ t 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.r   rP   r   r   ú"zunquoted etag emitted.Úlocationz*absolute URLs required for location header)rn   ro   éÿÿÿÿ)rE   Ú
startswithr   r   r
   Únetloc)r   rI   rm   rq   r   r   r   rg   H  s     0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   )rf   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 ƒ| rB t d t ƒ |  d  d … \ } } t  |  ƒ d k rw |  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   rl   r/   )r"   Úkwargsr`   rI   rh   )r2   r;   r   Ústart_responser   r   Úchecking_start_response�  s    z8LintMiddleware.__call__.<locals>.checking_start_response)
r    r   r   r_   r   r(   r	   rO   rv   r5   )r   r"   rw   r]   ry   ru   r   )r2   r;   r   rx   r   r4   k  s     
zLintMiddleware.__call__N)
r   r   r   r   r   r_   rl   rg   rv   r4   r   r   r   r   rN   Ò   s   ,0
rN   N)r   Úwarningsr   Ú_compatr   r   r   Zdatastructuresr   Úhttpr   Zwsgir	   Úurllib.parser
   ÚImportErrorÚWarningr   r   r   Úobjectr   r(   r/   r5   rN   r   r   r   r   Ú<module>   s(   4P