a
    mzfj0                    @   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ZddlZddl	Z
ddl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 ddlmZmZmZ ddlmZ ddlmZ ddlmZ dd	lmZmZ d
Z e! dkZ"e#dd$dZ%e" oe% Z&e&r:zddl'Z'ddl(Z(W n  e)y0   dZ&d\Z'Z(Y n0 ddl*Z*e"rpe+edrpe+eds^de_,e+edspde_-e+edserdnde_.dZ/dZ0dZ1dZ2dZ3dZ4dZ5d Z6d!Z7d"Z8e9d:d#e8fZ;e< Z=g d$Z>e+ed%si e_?G d&d' d'Z@G d(d) d)e@ZAG d*d+ d+ZBG d,d- d-ZCG d.d/ d/ZDG d0d1 d1ZEG d2d3 d3ZFG d4d5 d5ZGG d6d7 d7ZHd8d9d:ZId>d<d=ZJdS )?a  
A high-speed, production ready, thread pooled, generic HTTP server.

For those of you wanting to understand internals of this module, here's the
basic call flow. The server's listening thread runs a very tight loop,
sticking incoming connections onto a Queue::

    server = HTTPServer(...)
    server.start()
    ->  serve()
        while ready:
            _connections.run()
                while not stop_requested:
                    child = socket.accept()  # blocks until a request comes in
                    conn = HTTPConnection(child, ...)
                    server.process_conn(conn)  # adds conn to threadpool

Worker threads are kept in a pool and poll the Queue, popping off and then
handling each connection in turn. Each connection can consist of an arbitrary
number of requests and their responses, so we run a nested loop::

    while True:
        conn = server.requests.get()
        conn.communicate()
        ->  while True:
                req = HTTPRequest(...)
                req.parse_request()
                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
                    req.rfile.readline()
                    read_headers(req.rfile, req.inheaders)
                req.respond()
                ->  response = app(...)
                    try:
                        for chunk in response:
                            if chunk:
                                req.write(chunk)
                    finally:
                        if hasattr(response, "close"):
                            response.close()
                if req.close_connection:
                    return

For running a server you can invoke :func:`start() <HTTPServer.start()>` (it
will run the server forever) or use invoking :func:`prepare()
<HTTPServer.prepare()>` and :func:`serve() <HTTPServer.serve()>` like this::

    server = HTTPServer(...)
    server.prepare()
    try:
        threading.Thread(target=server.serve).start()

        # waiting/detecting some appropriate stop condition here
        ...

    finally:
        server.stop()

And now for a trivial doctest to exercise the test suite

.. testsetup::

   from cheroot.server import HTTPServer

>>> 'HTTPServer' in globals()
True
    N)	lru_cache   )connectionserrors__version__)bton)IS_PPC)
threadpool)MakeFileStreamWriter)
HTTPRequestHTTPConnection
HTTPServerHeaderReaderDropUnderscoreHeaderReaderSizeCheckWrapperKnownLengthRFileChunkedRFileGatewayget_ssl_adapter_classWindowsSERVER_SOFTWARE zGoogle App Engine/F)NNAF_INET6IPPROTO_IPV6)   IPV6_V6ONLY   SO_PEERCRED         
s   
   	       :   ;       *   /s   %2Fs   (?i))s   Accepts   Accept-Charsets   Accept-Encodings   Accept-Languages   Accept-Rangess   Allows   Cache-Control
   Connections   Content-Encodings   Content-Language   Expects   If-Matchs   If-None-Matchs   Pragmas   Proxy-Authenticates   TEs   Trailer   Transfer-Encodings   Upgrades   Varys   Vias   Warnings   WWW-Authenticate
statisticsc                   @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r   z`Object for reading headers from an HTTP request.

    Interface and default implementation.
    Nc                 C   s   |du ri }|  }|s td|tkr*q|ts<td|dd ttfv rZ| }nFz|td\}}W n ty   tdY n0 | }| 	|}|}| 
|sq|tv r||}|rd||f}|||< q|S )a  
        Read headers from the given stream into the given header dict.

        If hdict is None, a new header dict is created. Returns the populated
        header dict.

        Headers which are repeated are folded together using a comma if their
        specification so dictates.

        This function raises ValueError when the read bytes violate the HTTP
        spec.
        You should probably return "400 Bad Request" if this happens.
        NIllegal end of headers.HTTP requires CRLF terminatorsr   zIllegal header line.s   , )readline
ValueErrorCRLFendswithSPACETABstripsplitCOLON_transform_key_allow_headercomma_separated_headersgetjoin)selfrfileZhdictlinevkhnameexisting rD   </var/www/media/lib/python3.9/site-packages/cheroot/server.py__call__   s4    





zHeaderReader.__call__c                 C   s   dS )NTrD   r=   key_namerD   rD   rE   r9      s    zHeaderReader._allow_headerc                 C   s   |   S N)r5   titlerG   rD   rD   rE   r8      s    zHeaderReader._transform_key)N)__name__
__module____qualname____doc__rF   r9   r8   rD   rD   rD   rE   r      s   
8r   c                       s    e Zd ZdZ fddZ  ZS )r   zDCustom HeaderReader to exclude any headers with underscores in them.c                    s   t t| |}|od|vS )N_)superr   r9   )r=   rH   orig	__class__rD   rE   r9      s    z(DropUnderscoreHeaderReader._allow_header)rK   rL   rM   rN   r9   __classcell__rD   rD   rR   rE   r      s   r   c                   @   sZ   e Zd ZdZdd Zdd ZdddZdd	d
ZdddZdd Z	dd Z
dd ZeZdS )r   zWraps a file-like object, raising MaxSizeExceeded if too large.

    :param rfile: ``file`` of a limited size
    :param int maxlen: maximum length of the file being read
    c                 C   s   || _ || _d| _dS )z%Initialize SizeCheckWrapper instance.r   N)r>   maxlen
bytes_read)r=   r>   rU   rD   rD   rE   __init__  s    zSizeCheckWrapper.__init__c                 C   s   | j r| j| j krt d S rI   )rU   rV   r   MaxSizeExceededr=   rD   rD   rE   _check_length  s    zSizeCheckWrapper._check_lengthNc                 C   s*   | j |}|  jt|7  _|   |S )Read a chunk from ``rfile`` buffer and return it.

        :param size: amount of data to read
        :type size: int

        :returns: chunk from ``rfile``, limited by size if specified
        :rtype: bytes
        )r>   readrV   lenrZ   r=   sizedatarD   rD   rE   r\     s    	zSizeCheckWrapper.readc                 C   s   |dur2| j |}|  jt|7  _|   |S g }| j d}|  jt|7  _|   || t|dk s|dd tkr6t|S q6dS )Read a single line from ``rfile`` buffer and return it.

        :param size: minimum amount of data to read
        :type size: int

        :returns: one line from ``rfile``
        :rtype: bytes
        N   )	r>   r/   rV   r]   rZ   appendLFEMPTYr<   )r=   r_   r`   resrD   rD   rE   r/     s    	
