o
    iHa                     @   s   d Z ddlZddlZddlZddlZddlZddlmZmZm	Z	m
Z
mZmZmZmZmZmZmZmZmZmZmZ ddlmZmZmZmZ ddlmZmZ dZdZdZ dZ!e"d	e#e! Z$e"d
Z%e"dZ&e"dZ'dd Z(G dd dZ)dS )z
Async version of gunicorn/http/message.py for ASGI workers.

Reuses the parsing logic from the sync version, adapted for async I/O.
    N)ExpectationFailedInvalidHeaderInvalidHeaderName
NoMoreDataInvalidRequestLineInvalidRequestMethodInvalidHTTPVersionLimitRequestLineLimitRequestHeadersUnsupportedTransferCodingObsoleteFoldingInvalidProxyLineInvalidProxyHeaderForbiddenProxyRequestInvalidSchemeHeaders)PP_V2_SIGNATURE	PPCommandPPFamily
PPProtocol)bytes_to_strsplit_request_urii  i   z!#$%&'*+-.^_`|~z[%s0-9a-zA-Z]+z[a-z#]zHTTP/(\d)\.(\d)z[\0\r\n]c                 C   sL   d|v rdS zt | }W n
 ty   Y dS w |D ]	}||v r# dS qdS )zCheck if IP address is in the allow list.

    Args:
        ip_str: The IP address string to check
        allow_list: The original allow list (strings, may contain "*")
        networks: Pre-computed ipaddress.ip_network objects from config
    *TF)	ipaddress
ip_address
ValueError)ip_str
allow_listnetworksipnetwork r    I/home/ubuntu/.local/lib/python3.10/site-packages/gunicorn/asgi/message.py_ip_in_allow_list+   s   r"   c                   @   s   e Zd ZdZd3ddZed3ddZdd Zd	d
 Zd4ddZ	dd Z
dd Zdd Zdd Zdd Zd5ddZdd Zdd Zdd  Zd!d" Zd6d$d%Zd&d' Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2S )7AsyncRequestzAsync HTTP request parser.

    Parses HTTP/1.x requests using async I/O, reusing gunicorn's
    parsing logic where possible.
       c                 C   s  || _ || _|| _|| _|| _d | _d | _d | _d | _d | _	d | _
g | _g | _|jr,dnd| _d| _d| _d | _|j| _| jdk sF| jtkrIt| _|j| _| jdksW| jtkrZt| _|j| _| jdk rft| _| jpjt}| j|d  d | _d | _d| _d | _d| _d S )NhttpshttpFr         )cfgunreader	peer_addrremote_addr
req_numberversionmethoduripathqueryfragmentheaderstrailersis_sslscheme
must_close_expected_100_continueproxy_protocol_infolimit_request_lineMAX_REQUEST_LINElimit_request_fieldsMAX_HEADERSlimit_request_field_sizeDEFAULT_MAX_HEADERFIELD_SIZEmax_buffer_headerscontent_lengthchunked_body_reader_body_remaining)selfr)   r*   r+   r-   max_header_field_sizer    r    r!   __init__F   sL   






zAsyncRequest.__init__c                    s"   | ||||}|  I dH  |S )a  Parse an HTTP request from the stream.

        Args:
            cfg: gunicorn config object
            unreader: AsyncUnreader instance
            peer_addr: client address tuple
            req_number: request number on this connection (for keepalive)

        Returns:
            AsyncRequest: Parsed request object

        Raises:
            NoMoreData: If no data available
            Various parsing errors for malformed requests
        N)_parse)clsr)   r*   r+   r-   reqr    r    r!   parsev   s   zAsyncRequest.parsec                    s   t  }| |I dH  | jj}|dkr"| jdkr"| ||I dH }| || jI dH \}}| | t	|}	 |
d}|dd dk}|dk rc|sc| |I dH  t	|}t|| jkrbtd	nnq8|rr| j|dd  n| j|d| d
d| _| j||d d  |   dS )z$Parse the request from the unreader.Noffr$   T   

