U
    /v¶hŽ8  ã                   @  s  d Z ddlm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ddd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
é    )ÚannotationsN)Ú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-unpacked-wheel-c3xdgso7/werkzeug/middleware/lint.pyr   !   s   r   c                   @  s   e Zd ZdZdS )ÚHTTPWarningz Warning class for HTTP warnings.Nr   r   r   r   r   r   %   s   r   ÚstrÚobjectÚtypeÚNone)Ú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)r   r   r   r   )r   r   r   r   r   r   Ú
check_type)   s    ýr"   c                   @  sX   e Zd Zdddœdd„Zdddœd	d
„Zdddœdd„Zddœdd„Zddœdd„ZdS )ÚInputStreamzt.IO[bytes]r   ©Ústreamr   c                 C  s
   || _ d S ©N©Ú_stream©Úselfr%   r   r   r   Ú__init__3   s    zInputStream.__init__út.AnyÚbytes)Ú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   r1   6   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()'.)r0   r   r   Ú	TypeErrorr(   Úreadliner2   r   r   r   r4   G   s    üüzInputStream.readlinezt.Iterator[bytes]©r   c                 C  s<   zt | jƒW S  tk
r6   tdtdd� t dƒ Y S X d S )Nz'wsgi.input' is not iterable.r   r    r   )Úiterr(   r3   r   r   ©r*   r   r   r   Ú__iter__Z   s
    zInputStream.__iter__c                 C  s   t dtdd� | j ¡  d S )Nz(The application closed the input stream!r   r    ©r   r   r(   Úcloser7   r   r   r   r:   a   s    zInputStream.closeN)r   r   r   r+   r1   r4   r8   r:   r   r   r   r   r#   2   s
   r#   c                   @  sX   e Zd Zdddœdd„Zdddœdd	„Zdd
œdd„Zdddœdd„Zdd
œdd„ZdS )ÚErrorStreamz	t.IO[str]r   r$   c                 C  s
   || _ d S r&   r'   r)   r   r   r   r+   g   s    zErrorStream.__init__r   ©Úsr   c                 C  s   t d|tƒ | j |¡ d S )Nzwsgi.error.write())r"   r   r(   Úwrite©r*   r=   r   r   r   r>   j   s    zErrorStream.writer5   c                 C  s   | j  ¡  d S r&   )r(   Úflushr7   r   r   r   r@   n   s    zErrorStream.flushzt.Iterable[str])Úseqr   c                 C  s   |D ]}|   |¡ qd S r&   )r>   )r*   rA   Úliner   r   r   Ú
writelinesq   s    zErrorStream.writelinesc                 C  s   t dtdd� | j ¡  d S )Nz(The application closed the error stream!r   r    r9   r7   r   r   r   r:   u   s    zErrorStream.closeN)r   r   r   r+   r>   r@   rC   r:   r   r   r   r   r;   f   s
   r;   c                   @  s.   e Zd Zddddœdd„Zdddœd	d
„ZdS )ÚGuardedWritezt.Callable[[bytes], object]ú	list[int]r   )r>   Úchunksr   c                 C  s   || _ || _d S r&   )Ú_writeÚ_chunks)r*   r>   rF   r   r   r   r+   {   s    zGuardedWrite.__init__r-   r<   c                 C  s*   t d|tƒ |  |¡ | j t|ƒ¡ d S )Nzwrite())r"   r-   rG   rH   Úappendr0   r?   r   r   r   Ú__call__   s    
zGuardedWrite.__call__N)r   r   r   r+   rJ   r   r   r   r   rD   z   s   rD   c                   @  sX   e Zd Zdddddœdd„Zd dœd	d
„Zddœdd„Zddœdd„Zddœdd„ZdS )ÚGuardedIteratorút.Iterable[bytes]útuple[int, Headers]rE   r   )ÚiteratorÚheaders_setrF   r   c                 C  s(   || _ t|ƒj| _d| _|| _|| _d S )NF)Ú	_iteratorr6   Ú__next__Ú_nextÚclosedrO   rF   )r*   rN   rO   rF   r   r   r   r+   †   s
    zGuardedIterator.__init__r5   c                 C  s   | S r&   r   r7   r   r   r   r8   ’   s    zGuardedIterator.__iter__r-   c                 C  sP   | j rtdtdd� |  ¡ }| js0tdtdd� td|tƒ | j t	|ƒ¡ |S )Nz Iterated over closed 'app_iter'.r   r    z8The application returned before it started the response.zapplication iterator items)
rS   r   r   rR   rO   r"   r-   rF   rI   r0   )r*   Úrvr   r   r   rQ   •   s    ýzGuardedIterator.__next__c                 C  s  d| _ t| jdƒr| j ¡  | j�r| j\}}t| jƒ}|jdtd�}|dkrž|D ]6\}}| 	¡ }|dkrRt
|ƒrRtd|›d�td	d
� qR|rœtdtd	d
� nvd|  kr²dk s¼n |dkrò|dkrØt|› d�td	d
� |rðt|› d�td	d
� n"|d k	�r||k�rtdtd	d
� d S )NTr:   zcontent-length)r   i0  )Úexpireszcontent-locationzEntity header z found in 304 response.r   r    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.)rS   ÚhasattrrP   r:   rO   ÚsumrF   ÚgetÚintÚlowerr   r   r   r   )r*   Ústatus_codeÚheadersZ
bytes_sentÚcontent_lengthÚkeyÚ_valuer   r   r   r:   ¦   sV    



