o
    i7                     @   s   d Z ddl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 d	d
 ZG dd dZG dd dejZdS )z
ASGI protocol handler for gunicorn.

Implements asyncio.Protocol to handle HTTP/1.x and HTTP/2 connections
and dispatch to ASGI applications.
    N)datetime)AsyncUnreader)AsyncRequest)AsyncUWSGIRequest)
NoMoreData)UWSGIParseExceptionc                 C   s   | r
t | dd S dS )zNormalize socket address to ASGI-compatible (host, port) tuple.

    ASGI spec requires server/client to be (host, port) tuples.
    IPv6 sockets return 4-tuples (host, port, flowinfo, scope_id),
    so we extract just the first two elements.
    N   )tuple)sockaddr r   J/home/ubuntu/.local/lib/python3.10/site-packages/gunicorn/asgi/protocol.py_normalize_sockaddr   s   r   c                   @   s   e Zd ZdZdd ZdS )ASGIResponseInfoz;Simple container for ASGI response info for access logging.c                 C   s\   || _ || _g | _|D ] \}}t|tr|d}t|tr#|d}| j||f qd S )Nlatin-1)statussentheaders
isinstancebytesdecodeappend)selfr   r   r   namevaluer   r   r   __init__$   s   



zASGIResponseInfo.__init__N)__name__
__module____qualname____doc__r   r   r   r   r   r   !   s    r   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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d4d#d$Zd%d& Zd'd( Zd)d* Zd+d, Zd-d. Zd/d0 Zd1d2 Zd3S )5ASGIProtocolzHTTP/1.1 protocol handler for ASGI applications.

    Handles connection lifecycle, request parsing, and ASGI app invocation.
    c                 C   sL   || _ |j| _|j| _|j| _d | _d | _d | _d | _d| _	d| _
d | _d S )Nr   F)workercfglogasgiapp	transportreaderwriter_task	req_count_closed_receive_queue)r   r    r   r   r   r   7   s   
zASGIProtocol.__init__c                 C   s   || _ | j jd7  _|d}|r2t|dr2| }|dkr2t | _| jj	
| ||| _dS t | _|| _| jj	
|  | _dS )z(Called when a connection is established.   
ssl_objectselected_alpn_protocolh2N)r%   r    nr_connsget_extra_infohasattrr.   asyncioStreamReaderr&   loopcreate_task_handle_http2_connectionr(   r'   _handle_connection)r   r%   r-   alpnr   r   r   connection_madeG   s   



zASGIProtocol.connection_madec                 C   s   | j r| j | dS dS )z/Called when data is received on the connection.N)r&   	feed_data)r   datar   r   r   data_receiveda   s   zASGIProtocol.data_receivedc                 C   s   | j rdS d| _ | j jd8  _| jr| j  | jdur%| jddi | jrJ| j sLt	| j
dd}|dkrC| jj|| j dS | j  dS dS dS )	a  Called when the connection is lost or closed.

        Instead of immediately cancelling the task, we signal a disconnect
        event and send an http.disconnect message to the receive queue.
        This allows the ASGI app to clean up resources (like database
        connections) gracefully before the task is cancelled.

        See: https://github.com/benoitc/gunicorn/issues/3484
        NTr,   typehttp.disconnectasgi_disconnect_grace_period   r   )r*   r    r0   r&   feed_eofr+   
put_nowaitr(   donegetattrr!   r5   
call_later_cancel_task_if_pendingcancel)r   excgrace_periodr   r   r   connection_lostf   s"   

zASGIProtocol.connection_lostc                 C   s&   | j r| j  s| j   dS dS dS )z9Cancel the task if it's still pending after grace period.N)r(   rD   rH   r   r   r   r   rG      s   z$ASGIProtocol._cancel_task_if_pendingc              
   C   s   z	| j | W dS  ty4 } z|jtjtjtjfvr)| jd W Y d}~dS W Y d}~dS d}~w t	t
fy?   Y dS w )ax  Write data to transport, handling connection errors gracefully.

        Catches exceptions that occur when the client has disconnected:
        - OSError with errno EPIPE, ECONNRESET, ENOTCONN
        - RuntimeError when transport is closing/closed
        - AttributeError when transport is None

        These are silently ignored since the client is already gone.
        zSocket error writing response.N)r%   writeOSErrorerrnoEPIPE
