U
    lufa                     @   s  d 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	m
Z
mZ ddlmZ ddlmZmZmZmZ dd	lmZmZmZmZmZmZmZmZmZ G d
d dejZG dd deZG dd dej Z!G dd dej Z"eedee eee#df ef eee#df eee#ef f eee#df eee#ef e#f f  Z$G dd deZ%G dd de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.ee#e/d$d%d&Z0eddd$d'd&Z0ee# ee/ d$d(d&Z0dS ))ap  Flexible routing implementation.

Tornado routes HTTP requests to appropriate handlers using `Router`
class implementations. The `tornado.web.Application` class is a
`Router` implementation and may be used directly, or the classes in
this module may be used for additional flexibility. The `RuleRouter`
class can match on more criteria than `.Application`, or the `Router`
interface can be subclassed for maximum customization.

`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`
to provide additional routing capabilities. This also means that any
`Router` implementation can be used directly as a ``request_callback``
for `~.httpserver.HTTPServer` constructor.

`Router` subclass must implement a ``find_handler`` method to provide
a suitable `~.httputil.HTTPMessageDelegate` instance to handle the
request:

.. code-block:: python

    class CustomRouter(Router):
        def find_handler(self, request, **kwargs):
            # some routing logic providing a suitable HTTPMessageDelegate instance
            return MessageDelegate(request.connection)

    class MessageDelegate(HTTPMessageDelegate):
        def __init__(self, connection):
            self.connection = connection

        def finish(self):
            self.connection.write_headers(
                ResponseStartLine("HTTP/1.1", 200, "OK"),
                HTTPHeaders({"Content-Length": "2"}),
                b"OK")
            self.connection.finish()

    router = CustomRouter()
    server = HTTPServer(router)

The main responsibility of `Router` implementation is to provide a
mapping from a request to `~.httputil.HTTPMessageDelegate` instance
that will handle this request. In the example above we can see that
routing is possible even without instantiating an `~.web.Application`.

For routing to `~.web.RequestHandler` implementations we need an
`~.web.Application` instance. `~.web.Application.get_handler_delegate`
provides a convenient way to create `~.httputil.HTTPMessageDelegate`
for a given request and `~.web.RequestHandler`.

Here is a simple example of how we can we route to
`~.web.RequestHandler` subclasses by HTTP method:

.. code-block:: python

    resources = {}

    class GetResource(RequestHandler):
        def get(self, path):
            if path not in resources:
                raise HTTPError(404)

            self.finish(resources[path])

    class PostResource(RequestHandler):
        def post(self, path):
            resources[path] = self.request.body

    class HTTPMethodRouter(Router):
        def __init__(self, app):
            self.app = app

        def find_handler(self, request, **kwargs):
            handler = GetResource if request.method == "GET" else PostResource
            return self.app.get_handler_delegate(request, handler, path_args=[request.path])

    router = HTTPMethodRouter(Application())
    server = HTTPServer(router)

`ReversibleRouter` interface adds the ability to distinguish between
the routes and reverse them to the original urls using route's name
and additional arguments. `~.web.Application` is itself an
implementation of `ReversibleRouter` class.

`RuleRouter` and `ReversibleRuleRouter` are implementations of
`Router` and `ReversibleRouter` interfaces and can be used for
creating rule-based routing configurations.

Rules are instances of `Rule` class. They contain a `Matcher`, which
provides the logic for determining whether the rule is a match for a
particular request and a target, which can be one of the following.

1) An instance of `~.httputil.HTTPServerConnectionDelegate`:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/handler"), ConnectionDelegate()),
        # ... more rules
    ])

    class ConnectionDelegate(HTTPServerConnectionDelegate):
        def start_request(self, server_conn, request_conn):
            return MessageDelegate(request_conn)

2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/callable"), request_callable)
    ])

    def request_callable(request):
        request.write(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK")
        request.finish()

3) Another `Router` instance:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/router.*"), CustomRouter())
    ])

Of course a nested `RuleRouter` or a `~.web.Application` is allowed:

.. code-block:: python

    router = RuleRouter([
        Rule(HostMatches("example.com"), RuleRouter([
            Rule(PathMatches("/app1/.*"), Application([(r"/app1/handler", Handler)])),
        ]))
    ])

    server = HTTPServer(router)

In the example below `RuleRouter` is used to route between applications:

