
_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   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   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)NzThe 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