zSizeCheckWrapper.readliner   c                 C   sV   d}g }|  |}|rR|| |t|7 }d|  k r@|krFqR nqR|  |}q|S zRead all lines from ``rfile`` buffer and return them.

        :param sizehint: hint of minimum amount of data to read
        :type sizehint: int

        :returns: lines of bytes read from ``rfile``
        :rtype: list[bytes]
        r   r/   rd   r]   r=   sizehinttotallinesr?   rD   rD   rE   	readlines9  s    


zSizeCheckWrapper.readlinesc                 C   s   | j   dS z*Release resources allocated for ``rfile``.Nr>   closerY   rD   rD   rE   rq   N  s    zSizeCheckWrapper.closec                 C   s   | S zReturn file iterator.rD   rY   rD   rD   rE   __iter__R  s    zSizeCheckWrapper.__iter__c                 C   s(   t | j}|  jt|7  _|   |S zGenerate next file chunk.)nextr>   rV   r]   rZ   r=   r`   rD   rD   rE   __next__V  s    
zSizeCheckWrapper.__next__)N)N)r   )rK   rL   rM   rN   rW   rZ   r\   r/   rn   rq   rs   rw   ru   rD   rD   rD   rE   r      s   


r   c                   @   sR   e Zd ZdZdd ZdddZdddZdd
dZdd Zdd Z	dd Z
e
ZdS )r   zWraps a file-like object, returning an empty string when exhausted.

    :param rfile: ``file`` of a known size
    :param int content_length: length of the file being read
    c                 C   s   || _ || _dS )z%Initialize KnownLengthRFile instance.N)r>   	remaining)r=   r>   content_lengthrD   rD   rE   rW   g  s    zKnownLengthRFile.__init__Nc                 C   sL   | j dkrdS |du r| j }nt|| j }| j|}|  j t|8  _ |S )zRead a chunk from ``rfile`` buffer and return it.

        :param size: amount of data to read
        :type size: int

        :rtype: bytes
        :returns: chunk from ``rfile``, limited by size if specified
        r   r&   N)rx   minr>   r\   r]   r^   rD   rD   rE   r\   l  s    	
zKnownLengthRFile.readc                 C   sL   | j dkrdS |du r| j }nt|| j }| j|}|  j t|8  _ |S )ra   r   r&   N)rx   rz   r>   r/   r]   r^   rD   rD   rE   r/     s    	
zKnownLengthRFile.readliner   c                 C   sV   d}g }|  |}|rR|| |t|7 }d|  k r@|krFqR nqR|  |}q|S rh   ri   rj   rD   rD   rE   rn     s    


zKnownLengthRFile.readlinesc                 C   s   | j   dS ro   rp   rY   rD   rD   rE   rq     s    zKnownLengthRFile.closec                 C   s   | S rr   rD   rY   rD   rD   rE   rs     s    zKnownLengthRFile.__iter__c                 C   s    t | j}|  jt|8  _|S rt   )ru   r>   rx   r]   rv   rD   rD   rE   rw     s    
zKnownLengthRFile.__next__)N)N)r   )rK   rL   rM   rN   rW   r\   r/   rn   rq   rs   rw   ru   rD   rD   rD   rE   r   `  s   


r   c                   @   sP   e Zd ZdZdddZdd Zddd	Zdd
dZdddZdd Z	dd Z
dS )r   a  Wraps a file-like object, returning an empty string when exhausted.

    This class is intended to provide a conforming wsgi.input value for
    request entities that have been encoded with the 'chunked' transfer
    encoding.

    :param rfile: file encoded with the 'chunked' transfer encoding
    :param int maxlen: maximum length of the file being read
    :param int bufsize: size of the buffer used to read the file
        c                 C   s(   || _ || _d| _t| _|| _d| _dS )z!Initialize ChunkedRFile instance.r   FN)r>   rU   rV   rf   bufferbufsizeclosed)r=   r>   rU   r}   rD   rD   rE   rW     s    zChunkedRFile.__init__c                 C   s  | j r
d S | j }|  jt|7  _| jrF| j| jkrFtd| j| 	t
d}z|d}t|d}W n" ty   tdj|dY n0 |dkrd| _ d S | jr| j| | jkrtd| j|}|  jt|7  _|  j|7  _| jd}|tkrtd	t| d
 d S )NRequest Entity Too Larger   r      z)Bad chunked transfer size: {chunk_size!r})
chunk_sizeT   z2Bad chunked transfer coding (expected '\r\n', got ))r~   r>   r/   rV   r]   rU   r   rX   r5   r6   	SEMICOLONpopintr0   formatIOErrorr\   r|   r1   repr)r=   r?   r   chunkZcrlfrD   rD   rE   _fetch  sF    