.. code-block:: python

    app1 = Application([
        (r"/app1/handler", Handler1),
        # other handlers ...
    ])

    app2 = Application([
        (r"/app2/handler", Handler2),
        # other handlers ...
    ])

    router = RuleRouter([
        Rule(PathMatches("/app1.*"), app1),
        Rule(PathMatches("/app2.*"), app2)
    ])

    server = HTTPServer(router)

For more information on application-level routing see docs for `~.web.Application`.

.. versionadded:: 4.5

    N)partial)httputil)_CallableAdapter)
url_escapeurl_unescapeutf8)app_log)basestring_typeimport_objectre_unescapeunicode_type)	AnyUnionOptional	AwaitableListDictPatternTupleoverloadc                   @   s@   e Zd ZdZejeeej dddZ	e
ejejdddZdS )	RouterzAbstract router interface.requestkwargsreturnc                 K   s
   t  dS )a  Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`
        that can serve the request.
        Routing implementations may pass additional kwargs to extend the routing logic.

        :arg httputil.HTTPServerRequest request: current HTTP request.
        :arg kwargs: additional keyword arguments passed by routing implementation.
        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to
            process the request.
        NNotImplementedError)selfr   r    r   3/tmp/pip-unpacked-wheel-bmg6zs32/tornado/routing.pyfind_handler   s    zRouter.find_handler)server_connrequest_connr   c                 C   s   t | ||S N)_RoutingDelegate)r   r!   r"   r   r   r   start_request   s    zRouter.start_requestN)__name__
__module____qualname____doc__r   HTTPServerRequestr   r   HTTPMessageDelegater    objectHTTPConnectionr%   r   r   r   r   r      s     r   c                   @   s&   e Zd ZdZeeee dddZdS )ReversibleRouterzxAbstract router interface for routers that can handle named routes
    and support reversing them to original urls.
    nameargsr   c                 G   s
   t  dS )a  Returns url string for a given route name and arguments
        or ``None`` if no match is found.

        :arg str name: route name.
        :arg args: url parameters.
        :returns: parametrized url string for a given route name (or ``None``).
        Nr   )r   r0   r1   r   r   r   reverse_url   s    zReversibleRouter.reverse_urlN)r&   r'   r(   r)   strr   r   r2   r   r   r   r   r.      s   r.   c                   @   s~   e Zd ZeeejddddZeej	ej
f ejeed  dddZeeed  dd	d
ZddddZddddZdS )r$   N)routerr!   r"   r   c                 C   s   || _ || _d | _|| _d S r#   )r!   r"   delegater4   )r   r4   r!   r"   r   r   r   __init__   s    z_RoutingDelegate.__init__)
start_lineheadersr   c                 C   sj   t |tjsttj| j| j||d}| j|| _	| j	d kr\t
d|j|j t| j| _	| j	||S )N)
connectionserver_connectionr7   r8   z$Delegate for %s %s request not found)
isinstancer   RequestStartLineAssertionErrorr*   r"   r!   r4   r    r5   r   debugmethodpath_DefaultMessageDelegateheaders_received)r   r7   r8   r   r   r   r   rB      s     
z!_RoutingDelegate.headers_received)chunkr   c                 C   s   | j d k	st| j |S r#   )r5   r=   data_received)r   rC   r   r   r   rD     s    z_RoutingDelegate.data_receivedr   c                 C   s   | j d k	st| j   d S r#   )r5   r=   finishr   r   r   r   rF   
  s    z_RoutingDelegate.finishc                 C   s   | j d k	st| j   d S r#   )r5   r=   on_connection_closerG   r   r   r   rH     s    z$_RoutingDelegate.on_connection_close)r&   r'   r(   r   r,   r   r-   r6   r   r<   ResponseStartLineHTTPHeadersr   r   rB   bytesrD   rF   rH   r   r   r   r   r$      s     

r$   c                   @   s,   e Zd ZejddddZddddZdS )rA   N)r9   r   c                 C   s
   || _ d S r#   )r9   )r   r9   r   r   r   r6     s    z _DefaultMessageDelegate.__init__rE   c                 C   s*   | j tdddt  | j   d S )NzHTTP/1.1i  z	Not Found)r9   Zwrite_headersr   rI   rJ   rF   rG   r   r   r   rF     s
    z_DefaultMessageDelegate.finish)r&   r'   r(   r   r-   r6   rF   r   r   r   r   rA     s   rA   RuleMatcherc                   @   s|   e Zd ZdZdee ddddZeddddZddd	d
dZe	j
eee	j dddZee	j
eee	j dddZdS )
RuleRouterz!Rule-based router implementation.Nrulesr   c                 C   s   g | _ |r| | dS )aI  Constructs a router from an ordered list of rules::

            RuleRouter([
                Rule(PathMatches("/handler"), Target),
                # ... more rules
            ])

        You can also omit explicit `Rule` constructor and use tuples of arguments::

            RuleRouter([
                (PathMatches("/handler"), Target),
            ])

        `PathMatches` is a default matcher, so the example above can be simplified::

            RuleRouter([
                ("/handler", Target),
            ])

        In the examples above, ``Target`` can be a nested `Router` instance, an instance of
        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,
        accepting a request argument.

        :arg rules: a list of `Rule` instances or tuples of `Rule`
            constructor arguments.
        N)rP   	add_rulesr   rP   r   r   r   r6   /  s    zRuleRouter.__init__c                 C   st   |D ]j}t |ttfr\t|dks&tt |d trTtt|d f|dd  }nt| }| j	| 
| qdS )zAppends new rules to the router.

        :arg rules: a list of Rule instances (or tuples of arguments, which are
            passed to Rule constructor).
        )         r      N)r;   tuplelistlenr=   r	   rL   PathMatchesrP   appendprocess_rule)r   rP   ruler   r   r   rQ   N  s     zRuleRouter.add_rulesrL   r]   r   c                 C   s   |S )zOverride this method for additional preprocessing of each rule.

        :arg Rule rule: a rule to be processed.
        :returns: the same or modified Rule instance.
        r   r   r]   r   r   r   r\   ^  s    zRuleRouter.process_ruler   c                 K   sV   | j D ]J}|j|}|d k	r|jr.|j|d< | j|j|f|}|d k	r|  S qd S )Ntarget_kwargs)rP   matchermatchr`   get_target_delegatetarget)r   r   r   r]   target_paramsr5   r   r   r   r    f  s    

 
