o
    j&                     @   sL  d Z ddlZddlZddlmZ G dd deZG dd deZG dd	 d	eZd
Z	e
dZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZG d d! d!eZG d"d# d#eZG d$d% d%eZG d&d' d'ZG d(d) d)ZdS )*z
HTTP parser for ASGI workers.

Provides callback-based parsing using either the fast C parser (gunicorn_h1c)
or the pure Python PythonProtocol fallback.
    N)IntEnumc                   @      e Zd ZdZdS )
ParseErrorz&Base error raised during HTTP parsing.N__name__
__module____qualname____doc__ r
   r
   I/var/www/ai-app/venv/lib/python3.10/site-packages/gunicorn/asgi/parser.pyr          r   c                   @   r   )InvalidProxyLinezInvalid PROXY protocol v1 line.Nr   r
   r
   r
   r   r      r   r   c                   @   r   )InvalidProxyHeaderz!Invalid PROXY protocol v2 header.Nr   r
   r
   r
   r   r      r   r   s   

 
QUIT
)s   host   content-length   transfer-encodings   trailers   authorizations   tec                   @   s   e Zd ZdZdZdZdS )	PPCommandzPROXY protocol v2 commands.r      N)r   r   r   r	   LOCALPROXYr
   r
   r
   r   r   -   s    r   c                   @   s    e Zd ZdZdZdZdZdZdS )PPFamilyz#PROXY protocol v2 address families.r   r         N)r   r   r   r	   UNSPECINETINET6UNIXr
   r
   r
   r   r   3   s    r   c                   @   s   e Zd ZdZdZdZdZdS )
PPProtocolz&PROXY protocol v2 transport protocols.r   r   r   N)r   r   r   r	   r   STREAMDGRAMr
   r
   r
   r   r   ;   s
    r   c                   @   r   )LimitRequestLinez&Request line exceeds configured limit.Nr   r
   r
   r
   r   r   B   r   r   c                   @   r   )LimitRequestHeadersz+Too many headers or header field too large.Nr   r
   r
   r
   r   r    F   r   r    c                   @   r   )InvalidRequestLinezInvalid request line.Nr   r
   r
   r
   r   r!   J   r   r!   c                   @   r   )InvalidRequestMethodzInvalid HTTP method.Nr   r
   r
   r
   r   r"   N   r   r"   c                   @   r   )InvalidHTTPVersionzInvalid HTTP version.Nr   r
   r
   r
   r   r#   R   r   r#   c                   @   r   )InvalidHeaderNamezInvalid header name.Nr   r
   r
   r
   r   r$   V   r   r$   c                   @   r   )InvalidHeaderzInvalid header value.Nr   r
   r
   r
   r   r%   Z   r   r%   c                   @   r   )UnsupportedTransferCodingz$Unsupported Transfer-Encoding value.Nr   r
   r
   r
   r   r&   ^   r   r&   c                   @   r   )InvalidChunkSizez0Invalid chunk size in chunked transfer encoding.Nr   r
   r
   r
   r   r'   b   r   r'   c                   @   r   )InvalidChunkExtensionz%Invalid chunk extension per RFC 9112.Nr   r
   r
   r
   r   r(   f   r   r(   c                   @   s   e Zd ZdZdZ												d(dd	Zd
d Zedd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' ZdS ))PythonProtocola  Callback-based HTTP/1.1 parser (pure Python fallback).

    Mirrors H1CProtocol interface for seamless switching between
    the C extension and pure Python implementations.

    Callbacks:
        on_message_begin: () -> None - Called when request starts
        on_url: (url: bytes) -> None - Called with request URL/path
        on_header: (name: bytes, value: bytes) -> None - Called for each header
        on_headers_complete: () -> bool - Called when headers done (return True to skip body)
        on_body: (chunk: bytes) -> None - Called with body data chunks
        on_message_complete: () -> None - Called when request is complete
    )_on_message_begin_on_url
_on_header_on_headers_complete_on_body_on_message_complete_state_buffer_headers_listmethodpathhttp_versionheaderscontent_length
is_chunkedshould_keep_aliveis_complete_body_remaining
_skip_body_chunk_state_chunk_size_chunk_remaining_limit_request_line_limit_request_fields_limit_request_field_size"_permit_unconventional_http_method#_permit_unconventional_http_version_header_count_proxy_protocol_proxy_protocol_info_proxy_protocol_doneN  d   Foffc                 C   s   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|| _
d| _|| _d | _|dk| _|dkr5dnd| _t | _g | _d | _d | _d | _g | _d | _d| _d| _d| _d| _d| _d| _d| _d| _d S )Nr   rK   proxy_protocolrequest_lineFTsize) r*   r+   r,   r-   r.   r/   r@   rA   rB   rC   rD   rE   rF   rG   rH   r0   	bytearrayr1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   )selfon_message_beginon_url	on_headeron_headers_completeon_bodyon_message_completelimit_request_linelimit_request_fieldslimit_request_field_size!permit_unconventional_http_method"permit_unconventional_http_versionrL   r
   r
   r   __init__   s>   