ECONNRESETENOTCONNr"   	exceptionRuntimeErrorAttributeError)r   r<   er   r   r   _safe_write   s   
zASGIProtocol._safe_writec              
      s  t | j}zz| jd}| jd}| js|  jd7  _z(t| jdd}|dkr8t	| j||| jI dH }nt
	| j||| jI dH }W n! tyO   Y ne tyg } z| jd| W Y d}~nQd}~ww | |rx| |||I dH  n<| |||I dH }| j jd7  _| jj| jjkr| jd	 d
| j_d
}|r| jjsn| jjsn
| I dH  | jrW n" tjy   Y n  ty } z| jd| W Y d}~nd}~ww W |   dS W |   dS W |   dS |   w )z/Main request handling loop for this connection.peernamesocknamer,   protocolhttpuwsgiNzuWSGI parse error: %s,Autorestarting worker after current request.FzError handling connection: %s)r   r&   r%   r1   r*   r)   rE   r!   r   parser   r   r   r"   debug_is_websocket_upgrade_handle_websocket_handle_http_requestr    nrmax_requestsinfoalive	keepalive
drain_bodyr3   CancelledError	ExceptionrS   _close_transport)r   unreaderrX   rY   rZ   requestrV   rg   r   r   r   r8      sx   



8zASGIProtocol._handle_connectionc                 C   s\   |j dkrdS d}d}|jD ]\}}|dkr| }q|dkr#| }q|dko-|o-d|v S )a	  Check if request is a WebSocket upgrade.

        Per RFC 6455 Section 4.1, the opening handshake requires:
        - HTTP method MUST be GET
        - Upgrade header MUST be "websocket" (case-insensitive)
        - Connection header MUST contain "Upgrade"
        GETFNUPGRADE