zChunkedRFile._fetchNc                 C   s   t }|dkr|S |r$t||kr$|S | js<|   | js<|S |rp|t| }|| jd| 7 }| j|d | _q|| j7 }t | _qdS )r[   r   N)rf   r]   r|   r   )r=   r_   r`   rx   rD   rD   rE   r\     s    	
zChunkedRFile.readc                 C   s   t }|dkr|S |r$t||kr$|S | js<|   | js<|S | jt}|r|dkr|t| }|| jd| 7 }| j|d | _qt|t| |}|| jd| 7 }| j|d | _q|dkr|| j7 }t | _q|| jd| 7 }| j|d | _qdS )ra   r   rc   N)rf   r]   r|   r   findre   rz   )r=   r_   r`   Znewline_posrx   rD   rD   rE   r/     s.    	
zChunkedRFile.readliner   c                 C   sV   d}g }|  |}|rR|| |t|7 }d|  k r@|krFqR nqR|  |}q|S rh   ri   rj   rD   rD   rE   rn   D  s    


zChunkedRFile.readlinesc                 c   sx   | j std| j }|s$td|  jt|7  _| jrP| j| jkrPtd|tkrZqt|	tsltd|V  qdS )zhRead HTTP headers and yield them.

        :yields: CRLF separated lines
        :ytype: bytes

        z:Cannot read trailers until the request body has been read.r-   r   r.   N)
r~   r0   r>   r/   rV   r]   rU   r   r1   r2   )r=   r?   rD   rD   rE   read_trailer_linesY  s    

zChunkedRFile.read_trailer_linesc                 C   s   | j   dS ro   rp   rY   rD   rD   rE   rq   w  s    zChunkedRFile.close)r{   )N)N)r   )rK   rL   rM   rN   rW   r   r\   r/   rn   r   rq   rD   rD   rD   rE   r     s   
	+
 
*
r   c                   @   s~   e Zd ZdZdZdZi Zg ZdZdZ	dZ
e ZdddZdd Zd	d
 Zdd Zdd ZdddZdd Zdd Zdd ZdS )r   zrAn HTTP Request (and response).

    A single HTTP connection may consist of multiple request/response pairs.
    NFTc                 C   sx   || _ || _d| _d| _d| _| j jdur0d| _d| _i | _d| _g | _	d| _
| jj| _d| _| jj| _|| _|| _dS )a  Initialize HTTP request container instance.

        Args:
            server (HTTPServer): web server object receiving this request
            conn (HTTPConnection): HTTP connection object for this request
            proxy_mode (bool): whether this HTTPServer should behave as a PROXY
            server for certain requests
            strict_mode (bool): whether we should return a 400 Bad Request when
            we encounter a request that a HTTP compliant client should not be
            making
        Fs   httpNs   httpszHTTP/1.0r   )serverconnreadystarted_requestschemessl_adapterresponse_protocol	inheadersstatus
outheaderssent_headersrS   close_connectionchunked_readchunked_write
proxy_modestrict_mode)r=   r   r   r   r   rD   rD   rE   rW     s"    

zHTTPRequest.__init__c                 C   s   t | jj| jj| _z|  }W n" tjyB   | dd Y dS 0 |sLdS z| 	 }W n" tjyz   | dd Y dS 0 |sdS d| _
dS )z;Parse the next HTTP request start-line and message-headers.z414 Request-URI Too LongzHThe Request-URI sent with the request exceeds the maximum allowed bytes.N413 Request Entity Too LargezCThe headers sent with the request exceed the maximum allowed bytes.T)r   r   r>   r   max_request_header_sizeread_request_liner   rX   simple_responseread_request_headersr   )r=   successrD   rD   rE   parse_request  s2    zHTTPRequest.parse_requestc              
   C   s  | j  }d| _|sdS |tkr2| j  }|s2dS |tsL| dd dS z| td\}}}|	ds| dd W dS |dd	 d
d}t
|dkr| dd W dS ttt|}|dkr| dd W dS W n& ttfy   | dd Y dS 0 || _| | _| jr<|| jkr<d}| d| dS ztj|\}}}	}
}W n" tyx   | dd Y dS 0 |p|}| jdkr| jr|r|n|	}	n| jdkrb| js| d dS tjdd|f}|\}}}}}t}z
|j}W n ty   Y n0 ||kp4| p4t||||f}|rL| dd dS | }}	t } }
}n| jov| j ov|}|r| dd dS | jo|	t o| }|rd}| d| dS |r| dd dS |	d	u r| dd dS zdd t|	D }W n: tyF } z | d|jd  W Y d	}~dS d	}~0 0 t |}	|		tsft|	 }	|turv|| _!|| _"|	| _#|
| _$t| j%j&d t| j%j&d  f}|d |d kr| d dS || _'d!t(|| | _)dS )"zRead and parse first line of the HTTP request.

        Returns:
            bool: True if the request line is valid or False if it's malformed.

        TF400 Bad Requestr.   r   s   HTTP/z$Malformed Request-Line: bad protocol   N   .r   z#Malformed Request-Line: bad version)r   r   z505 HTTP Version Not SupportedzCannot fulfill requestzMalformed Request-LinezMalformed method name: According to RFC 2616 (section 5.1.1) and its successors RFC 7230 (section 3.1.1) and RFC 7231 (section 4.1) method names are case-sensitive and uppercase.zMalformed Request-URIs   OPTIONSs   CONNECTz405 Method Not Allowedr&   s   //zFInvalid path in Request-URI: request-target must match authority-form.z2Absolute URI not allowed if server is not a proxy.zInvalid path in Request-URI: request-target must contain origin-form which starts with absolute-path (URI starting with a slash "/").z!Illegal #fragment in Request-URI.zInvalid path in Request-URI.c                 S   s   g | ]}t j|qS rD   )urllibparseunquote_to_bytes.0xrD   rD   rE   