zPythonProtocol.__init__c                 C   s   | j | | j rL| jdkr|  sdS n2| jdkr!|  s dS n&| jdkr-|  s,dS n| jdkr9|  s8dS n| jdkrE|  sDdS ndS | j s	dS dS )zProcess data, fire callbacks synchronously.

        Args:
            data: bytes or bytearray of incoming data

        Raises:
            ParseError: If the HTTP request is malformed
        rL   rM   r6   bodychunkedN)r1   extendr0   _parse_proxy_protocol_parse_request_line_parse_headers_parse_body_parse_chunked_body)rP   datar
   r
   r   feed   s0   	




zPythonProtocol.feedc                 C   s   | j S )z%Return proxy protocol info if parsed.)rG   rP   r
   r
   r   proxy_protocol_info   s   z"PythonProtocol.proxy_protocol_infoc                 C   sn   d| _ | j  g | _d| _d| _d| _g | _d| _d| _	d| _
d| _d| _d| _d| _d| _d| _d| _dS )z#Reset for next request (keepalive).rM   NFTr   rN   )r0   r1   clearr2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   rE   rg   r
   r
   r   reset   s"   

zPythonProtocol.resetc                 C   s>   | j dkr| jdkrd| _ d| _| jr|   dS dS dS dS )zMark parsing complete for EOF handling.

        Call when no more data will be received. Handles edge cases like
        chunked encoding without final trailer CRLF.
        r^   trailercompleteTN)r0   r=   r:   r/   rg   r
   r
   r   finish   s   zPythonProtocol.finishc                 C   sl   t | jdk r	dS | j}|dv r| jdd tkr|  S |dv r.| jdd dkr.|  S d| _d	| _dS )