zRuleRouter.find_handler)rd   r   re   r   c                 K   sp   t |tr|j|f|S t |tjrB|jdk	s2t||j|jS t	|rl|jdk	sXtt
t|f||jS dS )a  Returns an instance of `~.httputil.HTTPMessageDelegate` for a
        Rule's target. This method is called by `~.find_handler` and can be
        extended to provide additional target types.

        :arg target: a Rule's target.
        :arg httputil.HTTPServerRequest request: current request.
        :arg target_params: additional parameters that can be useful
            for `~.httputil.HTTPMessageDelegate` creation.
        N)r;   r   r    r   HTTPServerConnectionDelegater9   r=   r%   r:   callabler   r   )r   rd   r   re   r   r   r   rc   x  s    

 zRuleRouter.get_target_delegate)N)r&   r'   r(   r)   r   	_RuleListr6   rQ   r\   r   r*   r   r+   r    rc   r   r   r   r   rN   ,  s   	   rN   c                       sX   e Zd ZdZdee dd fddZddd fdd	Zee	ee d
ddZ
  ZS )ReversibleRuleRoutera  A rule-based router that implements ``reverse_url`` method.

    Each rule added to this router may have a ``name`` attribute that can be
    used to reconstruct an original uri. The actual reconstruction takes place
    in a rule's matcher (see `Matcher.reverse`).
    NrO   c                    s   i | _ t | d S r#   )named_rulessuperr6   rR   	__class__r   r   r6     s    zReversibleRuleRouter.__init__rL   r^   c                    s<   t  |}|jr8|j| jkr,td|j || j|j< |S )Nz4Multiple handlers named %s; replacing previous value)rk   r\   r0   rj   r   warningr_   rl   r   r   r\     s     z!ReversibleRuleRouter.process_ruler/   c                 G   sZ   || j kr| j | jj| S | jD ]2}t|jtr"|jj|f| }|d k	r"|  S q"d S r#   )rj   ra   reverserP   r;   rd   r.   r2   )r   r0   r1   r]   Zreversed_urlr   r   r   r2     s    