r'      
r   zmax buffer headersF)from_trailerr(   )	bytearray
_read_intor)   proxy_protocolr-   _handle_proxy_protocol
_read_liner;   _parse_request_linebytesfindlenrA   r
   r*   unread_parse_headersr4   _set_body_reader)rF   bufmodelinedataidxdoner    r    r!   rI      s2   

zAsyncRequest._parsec                    s0   | j  I dH }|stt||| dS )z7Read data from unreader and append to bytearray buffer.N)r*   readr   rW   extendrF   r]   r`   r    r    r!   rR      s
   zAsyncRequest._read_intor   c                    s   t |}	 |d}|dkr!||  krdkr n nt||n$t|d |  kr/dkr8n ntt||| |I dH  t |}q|d| t||d d fS )z<Read a line from buffer, returning (line, remaining_buffer).TrO   r   r'   N)rW   rX   r	   rY   rR   rQ   )rF   r]   limitr`   ra   r    r    r!   rU      s   

  zAsyncRequest._read_linec                    s   t |dk r| |I dH  t |dk s|dv r-|dd tkr-|   | |I dH S |dv rE|dd dkrE|   | |I dH S |S )ztHandle PROXY protocol detection and parsing.

        Returns the buffer with proxy protocol data consumed.
           N)v2auto)v1ri      s   PROXY )rY   rR   r   _proxy_protocol_access_check_parse_proxy_protocol_v2_parse_proxy_protocol_v1)rF   r]   r^   r    r    r!   rT      s   z#AsyncRequest._handle_proxy_protocolc                 C   s>   t | jtrt| jd | jj| j st| jd dS dS )z2Check if proxy protocol is allowed from this peer.r   N)
isinstancer+   tupler"   r)   proxy_allow_ipsproxy_allow_networksr   rF   r    r    r!   rl      s   z)AsyncRequest._proxy_protocol_access_checkc                    s  t |}d|vr| |I dH  t |}d|vs	|d}t|d| }t||d d }|d}t|dkr?t||d }|d }|d }	|dvrUtd	| |d
kruzt	tj
| t	tj
|	 W n+ tyt   t|w |dkrzt	tj| t	tj|	 W n ty   t|w zt|d }
t|d }W n ty   td| w d|
  krdkrn nd|  krdksn td| |||
|	|d| _|S )z`Parse PROXY protocol v1 (text format).

        Returns buffer with v1 header consumed.
        rO   Nr'    rk   r$      )TCP4TCP6zprotocol '%s' not supportedrv   rw   r(      zinvalid port %sr   i  rS   client_addrclient_port
proxy_addr
proxy_port)rW   rR   rX   r   rQ   splitrY   r   socket	inet_ptonAF_INETOSErrorAF_INET6intr   r:   )rF   r]   r`   ra   r_   	remainingbitsprotos_addrd_addrs_portd_portr    r    r!   rn      s\   

.z%AsyncRequest._parse_proxy_protocol_v1c                    s  t |dk r| |I dH  t |dk s|d }|d }tdt|dd d }|d@ d	? }|d
kr;td| |d@ }|tjtjfvrMtd| d| }t ||k re| |I dH  t ||k sW|tjkr{dddddd| _	t
||d S |d@ d	? }|d@ }	|	tjkrtdt|dd|  }
|tjkr|dk rtdttj|
dd	 }ttj|
d	d }td|
dd d }td|
dd d }d}n\|tjkr|dk rtdttj|
dd }ttj|
dd }td|
dd d }td|
dd d }d}n|tjkr,dddddd| _	t
||d S td| |||||d| _	t
||d S )zbParse PROXY protocol v2 (binary format).

        Returns buffer with v2 header consumed.
           Nrg      z>H   r      r(   r'   zunsupported version %d   zunsupported command %dLOCALry   zonly TCP protocol is supportedz"insufficient address data for IPv4   
   rv   $   z"insufficient address data for IPv6    "   rw   UNSPECzunsupported address family %d)rY   rR   structunpackrW   r   r   r   PROXYr:   rQ   r   STREAMr   INETr   	inet_ntopr   INET6r   r   )rF   r]   ver_cmd	fam_protolengthr.   commandtotal_header_sizefamilyprotocol	addr_datar   r   r   r   r   r    r    r!   rm   !  s|   