<listcomp>  s   z1HTTPRequest.read_request_line.<locals>.<listcomp>r      z
HTTP/%s.%s)*r>   r/   r   r1   r2   r   r5   r6   r3   
startswithr]   tuplemapr   r0   
IndexErroruriuppermethodr   r   r   urlsplitUnicodeErrorr   r<   rf   portanyFORWARD_SLASHQUOTED_SLASH_REGEXargsQUOTED_SLASHr   	authoritypathqsr   protocolZrequest_protocolrz   r   )r=   Zrequest_liner   r   Zreq_protocolrprespr   r   r   r   fragmentZuri_is_absolute_formZ	uri_splitZ_schemeZ
_authority_pathZ_qsZ	_fragmentZ_portZinvalid_pathZdisallowed_absoluteZatomsexsprD   rD   rE   r     s   















 
zHTTPRequest.read_request_linec              
   C   s  z|  | j| j W n8 tyL } z | d|jd  W Y d}~dS d}~0 0 | jj}zt| j	dd}W n  ty   | dd Y dS 0 |r||kr| dd dS | j
d	kr| j	d
ddkrd| _n| j	d
ddkrd| _d}| j
d	kr| j	d}|rdd |dD }d| _|r\|D ].}|dkrBd| _n| d d| _ dS q,| j	dddkrd| jjdtdttf}z| jj| W n< tjy } z |jd tjvrȂ W Y d}~n
d}~0 0 dS )zRead ``self.rfile`` into ``self.inheaders``.

        Ref: :py:attr:`self.inheaders <HTTPRequest.outheaders>`.

        :returns: success status
        :rtype: bool
        r   r   NF   Content-Lengthz Malformed Content-Length Header.r   CThe entity sent with the request exceeds the maximum allowed bytes.HTTP/1.1r)   r&      closeT
   Keep-Aliver+   c                 S   s    g | ]}|  r|   qS rD   )r5   lowerr   rD   rD   rE   r     r&   z4HTTPRequest.read_request_headers.<locals>.<listcomp>   ,   chunkedz501 Unimplementedr*   s   100-continueasciis   100 Continue)header_readerr>   r   r0   r   r   r   max_request_body_sizer   r;   r   r   r6   r   r<   r   encoder3   r1   r   wfilewritesocketerrorr   socket_errors_to_ignore)r=   r   mrbsclteencmsgrD   rD   rE   r     sb    	



z HTTPRequest.read_request_headersc                 C   s   | j j}| jr t| jj|| _nDt| jdd}|rT||k rT| j	sP| 
dd dS t| jj|| _| j |   | jo|   | jr| jjd dS )z/Call the gateway and write its iterable output.r   r   r   r   Ns   0

)r   r   r   r   r   r>   r   r   r;   r   r   r   gatewayrespondr   ensure_headers_sentr   r   r   )r=   r   r   rD   rD   rE   r   (  s     zHTTPRequest.respondr   c              
   C   s   t |}d| jj|f }dt| }d}|d|d|dg}|dd dv rtd| _| jd	krp|d
 nd}|t |rt	|t r|d}|| z| j
jt| W n8 tjy } z|jd tjvr܂ W Y d}~n
d}~0 0 dS )z+Write a simple response back to the client.z%s %s
zContent-Length: %s
zContent-Type: text/plain

ISO-8859-1N   )Z413Z414Tr   s   Connection: close
r   r   )strr   r   r]   r   r   r   rd   r1   
isinstancer   r   r   rf   r<   r   r   r   r   r   )r=   r   r   Zproto_statusry   content_typebufr   rD   rD   rE   r   ?  s.    




zHTTPRequest.simple_responsec                 C   s   | j sd| _ |   dS )z:Ensure headers are sent to the client if not already sent.TN)r   send_headersrY   rD   rD   rE   r   d  s    zHTTPRequest.ensure_headers_sentc                 C   sX   | j rF|rFtt|dd d}|t|tg}| jjt	| n| jj| dS )z$Write unbuffered data to the client.r   Nr   )
r   hexr]   r   r1   r   r   r   rf   r<   )r=   r   Zchunk_size_hexr   rD   rD   rE   r   j  s
    
zHTTPRequest.writec           
      C   s  dd | j D }t| jdd }|dkr2d| _nLd|vr~|dk s~|d	v rLn2| jd
ko^| jdk}|rxd| _| j d nd| _| js| jj	}| | _d|vr| jd
kr| jr| j d n| js| j d d| j v r| j ddj
| jjddf | js,| js,t| jdd}|dkr,| j| d|vrV| j dtjjdddf d|vrz| j d| jjdf | jjd}|t | j t g}| j D ]$\}}	||t t |	 t  q|t | jjt| dS )zAssert, process, and send the HTTP response message-headers.

        You must set ``self.status``, and :py:attr:`self.outheaders
        <HTTPRequest.outheaders>` before calling this.
        c                 S   s   g | ]\}}|  qS rD   )r   )r   keyvaluerD   rD   rE   r   y  r&   z,HTTPRequest.send_headers.<locals>.<listcomp>Nr   i  Ts   content-length   )      i0  r   s   HEAD)r+   r   s
   connection)r)   r   )r)   r   r   ztimeout={connection_timeout})Zconnection_timeoutr   rx   r   s   dates   Date)usegmts   servers   Serverr   )r   r   r   r   r   r   r   rd   r   can_add_keepalive_connectionr   timeoutr   r   getattrr>   r\   emailutils
formatdateserver_namer   r3   r1   r7   r   r   r   rf   r<   )
r=   Zhkeysr   Zneeds_chunkedZcan_keeprx   protor   rA   r@   rD   rD   rE   r   s  sh    






