
    k7i                         S r SSKrSSKJrJr  SSKJr  SSKJr  SSK	r	SSK
Jr  SSKJr  SS	KJrJrJr   " S
 S\5      r " S S5      r " S S5      r " S S\5      r " S S\5      rg)zCRate limiting middleware for protecting FastMCP servers from abuse.    N)defaultdictdeque)Callable)Any)McpError)	ErrorData   )CallNext
MiddlewareMiddlewareContextc                   8   ^  \ rS rSrSrSS\4U 4S jjjrSrU =r$ )RateLimitError   z)Error raised when rate limit is exceeded.messagec                 4   > [         TU ]  [        SUS95        g )Ni )coder   )super__init__r   )selfr   	__class__s     a/home/james-whalen/.local/lib/python3.13/site-packages/fastmcp/server/middleware/rate_limiting.pyr   RateLimitError.__init__   s    @A     )zRate limit exceeded)	__name__
__module____qualname____firstlineno____doc__strr   __static_attributes____classcell__)r   s   @r   r   r      s    3B B Br   r   c                   @    \ rS rSrSrS\S\4S jrSS\S\4S jjr	S	r
g
)TokenBucketRateLimiter   z.Token bucket implementation for rate limiting.capacityrefill_ratec                     Xl         X l        Xl        [        R                  " 5       U l        [
        R                  " 5       U l        g)z~Initialize token bucket.

Args:
    capacity: Maximum number of tokens in the bucket
    refill_rate: Tokens added per second
N)r&   r'   tokenstimelast_refillanyioLock_lock)r   r&   r'   s      r   r   TokenBucketRateLimiter.__init__   s/     !&99;ZZ\
r   r)   returnc                   #    U R                    ISh  vN   [        R                  " 5       nX R                  -
  n[        U R                  U R
                  X0R                  -  -   5      U l        X l        U R
                  U:  a'  U =R
                  U-  sl         SSS5      ISh  vN   g SSS5      ISh  vN   g N N N	! , ISh  vN  (       d  f       g= f7f)zTry to consume tokens from the bucket.

Args:
    tokens: Number of tokens to consume

Returns:
    True if tokens were available and consumed, False otherwise
NTF)r.   r*   r+   minr&   r)   r'   )r   r)   nowelapseds       r   consumeTokenBucketRateLimiter.consume&   s      :::))+C,,,G dmmT[[7EUEU;U-UVDK"{{f$v% ::  :::::sc   CB>CBCC&C 'C,C-C8C9C CCC
CCC)r.   r&   r+   r'   r)   N)r	   )r   r   r   r   r   intfloatr   boolr5   r!   r   r   r   r$   r$      s0    8" "5 "C   r   r$   c                   8    \ rS rSrSrS\S\4S jrS\4S jrSr	g	)
SlidingWindowRateLimiter=   z+Sliding window rate limiter implementation.max_requestswindow_secondsc                 n    Xl         X l        [        5       U l        [        R
                  " 5       U l        g)zInitialize sliding window rate limiter.

Args:
    max_requests: Maximum requests allowed in the time window
    window_seconds: Time window in seconds
N)r=   r>   r   requestsr,   r-   r.   )r   r=   r>   s      r   r   !SlidingWindowRateLimiter.__init__@   s&     ),ZZ\
r   r0   c                 F  #    U R                    ISh  vN   [        R                  " 5       nXR                  -
  nU R                  (       aS  U R                  S   U:  a@  U R                  R	                  5         U R                  (       a  U R                  S   U:  a  M@  [        U R                  5      U R                  :  a-  U R                  R                  U5         SSS5      ISh  vN   g SSS5      ISh  vN   g N N N	! , ISh  vN  (       d  f       g= f7f)zCheck if a request is allowed.Nr   TF)r.   r*   r>   r@   popleftlenr=   append)r   r3   cutoffs      r   
is_allowed#SlidingWindowRateLimiter.is_allowedL   s     :::))+C...F --DMM!$4v$=%%' --DMM!$4v$= 4==!D$5$55$$S) ::  :::::si   D!DD!BD?DD!)D*D!/D0D!;D<D!D!D!DDDD!)r.   r=   r@   r>   N)
r   r   r   r   r   r7   r   r9   rG   r!   r   r   r   r;   r;   =   s$    5
"S 
"# 
"$ r   r;   c            	       ~    \ rS rSrSr    SS\S\S-  S\\/\	4   S-  S\
4S jjrS	\S
\	4S jrS	\S\S
\4S jrSrg)RateLimitingMiddleware\   a  Middleware that implements rate limiting to prevent server abuse.

Uses a token bucket algorithm by default, allowing for burst traffic
while maintaining a sustainable long-term rate.

Example:
    ```python
    from fastmcp.server.middleware.rate_limiting import RateLimitingMiddleware

    # Allow 10 requests per second with bursts up to 20
    rate_limiter = RateLimitingMiddleware(
        max_requests_per_second=10,
        burst_capacity=20
    )

    mcp = FastMCP("MyServer")
    mcp.add_middleware(rate_limiter)
    ```