z ReversibleRuleRouter.reverse_url)N)r&   r'   r(   r)   r   rh   r6   r\   r3   r   r2   __classcell__r   r   rl   r   ri     s   ri   c                   @   sZ   e Zd ZdZddeeeeef  ee ddddZeee ddd	Z	ed
ddZ
dS )rL   zA routing rule.NrM   )ra   rd   r`   r0   r   c                 C   s6   t |trt|}|| _|| _|r&|ni | _|| _dS )ad  Constructs a Rule instance.

        :arg Matcher matcher: a `Matcher` instance used for determining
            whether the rule should be considered a match for a specific
            request.
        :arg target: a Rule's target (typically a ``RequestHandler`` or
            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,
            depending on routing implementation).
        :arg dict target_kwargs: a dict of parameters that can be useful
            at the moment of target instantiation (for example, ``status_code``
            for a ``RequestHandler`` subclass). They end up in
            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`
            method.
        :arg str name: the name of the rule that can be used to find it
            in `ReversibleRouter.reverse_url` implementation.
        N)r;   r3   r
   ra   rd   r`   r0   )r   ra   rd   r`   r0   r   r   r   r6     s    
zRule.__init__r1   r   c                 G   s   | j j| S r#   )ra   ro   r   r1   r   r   r   ro     s    zRule.reverserE   c                 C   s   d| j j| j| j| j| jf S Nz%s(%r, %s, kwargs=%r, name=%r))rm   r&   ra   rd   r`   r0   rG   r   r   r   __repr__  s    zRule.__repr__)NN)r&   r'   r(   r)   r   r   r   r3   r6   ro   rt   r   r   r   r   rL     s     !c                   @   sB   e Zd ZdZejeeee	f  dddZ
e	ee dddZdS )	rM   z*Represents a matcher for request features.r   r   c                 C   s
   t  dS )a1  Matches current instance against the request.

        :arg httputil.HTTPServerRequest request: current HTTP request
        :returns: a dict of parameters to be passed to the target handler
            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``
            can be passed for proper `~.web.RequestHandler` instantiation).
            An empty dict is a valid (and common) return value to indicate a match
            when the argument-passing features are not used.
            ``None`` must be returned to indicate that there is no match.Nr   r   r   r   r   r   rb     s    
zMatcher.matchrq   c                 G   s   dS )zEReconstructs full url from matcher instance and additional arguments.Nr   rr   r   r   r   ro     s    zMatcher.reverseN)r&   r'   r(   r)   r   r*   r   r   r3   r   rb   ro   r   r   r   r   rM     s   c                   @   s.   e Zd ZdZejeeee	f  dddZ
dS )
AnyMatcheszMatches any request.ru   c                 C   s   i S r#   r   rv   r   r   r   rb     s    zAnyMatches.matchN)r&   r'   r(   r)   r   r*   r   r   r3   r   rb   r   r   r   r   rw     s   rw   c                   @   sF   e Zd ZdZeeef ddddZej	e
eeef  dddZdS )	HostMatchesz@Matches requests from hosts specified by ``host_pattern`` regex.N)host_patternr   c                 C   s4   t |tr*|ds|d7 }t|| _n|| _d S )N$)r;   r	   endswithrecompilery   )r   ry   r   r   r   r6     s
    

zHostMatches.__init__ru   c                 C   s   | j |jri S d S r#   )ry   rb   Z	host_namerv   r   r   r   rb     s    zHostMatches.match)r&   r'   r(   r)   r   r3   r   r6   r   r*   r   r   r   rb   r   r   r   r   rx     s   rx   c                   @   s@   e Zd ZdZeeddddZeje	e
eef  dddZdS )	DefaultHostMatcheszMatches requests from host that is equal to application's default_host.
    Always returns no match if ``X-Real-Ip`` header is present.
    N)applicationry   r   c                 C   s   || _ || _d S r#   )r   ry   )r   r   ry   r   r   r   r6     s    zDefaultHostMatches.__init__ru   c                 C   s"   d|j kr| j| jjri S d S )Nz	X-Real-Ip)r8   ry   rb   r   Zdefault_hostrv   r   r   r   rb      s    
zDefaultHostMatches.match)r&   r'   r(   r)   r   r   r6   r   r*   r   r   r3   rb   r   r   r   r   r~     s   r~   c                   @   sx   e Zd ZdZeeef ddddZej	e
eeef  dddZee
e d	d
dZee
e e
e f dddZdS )rZ   z@Matches requests with paths specified by ``path_pattern`` regex.N)path_patternr   c                 C   sl   t |tr*|ds|d7 }t|| _n|| _t| jjd| jjfksXt	d| jj
 |  \| _| _d S )Nrz   r   zDgroups in url regexes must either be all named or all positional: %r)r;   r	   r{   r|   r}   regexrY   