zHTTPRequest.send_headers)FT)r   )rK   rL   rM   rN   r   r   r   r   r   r   r   r   r   rW   r   r   r   r   r   r   r   r   rD   rD   rD   rE   r   |  s&   
!" d`
%	r   c                   @   s   e Zd ZdZdZdZdZejZ	ejZ
eZdZdZdZefddZdd ZdZdd	 Zd
d Zdd Zdd Zedd Zedd Zedd Zdd Zedd Zedd Zdd ZdS )r   z#An HTTP connection (active socket).NFc                 C   sn   || _ || _||d| j| _||d| j| _d| _| j j| _| j j| _t	dd| j
| _
t	dd| j| _dS )aB  Initialize HTTPConnection instance.

        Args:
            server (HTTPServer): web server object receiving this request
            sock (socket._socketobject): the raw socket object (usually
                TCP) for this connection
            makefile (file): a fileobject class for reading from the socket
        rbwbr   r   )maxsizeN)r   r   rbufsizer>   wbufsizer   requests_seenpeercreds_enabledpeercreds_resolve_enabledr   resolve_peer_credsget_peer_creds)r=   r   sockmakefilerD   rD   rE   rW     s    	

zHTTPConnection.__init__c              
   C   s~  d}zZ|  | j| }|  | jjd r6|  jd7  _|jsBW dS d}|  |jsZW dS W n tj	y } zp|j
d }d}||v r|r|r|jr| |d n2|tjvr| jjdt| tjdd	 | |d
 W Y d}~nd}~0  ttfy    Y n| tjy   Y nh tjy0   | | Y nJ tyx } z0| jjt|tjdd	 | |d
 W Y d}~n
d}~0 0 dS )zrRead each request and respond appropriately.

        Returns true if the connection should be kept open.
        FEnabledr   Tr   )z	timed outzThe read operation timed outz408 Request Timeoutzsocket.error %slevel	tracebackz500 Internal Server ErrorN)RequestHandlerClassr   r   statsr   r   r   r   r   r   r   r   _conditional_errorr   r   	error_logr   loggingWARNINGKeyboardInterrupt
SystemExitFatalSSLAlert
NoSSLError_handle_no_ssl	ExceptionERROR)r=   Zrequest_seenreqr   ZerrnumZtimeout_errsrD   rD   rE   communicate  sH    


 "zHTTPConnection.communicatec                 C   s`   |r
|j rd S z| jj}W n ty4   | jj}Y n0 t|d| j| _d}|d| d| _	d S )Nr   zUThe client sent a plain HTTP request, but this server only speaks HTTPS on this port.r   T)
r   r   _sockAttributeError_socketr   r   r   r   linger)r=   r  Z	resp_sockr   rD   rD   rE   r  .  s    
zHTTPConnection._handle_no_sslc                 C   sR   |r
|j rdS z|| W n0 tjy0   Y n tjyL   | | Y n0 dS )zvRespond with an error.

        Don't bother writing if a response
        has already started being written.
        N)r   r   r   r  r  r  )r=   r  responserD   rD   rE   r
  ?  s    
z!HTTPConnection._conditional_errorc                 C   s(   | j   | js$|   | j  n dS )z,Close the socket underlying this connection.N)r>   rq   r  _close_kernel_socketr   rY   rD   rD   rE   rq   O  s
    
zHTTPConnection.closec              
   C   s   d}t s| jjtjkr tdn| js.tdz| jtjtj	t
|}W n, tjyx } zt|W Y d}~n&d}~0 0 t
||\}}}|||fS dS )a  Return the PID/UID/GID tuple of the peer socket for UNIX sockets.

        This function uses SO_PEERCRED to query the UNIX PID, UID, GID
        of the peer, which is only available if the bind address is
        a UNIX domain socket.

        Raises:
            NotImplementedError: in case of unsupported socket type
            RuntimeError: in case of SO_PEERCRED lookup unsupported or disabled

        Z3iz5SO_PEERCRED is only supported in Linux kernel and WSLz0Peer creds lookup is disabled within this serverN)
IS_WINDOWSr   familyAF_UNIXNotImplementedErrorr   RuntimeError
getsockopt
SOL_SOCKETr   structcalcsizer   unpack)r=   ZPEERCRED_STRUCT_DEFZ
peer_credsZ
socket_errpiduidgidrD   rD   rE   r  b  s$    zHTTPConnection.get_peer_credsc                 C   s   |   \}}}|S )z,Return the id of the connected peer process.r  )r=   r'  rO   rD   rD   rE   peer_pid  s    zHTTPConnection.peer_pidc                 C   s   |   \}}}|S )z1Return the user id of the connected peer process.r*  )r=   rO   r(  rD   rD   rE   peer_uid  s    zHTTPConnection.peer_uidc                 C   s   |   \}}}|S )z2Return the group id of the connected peer process.r*  )r=   rO   r)  rD   rD   rE   peer_gid  s    zHTTPConnection.peer_gidc                 C   s@   t stdn| jstdt| jj}t	| j
j}||fS )a  Look up the username and group tuple of the ``PEERCREDS``.

        :returns: the username and group tuple of the ``PEERCREDS``

        :raises NotImplementedError: if the OS is unsupported
        :raises RuntimeError: if UID/GID lookup is unsupported or disabled
        zUID/GID lookup is unavailable under current platform. It can only be done under UNIX-like OS but not under the Google App Enginez-UID/GID lookup is disabled within this server)IS_UID_GID_RESOLVABLEr   r   r!  pwdgetpwuidr,  pw_namegrpgetgrgidr-  gr_name)r=   usergrouprD   rD   rE   r     s    z!HTTPConnection.resolve_peer_credsc                 C   s   |   \}}|S )z2Return the username of the connected peer process.r   )r=   r5  rO   rD   rD   rE   	peer_user  s    zHTTPConnection.peer_userc                 C   s   |   \}}|S )z/Return the group of the connected peer process.r7  )r=   rO   r6  rD   rD   rE   
peer_group  s    zHTTPConnection.peer_groupc              
   C   sl   t | jd| jj}z|tj W nF tjy4   Y n4 tjyf } z|jtjvrR W Y d}~n