ÿ
ýýýýüzGuardedIterator.closec                 C  s2   | j s.ztdtdd� W n tk
r,   Y nX d S )Nz4Iterator was garbage collected before it was closed.r   r    )rS   r   r   Ú	Exceptionr7   r   r   r   Ú__del__×   s    ý
zGuardedIterator.__del__N)r   r   r   r+   r8   rQ   r:   rd   r   r   r   r   rK   …   s
   1rK   c                   @  sv   e Zd ZdZdddœdd„Zdddœd	d
„Zdddddœdd„Zdddœdd„Zdddœdd„Zddd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   r   )Úappr   c                 C  s
   || _ d S r&   )rf   )r*   rf   r   r   r   r+   ú   s    zLintMiddleware.__init__r   )Úenvironr   c                 C  sÀ   t |ƒtk	rtdtdd� dD ]"}||krtd|›d�tdd� q|d d	kr\td
tdd� | dd¡}| 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_METHODZSERVER_NAMEZSERVER_PORTúwsgi.versionú
wsgi.inputúwsgi.errorszwsgi.multithreadzwsgi.multiprocesszwsgi.run_oncezRequired environment key z
 not foundr   rj   )r/   r   z"Environ is not a WSGI 1.0 environ.ZSCRIPT_NAMEÚ Z	PATH_INFOr   ú/z+'SCRIPT_NAME' does not start with a slash: z)'PATH_INFO' does not start with a slash: )r   Údictr   r   r[   )r*   rg   ra   Úscript_nameZ	path_infor   r   r   Úcheck_environý   s:    ý
ýýýzLintMiddleware.check_environr   zlist[tuple[str, str]]z?None | tuple[type[BaseException], BaseException, TracebackType]rM   )Ústatusr_   Úexc_infor   c           
      C  s\  t d|tƒ | d d¡d }t|ƒdks0| ¡ s>tdtdd� t|ƒdk sV|d dkrltd	|›d
�tdd� t|ƒ}|dk rŠtdtdd� t|ƒt	k	r¤tdtdd� |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� | ¡ dkr¨tdtdd� q¨|d k	�rBt|t
ƒ�sBtdtdd� t|ƒ}	|  |	¡ ||	fS )Nrr   r/   r   r   z!Status code must be three digits.r    rh   ú zInvalid value for status zJ. Valid status strings are three digits, a space and a status explanation.rV   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"   r   Úsplitr0   Ú	isdecimalr   r   r\   r   ÚlistÚtupler]   Ú
isinstancer   Úcheck_headers)
r*   rr   r_   rs   Zstatus_code_strr^   ÚitemÚnameÚvalueZheaders_objr   r   r   Úcheck_start_response)  sH    
ü  ÿü
z#LintMiddleware.check_start_responser   )r_   r   c                 C  s¢   |  d¡}|d k	rt| d¡r@| d¡r4tdtdd� |dd … }|d d… |d	d …   krdd
kstn tdtdd� |  d¡}|d k	ržt|ƒjsžtdtdd� d S )NÚetag)zW/úw/r€   z)Weak etag indicator should be upper case.rh   r    r   r/   éÿÿÿÿú"zUnquoted etag emitted.Úlocationz+Absolute URLs required for location header.)r[   Ú
startswithr   r   r   Únetloc)r*   r_   r   rƒ   r   r   r   rz   ]  s(    


ý&

ýzLintMiddleware.check_headersrL   )Úapp_iterr   c                 C  s   t |tƒrtdtdd� d S )Nz The application returned a string. The response will send one character at a time to the client, which will kill performance. Return a list or iterable instead.r   r    )ry   r   r   r   )r*   r†   r   r   r   Úcheck_iteratorx  s    
ûzLintMiddleware.check_iteratorr,   ©r.   Úkwargsr   c                   sÎ   t |ƒdkrtdtdd� |r,tdtdd� |d }|d ‰ˆ |¡ t|d ƒ|d< t|d ƒ|d< t|d	< g ‰g ‰ d
d
ddœ‡ ‡‡‡fdd„}ˆ |t 	d|¡¡}ˆ 
|¡ t|t 	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/   rk   rl   zwsgi.file_wrapperr,   zt.Callable[[bytes], None]rˆ   c                    sˆ   t | ƒdkr&tdt | ƒ› d�tdd� |r8tdtdd� | d }| d }t | ƒd	kr\| d nd }ˆ |||¡ˆd d …< tˆ|||ƒˆ ƒS )
N>   r   r   zInvalid number of arguments: z, expected 2 or 3.r   r    z1'start_response' does not take keyword arguments.r   r/   r   )r0   r   r   r~   rD   )r.   r‰   rr   r_   rs   ©rF   rO   r*   Zstart_responser   r   Úchecking_start_response™  s$    ýýþz8LintMiddleware.__call__.<locals>.checking_start_responser
   )r0   r   r   rq   r#   r;   r	   rf   ÚtÚcastr‡   rK   ÚTupler\   r   )r*   r.   r‰   rg   r‹   r†   r   rŠ   r   rJ   ‚  s0      ÿ

  ÿzLintMiddleware.__call__N)
r   r   r   r   r+   rq   r~   rz   r‡   rJ   r   r   r   r   re   ã   s   ,4
re   ) r   Ú
__future__r   ÚtypingrŒ   Útypesr   Úurllib.parser   Úwarningsr   Zdatastructuresr   Úhttpr   Zwsgir	   ÚTYPE_CHECKINGZ_typeshed.wsgir
   r   r   ÚWarningr   r   r   r"   r#   r;   rD   rK   re   r   r   r   r   Ú<module>   s(   	4^