Вход Регистрация
Файл: vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php
Строк: 378
<?php

namespace IlluminateRoutingMiddleware;

use 
Closure;
use 
IlluminateCacheRateLimiter;
use 
IlluminateCacheRateLimitingUnlimited;
use 
IlluminateHttpExceptionsHttpResponseException;
use 
IlluminateHttpExceptionsThrottleRequestsException;
use 
IlluminateSupportArr;
use 
IlluminateSupportInteractsWithTime;
use 
RuntimeException;
use 
SymfonyComponentHttpFoundationResponse;

class 
ThrottleRequests
{
    use 
InteractsWithTime;

    
/**
     * The rate limiter instance.
     *
     * @var IlluminateCacheRateLimiter
     */
    
protected $limiter;

    
/**
     * Indicates if the rate limiter keys should be hashed.
     *
     * @var bool
     */
    
protected static $shouldHashKeys true;

    
/**
     * Create a new request throttler.
     *
     * @param  IlluminateCacheRateLimiter  $limiter
     * @return void
     */
    
public function __construct(RateLimiter $limiter)
    {
        
$this->limiter $limiter;
    }

    
/**
     * Specify the named rate limiter to use for the middleware.
     *
     * @param  string  $name
     * @return string
     */
    
public static function using($name)
    {
        return static::class.
':'.$name;
    }

    
/**
     * Specify the rate limiter configuration for the middleware.
     *
     * @param  int  $maxAttempts
     * @param  int  $decayMinutes
     * @param  string  $prefix
     * @return string
     *
     * @named-arguments-supported
     */
    
public static function with($maxAttempts 60$decayMinutes 1$prefix '')
    {
        return static::class.
':'.implode(','func_get_args());
    }

    
/**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @param  int|string  $maxAttempts
     * @param  float|int  $decayMinutes
     * @param  string  $prefix
     * @return SymfonyComponentHttpFoundationResponse
     *
     * @throws IlluminateHttpExceptionsThrottleRequestsException
     */
    
public function handle($requestClosure $next$maxAttempts 60$decayMinutes 1$prefix '')
    {
        if (
is_string($maxAttempts)
            && 
func_num_args() === 3
            
&& ! is_null($limiter $this->limiter->limiter($maxAttempts))) {
            return 
$this->handleRequestUsingNamedLimiter($request$next$maxAttempts$limiter);
        }

        return 
$this->handleRequest(
            
$request,
            
$next,
            [
                (object) [
                    
'key' => $prefix.$this->resolveRequestSignature($request),
                    
'maxAttempts' => $this->resolveMaxAttempts($request$maxAttempts),
                    
'decayMinutes' => $decayMinutes,
                    
'responseCallback' => null,
                ],
            ]
        );
    }

    
/**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @param  string  $limiterName
     * @param  Closure  $limiter
     * @return SymfonyComponentHttpFoundationResponse
     *
     * @throws IlluminateHttpExceptionsThrottleRequestsException
     */
    
protected function handleRequestUsingNamedLimiter($requestClosure $next$limiterNameClosure $limiter)
    {
        
$limiterResponse $limiter($request);

        if (
$limiterResponse instanceof Response) {
            return 
$limiterResponse;
        } elseif (
$limiterResponse instanceof Unlimited) {
            return 
$next($request);
        }

        return 
$this->handleRequest(
            
$request,
            
$next,
            
collect(Arr::wrap($limiterResponse))->map(function ($limit) use ($limiterName) {
                return (object) [
                    
'key' => self::$shouldHashKeys md5($limiterName.$limit->key) : $limiterName.':'.$limit->key,
                    
'maxAttempts' => $limit->maxAttempts,
                    
'decayMinutes' => $limit->decayMinutes,
                    
'responseCallback' => $limit->responseCallback,
                ];
            })->
all()
        );
    }

    
/**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @param  array  $limits
     * @return SymfonyComponentHttpFoundationResponse
     *
     * @throws IlluminateHttpExceptionsThrottleRequestsException
     */
    
protected function handleRequest($requestClosure $next, array $limits)
    {
        foreach (
$limits as $limit) {
            if (
$this->limiter->tooManyAttempts($limit->key$limit->maxAttempts)) {
                throw 
$this->buildException($request$limit->key$limit->maxAttempts$limit->responseCallback);
            }

            
$this->limiter->hit($limit->key$limit->decayMinutes 60);
        }

        
$response $next($request);

        foreach (
$limits as $limit) {
            
$response $this->addHeaders(
                
$response,
                
$limit->maxAttempts,
                
$this->calculateRemainingAttempts($limit->key$limit->maxAttempts)
            );
        }

        return 
$response;
    }

    
/**
     * Resolve the number of attempts if the user is authenticated or not.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  int|string  $maxAttempts
     * @return int
     */
    
protected function resolveMaxAttempts($request$maxAttempts)
    {
        if (
str_contains($maxAttempts'|')) {
            
$maxAttempts explode('|'$maxAttempts2)[$request->user() ? 0];
        }

        if (! 
is_numeric($maxAttempts) && $request->user()) {
            
$maxAttempts $request->user()->{$maxAttempts};
        }

        return (int) 
$maxAttempts;
    }

    
/**
     * Resolve request signature.
     *
     * @param  IlluminateHttpRequest  $request
     * @return string
     *
     * @throws RuntimeException
     */
    
protected function resolveRequestSignature($request)
    {
        if (
$user $request->user()) {
            return 
$this->formatIdentifier($user->getAuthIdentifier());
        } elseif (
$route $request->route()) {
            return 
$this->formatIdentifier($route->getDomain().'|'.$request->ip());
        }

        throw new 
RuntimeException('Unable to generate the request signature. Route unavailable.');
    }

    
/**
     * Create a 'too many attempts' exception.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  string  $key
     * @param  int  $maxAttempts
     * @param  callable|null  $responseCallback
     * @return IlluminateHttpExceptionsThrottleRequestsException|IlluminateHttpExceptionsHttpResponseException
     */
    
protected function buildException($request$key$maxAttempts$responseCallback null)
    {
        
$retryAfter $this->getTimeUntilNextRetry($key);

        
$headers $this->getHeaders(
            
$maxAttempts,
            
$this->calculateRemainingAttempts($key$maxAttempts$retryAfter),
            
$retryAfter
        
);

        return 
is_callable($responseCallback)
                    ? new 
HttpResponseException($responseCallback($request$headers))
                    : new 
ThrottleRequestsException('Too Many Attempts.'null$headers);
    }

    
/**
     * Get the number of seconds until the next retry.
     *
     * @param  string  $key
     * @return int
     */
    
protected function getTimeUntilNextRetry($key)
    {
        return 
$this->limiter->availableIn($key);
    }

    
/**
     * Add the limit header information to the given response.
     *
     * @param  SymfonyComponentHttpFoundationResponse  $response
     * @param  int  $maxAttempts
     * @param  int  $remainingAttempts
     * @param  int|null  $retryAfter
     * @return SymfonyComponentHttpFoundationResponse
     */
    
protected function addHeaders(Response $response$maxAttempts$remainingAttempts$retryAfter null)
    {
        
$response->headers->add(
            
$this->getHeaders($maxAttempts$remainingAttempts$retryAfter$response)
        );

        return 
$response;
    }

    
/**
     * Get the limit headers information.
     *
     * @param  int  $maxAttempts
     * @param  int  $remainingAttempts
     * @param  int|null  $retryAfter
     * @param  SymfonyComponentHttpFoundationResponse|null  $response
     * @return array
     */
    
protected function getHeaders($maxAttempts,
                                  
$remainingAttempts,
                                  
$retryAfter null,
                                  ?
Response $response null)
    {
        if (
$response &&
            ! 
is_null($response->headers->get('X-RateLimit-Remaining')) &&
            (int) 
$response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
            return [];
        }

        
$headers = [
            
'X-RateLimit-Limit' => $maxAttempts,
            
'X-RateLimit-Remaining' => $remainingAttempts,
        ];

        if (! 
is_null($retryAfter)) {
            
$headers['Retry-After'] = $retryAfter;
            
$headers['X-RateLimit-Reset'] = $this->availableAt($retryAfter);
        }

        return 
$headers;
    }

    
/**
     * Calculate the number of remaining attempts.
     *
     * @param  string  $key
     * @param  int  $maxAttempts
     * @param  int|null  $retryAfter
     * @return int
     */
    
protected function calculateRemainingAttempts($key$maxAttempts$retryAfter null)
    {
        return 
is_null($retryAfter) ? $this->limiter->retriesLeft($key$maxAttempts) : 0;
    }

    
/**
     * Format the given identifier based on the configured hashing settings.
     *
     * @param  string  $value
     * @return string
     */
    
private function formatIdentifier($value)
    {
        return 
self::$shouldHashKeys sha1($value) : $value;
    }

    
/**
     * Specify whether rate limiter keys should be hashed.
     *
     * @param  bool  $shouldHashKeys
     * @return void
     */
    
public static function shouldHashKeys(bool $shouldHashKeys true)
    {
        
self::$shouldHashKeys $shouldHashKeys;
    }
}
Онлайн: 0
Реклама