Nmax_requests_per_secondburst_capacityget_client_idglobal_limitc                    ^  UT l         U=(       d    [        US-  5      T l        UT l        UT l        [        U 4S j5      T l        T R                  (       a&  [        T R                  T R                   5      T l        gg)an  Initialize rate limiting middleware.

Args:
    max_requests_per_second: Sustained requests per second allowed
    burst_capacity: Maximum burst capacity. If None, defaults to 2x max_requests_per_second
    get_client_id: Function to extract client ID from context. If None, uses global limiting
    global_limit: If True, apply limit globally; if False, per-client
   c                  D   > [        T R                  T R                  5      $ N)r$   rM   rL   r   s   r   <lambda>1RateLimitingMiddleware.__init__.<locals>.<lambda>   s    *##T%A%Ar   N)	rL   r7   rM   rN   rO   r   limitersr$   global_limiter)r   rL   rM   rN   rO   s   `    r   r   RateLimitingMiddleware.__init__q   sw     (?$,P4Ka4O0P*( <G<
 "8##T%A%A#D r   contextr0   c                 H    U R                   (       a  U R                  U5      $ gz(Get client identifier for rate limiting.globalrN   r   rZ   s     r   _get_client_identifier-RateLimitingMiddleware._get_client_identifier       %%g..r   	call_nextc                 \  #    U R                   (       a5  U R                  R                  5       I Sh  vN nU(       d  [        S5      eOMU R	                  U5      nU R
                  U   nUR                  5       I Sh  vN nU(       d  [        SU 35      eU" U5      I Sh  vN $  Nt N+ N7f)z Apply rate limiting to requests.NzGlobal rate limit exceededz Rate limit exceeded for client: )rO   rX   r5   r   r`   rW   )r   rZ   rc   allowed	client_idlimiters         r   
on_request!RateLimitingMiddleware.on_request   s      //7799G$%ABB  33G<ImmI.G#OO--G$'G	{%STTw''' : . (s4   /B,B&A
B,<B(=$B,!B*"B,(B,*B,)rM   rN   rO   rX   rW   rL   )g      $@NNF)r   r   r   r   r   r8   r7   r   r   r    r9   r   r`   r
   r   rh   r!   r   r   r   rJ   rJ   \   s    , *.%)CG"!& d
  !2 3S 89D@	
 B.? C ((9 (h (SV (r   rJ   c            	       p    \ rS rSrSr  SS\S\S\\/\4   S-  4S jjr	S\S	\4S
 jr
S\S\S	\4S jrSrg)#SlidingWindowRateLimitingMiddleware   a  Middleware that implements sliding window rate limiting.

Uses a sliding window approach which provides more precise rate limiting
but uses more memory to track individual request timestamps.

Example:
    ```python
    from fastmcp.server.middleware.rate_limiting import SlidingWindowRateLimitingMiddleware

    # Allow 100 requests per minute
    rate_limiter = SlidingWindowRateLimitingMiddleware(
        max_requests=100,
        window_minutes=1
    )

    mcp = FastMCP("MyServer")
    mcp.add_middleware(rate_limiter)
    ```
Nr=   window_minutesrN   c                 ^   ^  UT l         US-  T l        UT l        [        U 4S j5      T l        g)zInitialize sliding window rate limiting middleware.

Args:
    max_requests: Maximum requests allowed in the time window
    window_minutes: Time window in minutes
    get_client_id: Function to extract client ID from context
<   c                  D   > [        T R                  T R                  5      $ rS   )r;   r=   r>   rT   s   r   rU   >SlidingWindowRateLimitingMiddleware.__init__.<locals>.<lambda>   s    ,T->->@S@STr   N)r=   r>   rN   r   rW   )r   r=   rm   rN   s   `   r   r   ,SlidingWindowRateLimitingMiddleware.__init__   s4     ),r1* >IT>
r   rZ   r0   c                 H    U R                   (       a  U R                  U5      $ gr\   r^   r_   s     r   r`   :SlidingWindowRateLimitingMiddleware._get_client_identifier   rb   r   rc   c                   #    U R                  U5      nU R                  U   nUR                  5       I Sh  vN nU(       d+  [        SU R                   SU R
                  S-   SU 35      eU" U5      I Sh  vN $  NF N7f)z/Apply sliding window rate limiting to requests.NzRate limit exceeded: z requests per ro   z minutes for client: )r`   rW   rG   r   r=   r>   )r   rZ   rc   rf   rg   re   s         r   rh   .SlidingWindowRateLimitingMiddleware.on_request   s     //8	--	***,, '(9(9':.&&",--B9+O 
 w''' - (s"   4BA=AB8A?9B?B)rN   rW   r=   r>   )r	   N)r   r   r   r   r   r7   r   r   r    r   r`   r
   r   rh   r!   r   r   r   rk   rk      ss    .  CG	

 
  !2 3S 89D@	
,.? C ((9 (h (SV (r   rk   )r   r*   collectionsr   r   collections.abcr   typingr   r,   mcpr   	mcp.typesr   
middlewarer
   r   r   r   r$   r;   rJ   rk   r   r   r   <module>r}      sa    I  * $     ? ?BX B$ $N >K(Z K(\=(* =(r   