z%AsyncRequest._parse_proxy_protocol_v2c                 C   s  dd | ddD }t|dkrtt||d | _| jjs?t| jr+t	| jdt|d   kr9dks?n t	| jt
| jsJt	| j| jjrT| j | _|d | _t| jdkrftt|zt| j}W n tyz   tt|w |jpd	| _|jpd	| _|jpd	| _t|d }|d
u rt|d t|dt|df| _d| j  krdk sn | jjst| jd
S d
S )zParse the HTTP request line.c                 S      g | ]}t |qS r    r   ).0bitr    r    r!   
<listcomp>      z4AsyncRequest._parse_request_line.<locals>.<listcomp>    r'   ru   r      r$    Nr$   r   )r'   r   )r~   rY   r   r   r/   r)   !permit_unconventional_http_methodMETHOD_BADCHAR_REsearchr   TOKEN_RE	fullmatchcasefold_http_methodupperr0   r   r   r1   r2   r3   
VERSION_REr   r   groupr.   "permit_unconventional_http_version)rF   
line_bytesr   partsmatchr    r    r!   rV   ~  sB   





z AsyncRequest._parse_request_lineFc                 C   s  | j }g }dd |dD }d}i }g }|rnt| jtr*t| jd |j| r0|j}|j	}|rBt
|| jkr>td|d}	t
|	t
d }
|	ddkrVt|	|	dd	\}}| j jrg|d
}t|spt|| }|d
g}|r|d dr| j jst||d}	|
t
|	t
d 7 }
|
| j  krdkrtd ||	d |r|d dsd|}t|rt||
| j  krdkrtd |s|dkr| dkr| j dk rnd| _!nt"|||v r||| k}|rdnd}|r|| j#krt$ nd}|| _#d|v r9||v s$d|v r%n| j j%dkr-n| j j%dkr5q0t||||f |s3|S )z!Parse HTTP headers from raw data.c                 S   r   r    r   )r   r_   r    r    r!   r     r   z/AsyncRequest._parse_headers.<locals>.<listcomp>rO   Fr   zlimit request headers fieldsz
:r$    	)rt   	z!limit request headers fields sizez	 rt   EXPECTz100-continuer$   r$   Tr%   r&   _r   	dangerousdrop)&r)   r~   ro   r+   rp   r"   forwarded_allow_ipsforwarded_allow_networkssecure_scheme_headersforwarder_headersrY   r=   r
   poprX   r   strip_header_spacesrstripr   r   r   r   strip
startswithpermit_obsolete_foldingr   r?   appendjoin!RFC9110_5_5_INVALID_AND_DANGEROUSr   lowerr.   r9   r   r7   r   
header_map)rF   r`   rP   r)   r4   linesscheme_headerr   r   currheader_lengthnamevaluesecurer7   r    r    r!   r[     s   








GzAsyncRequest._parse_headersc                 C   s  d}d}| j D ]^\}}|dkr|durtd| d|}q|dkredd |dD }|D ]8}| d	kr?|r<td| dd
}q,| dkrN|rMtd| dq,| dv ra|r\td| d|   q,t|q|r| jdk rstd| d|dur}td| dd
| _d| _d| _	dS |durzt
| rt|}ntd| dW n ty   td| dw |dk rtd| d|| _|| _	dS d| _d| _	dS )z'Determine how to read the request body.FNzCONTENT-LENGTH)rK   zTRANSFER-ENCODINGc                 S   s   g | ]}|  qS r    )r   )r   vr    r    r!   r     r   z1AsyncRequest._set_body_reader.<locals>.<listcomp>,rC   Tidentity)compressdeflategzipr   r   )r4   r   r~   r   force_closer   r.   rC   rB   rE   str	isnumericr   r   )rF   rC   rB   r   r   valsvalr    r    r!   r\     s^   





zAsyncRequest._set_body_readerc                 C   s
   d| _ dS )z/Mark connection for closing after this request.TN)r8   rs   r    r    r!   r   :  s   
zAsyncRequest.force_closec                 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
CONNECTIONr   closez
keep-aliveFr   )r8   r4   r   r   r.   )rF   hr   r    r    r!   should_close>  s   
zAsyncRequest.should_closec                 C   s,   |  }| jD ]\}}||kr|  S qdS )z.Get a header value by name (case-insensitive).N)r   r4   )rF   r   r   r   r    r    r!   
get_headerL  s   zAsyncRequest.get_header    c                    s6   | j dkrdS | jr| |I dH S | |I dH S )zRead a chunk of the request body.

        Args:
            size: Maximum bytes to read

        Returns:
            bytes: Body data, empty bytes when body is exhausted
        r       N)rE   rC   _read_chunked_body_read_length_bodyrF   sizer    r    r!   	read_bodyT  s   
	zAsyncRequest.read_bodyc                    sH   | j dkrdS t|| j }| j|I dH }|r"|  j t|8  _ |S )z"Read from a length-delimited body.r   r   N)rE   minr*   rc   rY   )rF   r   to_readr`   r    r    r!   r   e  s   