CONNECTION	websocketupgrade)methodr   lower)r   rm   rr   
connectionr   r   r   r   r   r`      s   
	
z"ASGIProtocol._is_websocket_upgradec                    sF   ddl m} | |||}|| j| j|| j| j}| I dH  dS )z!Handle WebSocket upgrade request.r   )WebSocketProtocolN)gunicorn.asgi.websocketrv   _build_websocket_scoper%   r&   r$   r"   run)r   rm   rY   rX   rv   scopews_protocolr   r   r   ra      s   zASGIProtocol._handle_websocketc                    s>  	 ||}dddd
dg dt 	_d jdkr6js6ddddI dH  d n	t	  	fd	d
}	
f	dd}		||}d}zz+t
 }		j	j 	|||I dH  dur|s	ddI dH  dW n tjy   	jd Y W d	_z"t
 |	 }
t}	j|||
 	j	j|| W dS  ty   	jd Y dS w  ty#   	jd s	ddI dH  dY W d	_z"t
 |	 }
t}	j|||
 	j	j|| W dS  ty"   	jd Y dS w w W d	_z!t
 |	 }
t}	j|||
 	j	j|| W nE tyY   	jd Y n6w d	_z!t
 |	 }
t}	j|||
 	j	j|| W w  ty   	jd Y w w  rdS 	jjo	jjS )zHandle a single HTTP request.FN  r   http.request    r>   body	more_bodyTc                     sD   j r
 r
ddiS  I d H } | ddkr | dds d | S )Nr>   r?   r}   r   T)r*   get)msg)body_completereceive_queuer   r   r   receive%  s   
z2ASGIProtocol._handle_http_request.<locals>.receivec                    sJ  j rd S | d }|dkr%| d}| dg }||I d H  d S |dkrbr1td d S d| d | dg tdd	 D }|sVjd
krVdtdg I d H  d S |dkrsntd d S rvtd d S | dd}| dd}|rj|dI d H  t	|7 |sr
d dd S d S d S )Nr>   http.response.informationalr   r   http.response.startResponse already startedTc                 s   sP    | ]#\}}t |tr| n| d kp#t |tr| n| dkV  qdS )s   content-lengthzcontent-lengthN)r   strrt   ).0r   _r   r   r   	<genexpr>N  s    
zBASGIProtocol._handle_http_request.<locals>.send.<locals>.<genexpr>r,   r,   )s   transfer-encodings   chunkedhttp.response.bodyResponse not startedResponse already completer   r~   r   F)chunkeds   0

)r*   r   _send_informationalrT   anyversionlist_send_response_start
_send_bodylenrW   )messagemsg_typeinfo_statusinfo_headershas_content_lengthr   r   )	exc_to_raiserm   response_completeresponse_headersresponse_sentresponse_startedresponse_statusr   use_chunkedr   r   send3  sR   

z/ASGIProtocol._handle_http_request.<locals>.sendInternal Server Errorz'Request cancelled (client disconnected)Exception in post_request hookError in ASGI application)_build_http_scoper3   Queuer+   content_lengthr   putr6   _read_body_to_queue_build_environr   nowr!   pre_requestr    r$   _send_error_responseri   r"   r_   r   accesspost_requestrj   rS   should_closerf   rg   )r   rm   rY   rX   rz   r   r   environresprequest_startrequest_timer   )r   r   r   rm   r   r   r   r   r   r   r   r   rb     s   >	
z!ASGIProtocol._handle_http_requestc              
      s   z(	 | dI dH }|r|d|ddI dH  n|ddddI dH  W dS q tyO } z| jd| |ddddI dH  W Y d}~dS d}~ww )	z.Read request body and put chunks on the queue.Ti   Nr}   r   r~   FzError reading body: %s)	read_bodyr   rj   r"   r_   )r   rm   queuechunkrV   r   r   r   r     s4   z ASGIProtocol._read_body_to_queuec           
      C   s   g }|j D ]\}}|| d|df qt|}t|}dddd|jd  d|jd  |j|j|j|jrA|jdnd	|j	rK|j	dnd	| j
jpQd
|||d}	t| jdrc| jj|	d< t|drsd|j|jdi|	d< |	S )z*Build ASGI HTTP scope from parsed request.r   r[   3.02.4r   spec_versionr   .r,   r~    r>   r#   http_versionrs   schemepathraw_pathquery_string	root_pathr   serverclientstatepriority_weighthttp.response.priorityweight
depends_on
extensions)r   r   rt   encoder   r   rs   r   r   queryr!   r   r2   r    r   r   priority_depends_on)
r   rm   rY   rX   r   r   r   r   r   rz   r   r   r   r     s6    

zASGIProtocol._build_http_scopec              	   C   sn   |j |j|j|jp
dd|jd  d|jd  |r|d ndd}|jD ]\}}d|dd	 }|||< q$|S )
z8Build minimal WSGI-like environ dict for access logging.r   HTTP/r   r   r,   -REQUEST_METHODRAW_URI	PATH_INFOQUERY_STRINGSERVER_PROTOCOLREMOTE_ADDRHTTP_r   )rs   urir   r   r   r   replacer   rm   rY   rX   r   r   r   keyr   r   r   r     s   

zASGIProtocol._build_environc                 C   s  g }|j D ]\}}|| d|df qg }|j D ]\}}|dkr2dd |dD } nqt|}t|}	dddd	|jd
  d|jd  |jdkrSdnd|j|jr_|jdnd|j	ri|j	dnd| j
jpod|||	|d}
t| jdr| jj|
d< |
S )z/Build ASGI WebSocket scope from parsed request.r   zSEC-WEBSOCKET-PROTOCOLc                 S   s   g | ]}|  qS r   )strip)r   sr   r   r   
<listcomp>  s    z7ASGIProtocol._build_websocket_scope.<locals>.<listcomp>,rq   r   r   r   r   r   r,   httpswsswsr~   r   )r>   r#   r   r   r   r   r   r   r   r   r   subprotocolsr   )r   r   rt   r   splitr   r   r   r   r   r!   r   r2   r    r   )r   rm   rY   rX   r   r   r   r   r   r   rz   r   r   r   rx     s6    
z#ASGIProtocol._build_websocket_scopec              	      s   |j dk rdS | |}d|j d  d|j d  d| d| d	}|D ]"\}}t|tr2|d	}t|tr<|d	}|| d
| d7 }q$|d7 }| |d	 dS )ao  Send an informational response (1xx) such as 103 Early Hints.

        Args:
            status: HTTP status code (100-199)
            headers: List of (name, value) header tuples
            request: The parsed request object

        Note: Informational responses are only sent for HTTP/1.1 or later.
        HTTP/1.0 clients do not support 1xx responses.
        r   Nr   r   r   r,    
r   : )r   _get_reason_phraser   r   r   rW   r   )r   r   r   rm   reasonresponser   r   r   r   r   r     s   

*



z ASGIProtocol._send_informationalc           
   	      s   |  |}d|jd  d|jd  d| d| d	}g }|D ]#\}}t|tr-|d}t|tr7|d}|| d| d q|d	 |d
| d }	| |	d dS )z&Send HTTP response status and headers.r   r   r   r,   r   r   r   r   zServer: gunicorn/asgi
r   N)	r   r   r   r   r   r   joinrW   r   )
r   r   r   rm   r   status_lineheader_linesr   r   r   r   r   r   r   5  s   
*




z!ASGIProtocol._send_response_startFc                    sH   |r"|rt |ddd| d }| | dS | | dS dS )zSend response body chunk.xr   r   s   
N)r   r   rW   )r   r   r   r   r   r   r   r   K  s   zASGIProtocol._send_bodyc                    sF   | d}d| d| dt| d}| | d | | dS )zSend an error response.zutf-8z	HTTP/1.1 r   z,
Content-Type: text/plain
Content-Length: z
Connection: close

r   N)r   r   rW   )r   r   r   r   r   r   r   r   r   U  s   
z!ASGIProtocol._send_error_responsec                 C   s   i ddddddddd	d
dddddddddddddddddddddd d!d"i d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdB}| |dCS )Dz'Get HTTP reason phrase for status code.d   Continuee   zSwitching Protocolsg   zEarly Hints   OK   Created   Accepted   z
No Content   zPartial Contenti-  zMoved Permanentlyi.  Foundi/  z	See Otheri0  zNot Modifiedi3  zTemporary Redirecti4  zPermanent Redirecti  zBad Requesti  Unauthorizedi  	Forbiddeni  z	Not Foundi  zMethod Not Allowedi  zRequest Timeouti  Conflicti  Gonei  zLength Requiredi  zPayload Too Largei  zURI Too Longi  zUnsupported Media Typei  zUnprocessable Entityi  zToo Many Requestsr|   r   i  zNot Implementedi  zBad Gatewayi  zService Unavailablei  zGateway TimeoutUnknown)r   )r   r   reasonsr   r   r   r   b  s   	
 !#zASGIProtocol._get_reason_phrasec                 C   sT   | j r&| js(z| j  r| j   | j   W n	 ty    Y nw d| _dS dS dS )zClose the transport safely.

        Calls write_eof() first if supported to signal end of writing,
        which helps ensure buffered data is flushed before closing.
        TN)r%   r*   can_write_eof	write_eofcloserj   rL   r   r   r   rk     s   


zASGIProtocol._close_transportc                    s  z+zddl m} |d}|d}| j}t|}t|||| jj}|| j	|||}	|	
 I dH  |	| _|	js| jjrz|	jddI dH }
W n" tjyT   Y q8 tyl } z| jd| W Y d}~nd}~ww |
D ]S}zIz| ||	||I dH  W n2 ty } z&| jd	 z|	|jjd
t|I dH  W n	 ty   Y nw W Y d}~nd}~ww W |	|jj qo|	|jj w | j jt|
7  _| jj| jjkr| jd d| j_n|	js| jjs?W n# tjy   Y n ty } z| jd| W Y d}~nd}~ww W t| dr(z
| j  I dH  W n
 ty'   Y nw | !  dS t| drIz
| j  I dH  W n
 tyH   Y nw | !  w )zHandle an HTTP/2 connection.r   )AsyncHTTP2ConnectionrX   rY   Ng      ?)timeoutzHTTP/2 receive error: %szError handling HTTP/2 requestr|   r]   FzHTTP/2 connection error: %s_h2_conn)"gunicorn.http2.async_connectionr  r1   r&   r3   StreamReaderProtocolStreamWriterr    r5   r!   initiate_connectionr  	is_closedrf   receive_dataTimeoutErrorrj   r"   r_   _handle_http2_requestrS   
send_errorstream	stream_idr   cleanup_streamrc   r   rd   re   ri   r2   r  rk   )r   r%   r-   r  rX   rY   r&   rZ   r'   h2_connrequestsrV   reqr   r   r   r7     s   



 	"
z%ASGIProtocol._handle_http2_connectionc                    s  j j	| ||}ddd dg dg fdd} 	f	dd}| ||}t }	z	z| j| j | 	|||I dH   durT rg }
D ]\}}t
|trh|d	}t
|trr|d	}|
||f qZrd
tfg}|
D ]\}}|| t|f qjj	|dd j	 }|j|dd  I dH  rʈjj	dd |jdd  I dH  	I dH  n	|
I dH  n	ddI dH  dW n ty   | jd s
	ddI dH  dY nw W z$t |	 }tt}| j||| | j| j|| W dS  tyC   | jd Y dS w z#t |	 }tt}| j||| | j| j|| W w  tyw   | jd Y w w )zHandle a single HTTP/2 request.FNr|   r~   c                     s    j  } d| ddS )Nr}   Fr   )r   read)r   )rm   r   r   r     s   
z3ASGIProtocol._handle_http2_request.<locals>.receivec                    s  | d }|dkrD|  d}|  dg }g }|D ]\}}t|tr&|d}t|tr0|d}|||f q||I d H  d S |dkr^rPtd d S d| d |  dg d S |d	krsjtd
 d S rrtd d S |  dd}|  dd}|r|7 |sdd S d S |dkrɈstd d S |  dg }	g }
|	D ]\}}t|tr|d}t|tr|d}|
||f q|
 d S d S )Nr>   r   r   r   r   r   r   Tr   r   r   r   r~   r   Fhttp.response.trailersz)Cannot send trailers before body complete)r   r   r   r   r   send_informationalrT   extend)r   r   r   r   r   r   r   r   r   trailer_headerstrailers)	r   r  response_bodyr   r   r   r   response_trailersr  r   r   r     sb   








z0ASGIProtocol._handle_http2_request.<locals>.sendr   z:status)
end_streamr   r   r   )r  r  _build_http2_scope_build_http2_environr   r   r!   r   r    r$   r   r   r   r   r   rt   r  send_headersstreams_send_pending_data	send_datasend_trailerssend_responser  rj   r"   rS   r   r   r   r   )r   rm   r  rY   rX   rz   r   r   r   r   r   r   r   response_hdrsr  r   r   r   )
r   r  rm   r%  r   r   r   r   r&  r  r   r    s   	>






z"ASGIProtocol._handle_http2_requestc                 C   s   g }|j D ]\}}|| d|df qt|}t|}ddddd|j|j|j|jr6|jdnd|jr@|jdnd| j	j
pFd|||d	}	t| jd
rX| jj|	d
< i }
t|drh|j|jd|
d< i |
d< |
|	d< |	S )z*Build ASGI HTTP scope from HTTP/2 request.r   r[   r   r   r   2r~   r   r   r   r   r   r   r   r   )r   r   rt   r   r   rs   r   r   r   r!   r   r2   r    r   r   r   )r   rm   rY   rX   r   r   r   r   r   rz   r   r   r   r   r(  v  s>   


zASGIProtocol._build_http2_scopec                 C   sV   |j |j|j|jp
dd|r|d ndd}|jD ]\}}d|dd }|||< q|S )z.Build minimal environ dict for access logging.r   zHTTP/2r   r   r   r   r   )rs   r   r   r   r   r   r   r   r   r   r)    s   	
z!ASGIProtocol._build_http2_environN)F)r   r   r   r   r   r:   r=   rK   rG   rW   r8   r`   ra   rb   r   r   r   rx   r   r   r   r   r   rk   r7   r  r(  r)  r   r   r   r   r   1   s8    #G
 (&

'G +r   )r   r3   rO   r   gunicorn.asgi.unreaderr   gunicorn.asgi.messager   gunicorn.asgi.uwsgir   gunicorn.http.errorsr   gunicorn.uwsgi.errorsr   r   r   Protocolr   r   r   r   r   <module>   s   