d}~0 0 dS )z0Terminate the connection at the transport level.Zsock_shutdownN)	r   r   shutdown	SHUT_RDWRr   Z#acceptable_sock_shutdown_exceptionsr   errnoZ$acceptable_sock_shutdown_error_codes)r=   r:  erD   rD   rE   r    s    z#HTTPConnection._close_kernel_socket) rK   rL   rM   rN   Zremote_addrZremote_portZssl_envioDEFAULT_BUFFER_SIZEr   r   r   r  r   r   Z	last_usedr
   rW   r  r  r  r
  rq   r  propertyr+  r,  r-  r   r8  r9  r  rD   rD   rD   rE   r     s:   3+




r   c                   @   sv  e Zd ZdZdZdZdZdZdZdZ	dZ
dZdZdZdZdjed	ZdZd
ZdZdZdZeZdZd
Zd
Zd
ZdZd@ddZdd Zdd Z dd Z!e"dd Z#e#j$dd Z#dd Z%dd Z&dd Z'dd  Z(e)j*d!d" Z+e"d#d$ Z,d%d& Z-dAd)d*Z.dBd+d,Z/d-d. Z0e1d/d0 Z2e3dCd1d2Z4e1d3d4 Z5e1d5d6 Z6d7d8 Z7e"d9d: Z8e"d;d< Z9e8j$d=d: Z8d>d? Z:dS )Dr   zAn HTTP server.z	127.0.0.1Nr   r   
   g      ?zCheroot/{version!s})versionFr   Trc   c	           	      C   sT   || _ || _tj| |pd|d| _|s,| j}|| _|| _|o>|| _|| _	| 
  dS )a  Initialize HTTPServer instance.

        Args:
            bind_addr (tuple): network interface to listen to
            gateway (Gateway): gateway for processing HTTP requests
            minthreads (int): minimum number of threads for HTTP thread pool
            maxthreads (int): maximum number of threads for HTTP thread pool
            server_name (str): web server name to be advertised via Server
                HTTP header
            reuse_port (bool): if True SO_REUSEPORT option would be set to
                socket
        r   )rz   maxN)	bind_addrr   r	   Z
ThreadPoolrequestsrB  r   r   r   
reuse_portclear_stats)	r=   rD  r   
minthreads
maxthreadsr   r   r   rF  rD   rD   rE   rW   3  s    
zHTTPServer.__init__c                    s   d _ d _d fdd fddd fdd fdd fd	d fd
dddd dd dd dd dd dd i d _ jtjdt  < dS )zReset server stat counters..Nr   Fc                    s
   t  jS rI   )r   rD  srY   rD   rE   <lambda>\  r&   z(HTTPServer.clear_stats.<locals>.<lambda>c                    s   | d  rdp   S )Nr  rc   runtimerJ  rY   rD   rE   rL  ]  r&   c                    s   | d     S )NAcceptsrM  rJ  rY   rD   rE   rL  _  r&   c                    s   t  jdd S )Nqsizer   rE  rJ  rY   rD   rE   rL  `  r&   c                    s   t t jdg S )N_threads)r]   r   rE  rJ  rY   rD   rE   rL  a  r&   c                    s   t  jdd S )NZidlerQ  rJ  rY   rD   rE   rL  b  r&   c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s   | ]}|d  |V  qdS )RequestsNrD   r   wrD   rD   rE   	<genexpr>e  r&   ;HTTPServer.clear_stats.<locals>.<lambda>.<locals>.<genexpr>Worker Threadsr   sumvaluesrJ  rD   rD   rE   rL  d  s   c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s   | ]}|d  |V  qdS )
Bytes ReadNrD   rT  rD   rD   rE   rV  h  r&   rW  rX  r   rY  rJ  rD   rD   rE   rL  g  s   c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s   | ]}|d  |V  qdS )Bytes WrittenNrD   rT  rD   rD   rE   rV  k  r&   rW  rX  r   rY  rJ  rD   rD   rE   rL  j  s   c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s   | ]}|d  |V  qdS )	Work TimeNrD   rT  rD   rD   rE   rV  o  r&   rW  rX  r   rY  rJ  rD   rD   rE   rL  n  s   c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s*   | ]"}|d  ||d |pd V  qdS )r\  r^  ư>NrD   rT  rD   rD   rE   rV  r  s   rW  rX  r   rY  rJ  rD   rD   rE   rL  q  s
   
c                 S   s*   | d  rdp(t dd | d  D dS )Nr  rc   c                 s   s*   | ]"}|d  ||d |pd V  qdS )r]  r^  r_  NrD   rT  rD   rD   rE   rV  x  s   rW  rX  r   rY  rJ  rD   rD   rE   rL  w  s
   
)r  zBind AddresszRun timerO  zAccepts/secQueueZThreadszThreads IdlezSocket ErrorsrS  r\  r]  r^  zRead ThroughputzWrite ThroughputrX  zCheroot HTTPServer %d)_start_time	_run_timer	  r  r,   idrY   rD   rY   rE   rG  V  s(    





%zHTTPServer.clear_statsc                 C   s(   | j du r| jS | jt | j   S dS )zReturn server uptime.N)ra  rb  timerY   rD   rD   rE   rN    s    
zHTTPServer.runtimec                 C   s   d| j | jj| jf S )z1Render Server instance representing bind address.z	%s.%s(%r))rL   rS   rK   rD  rY   rD   rD   rE   __str__  s    
zHTTPServer.__str__c                 C   s   | j S )a  Return the interface on which to listen for connections.

        For TCP sockets, a (host, port) tuple. Host values may be any
        :term:`IPv4` or :term:`IPv6` address, or any valid hostname.
        The string 'localhost' is a synonym for '127.0.0.1' (or '::1',
        if your hosts file prefers :term:`IPv6`).
        The string '0.0.0.0' is a special :term:`IPv4` entry meaning
        "any active interface" (INADDR_ANY), and '::' is the similar
        IN6ADDR_ANY for :term:`IPv6`.
        The empty string or :py:data:`None` are not allowed.

        For UNIX sockets, supply the file name as a string.

        Systemd socket activation is automatic and doesn't require tempering
        with this variable.

        .. glossary::

           :abbr:`IPv4 (Internet Protocol version 4)`
              Internet Protocol version 4

           :abbr:`IPv6 (Internet Protocol version 6)`
              Internet Protocol version 6
        )
_bind_addrrY   rD   rD   rE   rD    s    zHTTPServer.bind_addrc                 C   s(   t |tr|d dv rtd|| _dS )z5Set the interface on which to listen for connections.r   )r   NzzHost values of '' or None are not allowed. Use '0.0.0.0' (IPv4) or '::' (IPv6) instead to listen on all active interfaces.N)r   r   r0   rf  )r=   r   rD   rD   rE   rD    s
    c              
   C   s   z|    W nr tyF } z"| j}|s,|| _||W Y d}~nBd}~0  ty~ } z"| j}|sd|| _||W Y d}~n
d}~0 0 dS )z4Run the server forever, and stop it cleanly on exit.N)startr  	interruptr  )r=   Zkb_intr_excZunderlying_interruptZsys_exit_excrD   rD   rE   
safe_start  s    zHTTPServer.safe_startc                 C   s   d| _ | jdu rd| j | _d| _d}tddrJtdtjtj| _n`t	| j
ttfrz| | j
 W nB tjy } z(d|| j
|f }t||W Y d}~n
d}~0 0 n| j
\}}zt||tjtjdtj}W nL tjy$   tj}| j
}d|v rtj}|d	 }|tjdd
|fg}Y n0 |D ]~}|\}	}
}}}z| |	|
| W  qW nN tjy } z2d|||f }| jr| j  d| _W Y d}~n
d}~0 0 q*| jst|| jd | j| j t| | _| j  d| _t | _ dS )zPrepare server to serving requests.

        It binds a socket's port, setups the socket to ``listen()`` and does
        other preparing things.
        Nz	%s ServerzNo socket could be createdZ