zAsyncRequest._read_length_bodyc                    sD   | j du r|  | _ z	t| j I dH W S  ty!   d| _Y dS w )zRead from a chunked body.Nr   r   )rD   _chunked_body_readeranextStopAsyncIterationrE   r   r    r    r!   r   p  s   

zAsyncRequest._read_chunked_bodyc                 C  s6  	 |   I dH }|dd^}}|r|d}tdd |D r%tdt|d	kr/tdt|d
}|d	krA|  I dH  dS |}|d	kre| j	t
|dI dH }|sXt |t|8 }|V  |d	ksG| j	dI dH }|dkrt|dk r| j	dt| I dH }|sn
||7 }t|dk sx|dkrtdq)z)Async generator for reading chunked body.TN   ;r$   s    	c                 s   s    | ]}|d vV  qdS )s   0123456789abcdefABCDEFNr    )r   nr    r    r!   	<genexpr>  s    z4AsyncRequest._chunked_body_reader.<locals>.<genexpr>zInvalid chunk sizer   r   r   r'   rO   zMissing chunk terminator)_read_chunk_size_liner~   r   anyr   rY   r   _skip_trailersr*   rc   r   r   )rF   	size_line
chunk_sizer   r   r`   crlfmorer    r    r!   r   {  sB   

z!AsyncRequest._chunked_body_readerc                    sR   t  }	 | jdI dH }|st || | dr(| dd S q)zRead a chunk size line.Tr$   NrO   )ioBytesIOr*   rc   r   writegetvalueendswithre   r    r    r!   r     s   
z"AsyncRequest._read_chunk_size_linec                    sT   t  }	 | jdI dH }|sdS || | }|dr#dS |dkr)dS q)z(Skip trailer headers after chunked body.Tr$   NrN   rO   )r  r  r*   rc   r  r  r  )rF   r]   r`   contentr    r    r!   r     s   

zAsyncRequest._skip_trailersc                    s   	 |  dI dH }|sdS q)zgDrain any unread body data.

        Should be called before reusing connection for keepalive.
        Tr   N)r   )rF   r`   r    r    r!   
drain_body  s   zAsyncRequest.drain_bodyN)r$   )r   )F)r   )__name__
__module____qualname____doc__rH   classmethodrL   rI   rR   rU   rT   rl   rn   rm   rV   r[   r\   r   r   r   r   r   r   r   r   r   r
  r    r    r    r!   r#   ?   s2    
0&
;]
*\6
+r#   )*r  r  r   rer   r   gunicorn.http.errorsr   r   r   r   r   r   r   r	   r
   r   r   r   r   r   r   gunicorn.http.messager   r   r   r   gunicorn.utilr   r   r<   r>   r@   RFC9110_5_6_2_TOKEN_SPECIALScompileescaper   r   r   r   r"   r#   r    r    r    r!   <module>   s&   D	