zParse PROXY protocol header if enabled.

        Returns True if parsing is complete (or not applicable),
        False if more data is needed.
           F)v2autoN)v1rp      s   PROXY TrM   )lenr1   rF   PP_V2_SIGNATURE_parse_proxy_protocol_v2_parse_proxy_protocol_v1rH   r0   )rP   moder
   r
   r   r`     s   z$PythonProtocol._parse_proxy_protocolc              
   C   s  | j d}|dkrt| j dkrtddS t| j d| d}| j d|d = |d	}t|dk r:td
|d  }|dkrNdddddd| _n|dv rt|dkr^td| |d }|d }|dkrmt	j
nt	j}zt	|| t	|| W n ttfy   td| w zt|d }t|d }	W n ty }
 ztd|
 d}
~
ww d|  krdkrn tdd|	  krdkstd td|||||	d| _ntd| d| _d| _dS )z|Parse PROXY protocol v1 (text format).

        Format: PROXY <PROTO> <SRC_ADDR> <DST_ADDR> <SRC_PORT> <DST_PORT>

           
k   zPROXY v1 header too longFNlatin-1r    zInvalid PROXY v1 liner   UNKNOWNrL   client_addrclient_port
proxy_addr
proxy_port)TCP4TCP6rr   zInvalid PROXY v1 line for %sr   r   zInvalid PROXY v1 %s address      zInvalid PROXY v1 port: %sr   i  zInvalid PROXY v1 port rangezUnknown PROXY v1 protocol: %sTrM   )r1   findrs   r   bytesdecodesplitupperrG   socketAF_INETAF_INET6	inet_ptonOSError
ValueErrorintrH   r0   )rP   idxlinepartsprotos_addrd_addrafs_portd_porter
   r
   r   rv     sj   


z'PythonProtocol._parse_proxy_protocol_v1c                 C   s  t | jdk r	dS | jd }| jd }tdt| jdd d }|d@ d	? }|d
kr2td| |d@ }|tjtjfvrDtd| d| }t | j|k rQdS t| jd| }| jd|= |tjkrvdddddd| _	d| _
d| _dS |d@ d	? }|d@ }	|tjtjfv r|	tjkrtd|tjkrt |dk rtdddd |dd	 D }
ddd |d	d D }td|dd d }td|dd d }d}np|tjkr*t |dk rtdtd|dd }td|dd }d d!d |D }
d d"d |D }td|dd# d }td|d#d d }d$}n|tjkrAd%ddddd| _	d| _
d| _dS td&| ||
|||d| _	d| _
d| _dS )'z(Parse PROXY protocol v2 (binary format).   Frn      z>H   r      r   r   z Unsupported PROXY v2 version: %d   z Unsupported PROXY v2 command: %dNr   r~   TrM   z1PROXY v2: only TCP (STREAM) protocol is supportedz"Invalid PROXY v2 IPv4 address data.c                 s       | ]}t |V  qd S Nstr.0br
   r
   r   	<genexpr>      z:PythonProtocol._parse_proxy_protocol_v2.<locals>.<genexpr>c                 s   r   r   r   r   r
   r
   r   r     r      
   r   $   z"Invalid PROXY v2 IPv6 address dataz>8H    :c                 s       | ]}d | V  qdS z%xNr
   r   wr
   r
   r   r     r   c                 s   r   r   r
   r   r
   r
   r   r     r   "   r   r   z'Unsupported PROXY v2 address family: %d)rs   r1   structunpackr   r   r   r   r   rG   rH   r0   r   r   r   r   r   joinr   )rP   ver_cmdfam_protlengthversioncommand
total_size	addr_datafamilyprotocolr   r   r   r   r   s_wordsd_wordsr
   r
   r   ru   f  s   



z'PythonProtocol._parse_proxy_protocol_v2c                 C   s  | j d}|dkrdS | jdkr|| jkrtdt| j d| }| j d|d = |dd}t|d	kr;td
|d | _|d | _	| j
sV| | jsVt| jd| j	dkrd| jdkrdtd
| jdkr}| j	dkr}| j	ds}d| j	vr}td
|d }|dkrd| _n<|dkrd| _n4| jst|d|drzt|dd }d|f| _W n ty   t|dw t|d| jr|   | jr| | j	 d| _dS )z,Parse request line, return True if complete.rx   ry   Fr   zRequest line is too largeNr       r   zInvalid request liner   r{      *s   OPTIONSs   CONNECT   /s   ://s   HTTP/1.1r   r   s   HTTP/1.0r   r   s   HTTP/1.   r6   T)r1   r   r@   r   r   r   rs   r!   r3   r4   rC   _is_valid_methodr"   r   
startswithr5   rD   r#   r   r   r*   r+   r0   )rP   r   r   r   r   minorr
   r
   r   ra     sX   






z"PythonProtocol._parse_request_linec                 C   s0  	 | j d}|dkrdS t| j d| }| j d|d = |s&|   dS | jdkr8t|d | jkr8td|  jd	7  _| jdkrN| j| jkrNtd
|d}|dkr[t	d|d| 
 }| |sot|d||d	 d 
 }| |rt	d| }| j||f | jr| || q)z3Parse headers, return True if headers are complete.Trx   ry   FNr   r   z!Request header field is too larger   zToo many headers   :zMissing colon in headerr{   z"Invalid characters in header value)r1   r   r   _finalize_headersrB   rs   r    rE   rA   r%   strip_is_valid_tokenr$   r   _has_invalid_header_charslowerr2   appendr,   )rP   r   r   colonnamevalue
name_lowerr
   r
   r   rb     s8   


zPythonProtocol._parse_headersc           
   	   C   sD  | j | _d}d}| jD ]\}}|dkr8|durtdzt|}W n ty,   tdw |dk r5td|}q|dkr|d	d
 |dD }|D ]2}| }|dkr[|rXtdd}qH|dkrf|retdqH|dv rt|rptdd| _qHt|	dq|dkr| }d|v rd| _qd|v rd| _q|r| j
dk rtd|durtdd| _d| _d| _n|dur|| _|| _nd| _d| _| j
dkr| jrtdd | jD }	|	sd| _| jr|  | _| jrd| _d| _| jr|   dS dS | jrd| _d| _dS | jr| jdkrd | _dS d| _d| _| jr |   dS dS )!ay  Called when all headers received.

        Validates headers for request smuggling vulnerabilities:
        - Rejects duplicate Content-Length headers
        - Rejects requests with both Content-Length and Transfer-Encoding
        - Rejects chunked Transfer-Encoding in HTTP/1.0
        - Rejects stacked chunked encoding
        - Validates Transfer-Encoding values
        NFr   zDuplicate Content-Length headerzInvalid Content-Length valuer   zNegative Content-Lengthr   c                 S   s   g | ]}|  qS r
   )r   )r   vr
   r
   r   
<listcomp>[  s    z4PythonProtocol._finalize_headers.<locals>.<listcomp>   ,s   chunkedzStacked chunked encodingTs   identityz'Invalid Transfer-Encoding after chunked)s   compresss   deflates   gzipr{   
   connections   close
   keep-aliver   z(Chunked encoding not allowed in HTTP/1.0z%Content-Length with Transfer-Encodingry   r   c                 s   s(    | ]\}}|d kod|  v V  qdS )r   r   N)r   )r   r   r   r
   r
   r   r     s
    
z3PythonProtocol._finalize_headers.<locals>.<genexpr>rl   r^   rN   r]   )r2   r6   r%   r   r   r   r   r9   r&   r   r5   r8   r7   r;   anyr-   r<   r0   r:   r/   r=   )
rP   r7   r^   r   r   cl_valuevalsval	val_lowerhas_keepaliver
   r
   r   r   ;  s   




z PythonProtocol._finalize_headersc                 C   s   | j r| jdkr
dS tt| j | j}t| j d| }| j d|= |  j|8  _| jr1| | | jdkrCd| _d| _| jrC|   dS )z$Parse Content-Length delimited body.r   FNrl   T)	r1   r;   minrs   r   r.   r0   r:   r/   )rP   
chunk_sizechunkr
   r
   r   rc     s   

zPythonProtocol._parse_bodyc                 C   sj  | j r3| jdkr| j d}|dkrdS t| j d| }| j d|d = |d}|dkrE||d d }d	|v r?td
|d| }|| krOtd|sUtd|D ]
}|dvratdqWzt|d| _W n t	yu   tdw | jdkrd| _n| j| _
d| _n| jdkr| j sdS tt| j | j
}t| j d| }| j d|= |  j
|8  _
| jr| | | j
dkrd| _no| jdkrt| j dk rdS | j dd= d| _nW| jdkr0| j d}|dkrdS t| j d| }| j d|d = |sd| _d| _| jr|   dS |d}	|	dkr0|d|	 d }
|
tv r0t|
d| j sdS )z Parse chunked transfer encoding.rN   rx   ry   FNr      ;r      zbare CR not allowedzWhitespace in chunk sizezEmpty chunk sizes   0123456789abcdefABCDEFzInvalid character in chunk sizer   zInvalid chunk sizer   rk   re   crlfrl   Tr   s    	r{   )r1   r=   r   r   r(   r   r'   r   r>   r   r?   r   rs   r.   r0   r:   r/   r   RFC9110_6_5_1_FORBIDDEN_TRAILERr$   r   )rP   r   	size_line	semicolon	chunk_extcto_readr   r   r   r   r
   r
   r   rd     s   









\z"PythonProtocol._parse_chunked_bodyc                 C   sJ   |sdS dt |  krdksdS  dS |D ]	}|dv r dS q| |S )z>Check if method is valid token with conventional restrictions.Fr      s   abcdefghijklmnopqrstuvwxyz#)rs   r   )rP   r3   r   r
   r
   r   r     s   
zPythonProtocol._is_valid_methodc                 C   s:   |sdS |D ]}|dk s|dkr dS |dv r dS qdS )z6Check if data contains only RFC 9110 token characters.F!   ~   s   "(),/:;<=>?@[\]{}"Tr
   )rP   re   r   r
   r
   r   r   ,  s   zPythonProtocol._is_valid_tokenc                 C   s:   |D ]}|dksd|  krdksn |dkr dS qdS )zARFC 9110 section 5.5: only VCHAR, SP, HTAB, and obs-text allowed.r   r         TFr
   )rP   r   r   r
   r
   r   r   8  s
   &z(PythonProtocol._has_invalid_header_chars)NNNNNNrI   rJ   rI   FFrK   )r   r   r   r	   	__slots__r\   rf   propertyrh   rj   rm   r`   rv   ru   ra   rb   r   rc   rd   r   r   r   r
   r
   r
   r   r)   j   s@    