LISTEN_PIDr   z%s -- (%s: %s)r   :)r   r   r   r   T)!
_interruptsoftwarerB  r   osgetenvfromfdAF_INETSOCK_STREAMr   rD  r   bytesbind_unix_socketr   getaddrinfo	AF_UNSPEC
AI_PASSIVEgaierrorr   bindrq   
settimeoutlistenrequest_queue_sizer   ZConnectionManager_connectionsrE  rg  r   rd  ra  )r=   r   Zserrhostr   infoZ	sock_typerD  rg   afsocktyper   
_canonnamesarD   rD   rE   prepare  sX    
$



 

zHTTPServer.preparec              	   C   s   | j r\| js\z| j| j W q  ttfy6    Y q  tyX   | jdt	j
dd Y q 0 q | jr| jrttd qb| jr| jdS )z1Serve requests, after invoking :func:`prepare()`.zError in HTTPServer.serveTr  g?N)r   rh  r|  runexpiration_intervalr  r  r  r  r  r  _stopping_for_interruptrd  sleeprY   rD   rD   rE   serve  s    zHTTPServer.servec                 C   s   |    |   dS )zmRun the server forever.

        It is shortcut for invoking :func:`prepare()` then :func:`serve()`.
        N)r  r  rY   rD   rD   rE   rg  +  s    	zHTTPServer.startc                 c   sF   |    tj| jd}d|_|  z|V  W |   n