groupindexgroupsr=   pattern_find_groups_path_group_count)r   r   r   r   r   r6   +  s    

zPathMatches.__init__ru   c                 C   sp   | j |j}|d krd S | j js&i S g }i }| j jrRtdd |  D }ndd | D }t||dS )Nc                 s   s"   | ]\}}t |t|fV  qd S r#   )r3   _unquote_or_none).0kvr   r   r   	<genexpr>I  s    z$PathMatches.match.<locals>.<genexpr>c                 S   s   g | ]}t |qS r   )r   )r   sr   r   r   
<listcomp>M  s     z%PathMatches.match.<locals>.<listcomp>)	path_argspath_kwargs)r   rb   r@   r   r   dict	groupdictitems)r   r   rb   r   r   r   r   r   rb   :  s    

zPathMatches.matchrq   c                 G   s   | j d krtd| jj t|| jks0tdt|s>| j S g }|D ]0}t|tt	fs`t
|}|tt|dd qF| j t| S )NzCannot reverse url regex z&required number of arguments not foundF)plus)r   
ValueErrorr   r   rY   r   r=   r;   r   rK   r3   r[   r   r   rW   )r   r1   Zconverted_argsar   r   r   ro   Q  s    
zPathMatches.reverserE   c              	   C   s   | j j}|dr|dd }|dr4|dd }| j j|dkrJdS g }|dD ]}d|kr|d}|d	krzt||d d }W n t	k
r   Y  dS X |
d
|  qXzt|}W n t	k
r   Y  dS X |
| qXd|| j jfS )zReturns a tuple (reverse string, group count) for a url.

        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method
        would return ('/%s/%s/', 2).
        ^rV   Nrz   ()NN)r   z%s )r   r   
startswithr{   r   countsplitindexr   r   r[   join)r   r   piecesfragmentZ	paren_locZunescaped_fragmentr   r   r   r   `  s.    




zPathMatches._find_groups)r&   r'   r(   r)   r   r3   r   r6   r   r*   r   r   r   rb   ro   r   intr   r   r   r   r   rZ   (  s
   rZ   c                       sV   e Zd ZdZd	eeef eee	eef  ee dd fddZ
edddZ  ZS )
URLSpeczSpecifies mappings between URLs and handlers.

    .. versionchanged: 4.5
       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for
       backwards compatibility.
    N)r   handlerr   r0   r   c                    s4   t |}t |||| |j| _| j| _|| _dS )a  Parameters:

        * ``pattern``: Regular expression to be matched. Any capturing
          groups in the regex will be passed in to the handler's
          get/post/etc methods as arguments (by keyword if named, by
          position if unnamed. Named and unnamed capturing groups
          may not be mixed in the same rule).

        * ``handler``: `~.web.RequestHandler` subclass to be invoked.

        * ``kwargs`` (optional): A dictionary of additional arguments
          to be passed to the handler's constructor.

        * ``name`` (optional): A name for this handler.  Used by
          `~.web.Application.reverse_url`.

        N)rZ   rk   r6   r   rd   handler_classr   )r   r   r   r   r0   ra   rl   r   r   r6     s
    zURLSpec.__init__rE   c                 C   s    d| j j| jj| j| j| jf S rs   )rm   r&   r   r   r   r   r0   rG   r   r   r   rt     s    zURLSpec.__repr__)NN)r&   r'   r(   r)   r   r3   r   r   r   r   r6   rt   rp   r   r   rl   r   r     s     
r   )r   r   c                 C   s   d S r#   r   r   r   r   r   r     s    r   c                 C   s   d S r#   r   r   r   r   r   r     s    c                 C   s   | dkr| S t | dddS )zNone-safe wrapper around url_unescape to handle unmatched optional
    groups correctly.

    Note that args are passed as bytes so the handler can decide what
    encoding to use.
    NF)encodingr   )r   r   r   r   r   r     s    )1r)   r|   	functoolsr   Ztornador   Ztornado.httpserverr   Ztornado.escaper   r   r   Ztornado.logr   Ztornado.utilr	   r
   r   r   typingr   r   r   r   r   r   r   r   r   rf   r   r.   r+   r$   rA   r3   rh   rN   ri   r,   rL   rM   rw   rx   r~   rZ   r   rK   r   r   r   r   r   <module>   sJ    $,.h%1a1