;
Gi@,o`r)   c                   @   s:   e Zd ZdZdZdd ZedddZdd	 Zd
d Z	dS )CallbackRequestzxRequest object built from callback parser state.

    Works with both H1CProtocol (C extension) and PythonProtocol.
    r3   urir4   queryfragmentr   r6   headers_bytesschemeraw_pathr7   r^   
must_closerh   _expect_100_continuec                 C   s^   d | _ d | _d | _d | _d | _d | _g | _g | _d| _d| _	d| _
d| _d| _d | _d| _d S )Nhttp    r   Fr   rg   r
   r
   r   r\   M  s   
zCallbackRequest.__init__Fc           
      C   s*  ddl m} |  }|jd|_|j}d|v r3|dd\}}||_||jddd|_|d	|_n||_||jddd|_d
|_|d	|_d
|_	|j
|_tt|ddpY|j|_dd |jD |_|ridnd|_|jppd|_|j|_|j |_|jD ]\}}	|dkr|	 dkrd|_ |S q~|S )zBuild request from callback parser state.

        Args:
            parser: H1CProtocol or PythonProtocol instance
            is_ssl: Whether connection is SSL/TLS

        Returns:
            CallbackRequest instance
        r   )unquote_to_bytesascii   ?r   zutf-8replace)errorsr{    asgi_headersNc                 S   s(   g | ]\}}| d  | d fqS )r{   )r   r   )r   nr   r
   r
   r   r     s    z/CallbackRequest.from_parser.<locals>.<listcomp>httpsr   s   expects   100-continueT)urllib.parser  r3   r   r4   r   r   r   r   r   r5   r   listgetattrr6   r   r   r7   r8   r^   r9   r   r   r   )
clsparseris_sslr  reqraw_url	path_part
query_partr   r   r
   r
   r   from_parser^  s<   
 zCallbackRequest.from_parserc                 C   sZ   | j rdS | jD ]\}}|dkr'| d}|dkr dS |dkr% dS  nq| jdkS )z8Check if connection should be closed after this request.T
CONNECTIONz 	closez
keep-aliveFr   )r   r6   r   r   r   )rP   r   r   r   r
   r
   r   should_close  s   
zCallbackRequest.should_closec                 C   s,   |  }| jD ]\}}||kr|  S qdS )z.Get a header value by name (case-insensitive).N)r   r6   )rP   r   hr   r
   r
   r   
get_header  s   zCallbackRequest.get_headerN)F)
r   r   r   r	   r   r\   classmethodr  r  r  r
   r
   r
   r   r   @  s    8r   )r	   r   r   enumr   	Exceptionr   r   r   rt   	frozensetr   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r   r
   r
   r
   r   <module>   s:   
     [