|   0 dS )z4Context manager for running this server in a thread.)targetTN)r  	threadingThreadr  daemonrg  stop)r=   threadrD   rD   rE   _run_in_thread7  s    zHTTPServer._run_in_threadc                 C   s   | j o| jjS )z>Flag whether it is allowed to add a new keep-alive connection.)r   r|  r   rY   rD   rD   rE   r   C  s    z'HTTPServer.can_add_keepalive_connectionc                 C   s    | j r| j| n|  dS )z7Put an idle connection back into the ConnectionManager.N)r   r|  putrq   r=   r   rD   rD   rE   put_connH  s    zHTTPServer.put_connr      c                 C   sD   t jdj|d t j  |r@t }t j| t j  dS )zWrite error message to log.

        Args:
            msg (str): error message
            level (int): logging level
            traceback (bool): add traceback to output or not
        z{msg!s}
)r   N)sysstderrr   r   flush
traceback_
format_exc)r=   r   r  r  ZtblinesrD   rD   rE   r  P  s    	
zHTTPServer.error_logc              	   C   sB   |  | j|||| j| j| j}| || j }| _| || _|S )z.Create (or recreate) the actual socket object.)prepare_socketrD  nodelayr   rF  bind_socketr   resolve_real_bind_addr)r=   r  typer   r  rD   rD   rE   rx  `  s    zHTTPServer.bindc              
   C   s  t rtdd}zt| j W n ty2   Y nz tyf } zt|}d|vrR W Y d}~nNd}~0  ty } z.t|}d|vrd|vrd|vr W Y d}~n
d}~0 0 | j|t	j
t	jd| j| j| jd	}zt| | d
}W n ty   d}Y n0 z| ||}W n  t	jy.   |   Y n0 | |}zD|s|zt|| W n$ tyv   tj||dd Y n0 d
}W n ty   Y n0 |s| jdtjd || _|| _	|S )z*Create (or recreate) a UNIX socket object.z0AF_UNIX sockets are not supported under Windows.i  zJremove() argument 1 must be encoded string without null bytes, not unicodeNz'unlink: embedded null character in pathzembedded null bytez0argument must be a string without NUL charactersr   )rD  r  r  r   r  r   rF  TF)follow_symlinksz(Failed to set socket fs mode permissions)r  )r  r0   rm  unlinkrD  OSError	TypeErrorr   r  r   r  rq  r  r   rF  fchmodfilenor  r   rq   r  lchmodr  chmodr  r  r  )r=   rD  Zfs_permissionsZtyp_errerr_msgZval_errr  ZFS_PERMS_SETrD   rD   rE   rs  l  s~    


zHTTPServer.bind_unix_socketc                 C   s   |d d \}}|dk}| j tjtjfvr2td|r>tdttdr\| tjtjd n>ttdrz| tjtj	d n t
r| tjtjd ntdd S )	Nr   r   zCannot reuse a non-IP socketz"Cannot reuse an ephemeral port (0)SO_REUSEPORT_LBr   SO_REUSEPORTz,Current platform does not support port reuse)r  r   rp  r   r0   hasattr
setsockoptr#  r  r  r  SO_REUSEADDRr   )socket_rD  r}  r   IS_EPHEMERAL_PORTrD   rD   rE   _make_socket_reusable  s    

z HTTPServer._make_socket_reusablec              	   C   s   t  |||}t| |dd \}	}
|
dk}|rB| j||d ts\|s\|t jt jd |rt|t	t
fs|t jt jd |dur||}tt do|t jko|	dv }|rz|t jt jd W n tt jfy   Y n0 |S )z%Create and prepare the socket object.Nr   r   r  rD  r   r   )z::z::0z	::0.0.0.0)r   r   Zprevent_socket_inheritancer  r  r  r#  r  r   r   rr  IPPROTO_TCPTCP_NODELAYrx  r  r   r   r   r  r   )clsrD  r  r  r   r  r   rF  r  r}  r   r  Zlistening_ipv6rD   rD   rE   r    s2    



zHTTPServer.prepare_socketc                 C   s   |  | | S )z#Bind the socket to given interface.)rx  r  rD   rD   rE   r    s    
zHTTPServer.bind_socketc                 C   s<   |   }| jtjtjfv r&|dd S t|tr8t|}|S )z/Retrieve actual bind address from bound socket.Nr   )getsocknamer  r   rp  r   r   rr  r   r  rD   rD   rE   r    s    	
z!HTTPServer.resolve_real_bind_addrc                 C   s2   z| j | W n tjy,   |  Y n0 dS )z#Process an incoming HTTPConnection.N)rE  r  queueFullrq   r  rD   rD   rE   process_conn*  s    zHTTPServer.process_connc                 C   s   | j S )zFlag interrupt of the server.)rk  rY   rD   rD   rE   rh  2  s    zHTTPServer.interruptc                 C   s
   | j tu S )z8Return whether the server is responding to an interrupt.)rk  _STOPPING_FOR_INTERRUPTrY   rD   rD   rE   r  7  s    z"HTTPServer._stopping_for_interruptc                 C   s@   t | _t|tr| d t|tr.| d |   || _dS )a  Perform the shutdown of this server and save the exception.

        Typically invoked by a worker thread in
        :py:mod:`~cheroot.workers.threadpool`, the exception is raised
        from the thread running :py:meth:`serve` once :py:meth:`stop`
        has completed.
        z!Keyboard Interrupt: shutting downz SystemExit raised: shutting downN)r  rk  r   r  r  r  r  )r=   rh  rD   rD   rE   rh  <  s    	



c              
   C   sr  | j s
dS d| _ | jdur2|  jt | j 7  _d| _| j  t| dd}|rVt| jt	t
fs<z| dd \}}W n8 tjy } z|jd tjvr W Y d}~nd}~0 0 t||tjtjD ]n}|\}}}}	}
d}z2t|||}|d |||f |  W q tjy8   |r4|  Y q0 qt|drP|  d| _| j  | j| j dS )z5Gracefully shutdown a server that is serving forever.NFr   r   r   g      ?rq   )r   ra  rb  rd  r|  r  r   r   rD  r   rr  r  r   r   r   r   r   rt  ru  rq  ry  connectrq   r  rE  shutdown_timeout)r=   r  r}  r   r   rg   r  r  r   r  Z_sarK  rD   rD   rE   r  P  sD    



zHTTPServer.stop)rA  rc   NFFF)r   r  F)r   )F);rK   rL   rM   rN   rf  rk  r   rH  rI  r   r   r{  r  r   r  r   r   rB  rl  r   r   r   r  r   ZConnectionClassr   r   r   rF  Zkeep_alive_conn_limitrW   rG  rN  re  r@  rD  setterri  r  r  rg  
contextlibcontextmanagerr  r   r  r  rx  rs  staticmethodr  classmethodr  r  r  r  rh  r  r  rD   rD   rD   rE   r     s      
#+

G



S
 4




r   c                   @   s    e Zd ZdZdd Zdd ZdS )r   zDBase class to interface HTTPServer with other systems, such as WSGI.c                 C   s
   || _ dS )zuInitialize Gateway instance with request.

        Args:
            req (HTTPRequest): current HTTP request
        N)r  )r=   r  rD   rD   rE   rW     s    zGateway.__init__c                 C   s   t dS )z>Process the current request. Must be overridden in a subclass.N)r   rY   rD   rD   rE   r     s    zGateway.respondN)rK   rL   rM   rN   rW   r   rD   rD   rD   rE   r     s   r   z%cheroot.ssl.builtin.BuiltinSSLAdapterz&cheroot.ssl.pyopenssl.pyOpenSSLAdapter)builtinZ	pyopensslr  c                 C   s   t |   }t|tr|d}||d d }|d| }ztj| }|du rVt W n& ty~   t|t	 t
 dg}Y n0 zt||}W n" ty   td||f Y n0 |S )z/Return an SSL adapter class for the given name..r   Nr   z!'%s' object has no attribute '%s')ssl_adaptersr   r   r   rfindr  modulesKeyError
__import__globalslocalsr   r  )nameadapterZlast_dot	attr_nameZmod_pathmodrD   rD   rE   r     s(    




r   )r  )KrN   rm  r>  reemail.utilsr   r   r  rd  r  r  r  platformr  r  r  urllib.parser   	functoolsr   r   r   r   r   _compatr   r   workersr	   r  r
   r   __all__systemr  rn  r   ZIS_GAEr.  r2  r/  ImportErrorr$  r  r   r   r   re   r1   r4   r3   r7   r   rf   ZASTERISKr   r   compiler<   r   r  r  r:   r,   r   r   r   r   r   r   r   r   r   r  r   rD   rD   rD   rE   <module>   s   C	
FaZ C    W       7