Commaaa2
This commit is contained in:
@@ -4,6 +4,7 @@ namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class SubstituteBindings
|
||||
{
|
||||
@@ -34,9 +35,17 @@ class SubstituteBindings
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$this->router->substituteBindings($route = $request->route());
|
||||
try {
|
||||
$this->router->substituteBindings($route = $request->route());
|
||||
|
||||
$this->router->substituteImplicitBindings($route);
|
||||
$this->router->substituteImplicitBindings($route);
|
||||
} catch (ModelNotFoundException $exception) {
|
||||
if ($route->getMissing()) {
|
||||
return $route->getMissing()($request, $exception);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Cache\RateLimiting\Unlimited;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -46,22 +49,92 @@ class ThrottleRequests
|
||||
*/
|
||||
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
|
||||
{
|
||||
$key = $prefix.$this->resolveRequestSignature($request);
|
||||
|
||||
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
|
||||
|
||||
if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
|
||||
throw $this->buildException($key, $maxAttempts);
|
||||
if (is_string($maxAttempts)
|
||||
&& func_num_args() === 3
|
||||
&& ! is_null($limiter = $this->limiter->limiter($maxAttempts))) {
|
||||
return $this->handleRequestUsingNamedLimiter($request, $next, $maxAttempts, $limiter);
|
||||
}
|
||||
|
||||
$this->limiter->hit($key, $decayMinutes * 60);
|
||||
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 \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string $limiterName
|
||||
* @param \Closure $limiter
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function handleRequestUsingNamedLimiter($request, Closure $next, $limiterName, Closure $limiter)
|
||||
{
|
||||
$limiterResponse = call_user_func($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' => md5($limiterName.$limit->key),
|
||||
'maxAttempts' => $limit->maxAttempts,
|
||||
'decayMinutes' => $limit->decayMinutes,
|
||||
'responseCallback' => $limit->responseCallback,
|
||||
];
|
||||
})->all()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param array $limits
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function handleRequest($request, Closure $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);
|
||||
|
||||
return $this->addHeaders(
|
||||
$response, $maxAttempts,
|
||||
$this->calculateRemainingAttempts($key, $maxAttempts)
|
||||
);
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,9 +169,7 @@ class ThrottleRequests
|
||||
{
|
||||
if ($user = $request->user()) {
|
||||
return sha1($user->getAuthIdentifier());
|
||||
}
|
||||
|
||||
if ($route = $request->route()) {
|
||||
} elseif ($route = $request->route()) {
|
||||
return sha1($route->getDomain().'|'.$request->ip());
|
||||
}
|
||||
|
||||
@@ -108,11 +179,13 @@ class ThrottleRequests
|
||||
/**
|
||||
* Create a 'too many attempts' exception.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param callable|null $responseCallback
|
||||
* @return \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function buildException($key, $maxAttempts)
|
||||
protected function buildException($request, $key, $maxAttempts, $responseCallback = null)
|
||||
{
|
||||
$retryAfter = $this->getTimeUntilNextRetry($key);
|
||||
|
||||
@@ -122,9 +195,9 @@ class ThrottleRequests
|
||||
$retryAfter
|
||||
);
|
||||
|
||||
return new ThrottleRequestsException(
|
||||
'Too Many Attempts.', null, $headers
|
||||
);
|
||||
return is_callable($responseCallback)
|
||||
? new HttpResponseException($responseCallback($request, $headers))
|
||||
: new ThrottleRequestsException('Too Many Attempts.', null, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +223,7 @@ class ThrottleRequests
|
||||
protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
|
||||
{
|
||||
$response->headers->add(
|
||||
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter)
|
||||
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter, $response)
|
||||
);
|
||||
|
||||
return $response;
|
||||
@@ -162,10 +235,20 @@ class ThrottleRequests
|
||||
* @param int $maxAttempts
|
||||
* @param int $remainingAttempts
|
||||
* @param int|null $retryAfter
|
||||
* @param \Symfony\Component\HttpFoundation\Response|null $response
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null)
|
||||
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,
|
||||
@@ -189,10 +272,6 @@ class ThrottleRequests
|
||||
*/
|
||||
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
|
||||
{
|
||||
if (is_null($retryAfter)) {
|
||||
return $this->limiter->retriesLeft($key, $maxAttempts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return is_null($retryAfter) ? $this->limiter->retriesLeft($key, $maxAttempts) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Illuminate\Routing\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||
use Illuminate\Redis\Limiters\DurationLimiter;
|
||||
|
||||
@@ -16,27 +17,30 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* The timestamp of the end of the current duration.
|
||||
* The timestamp of the end of the current duration by key.
|
||||
*
|
||||
* @var int
|
||||
* @var array
|
||||
*/
|
||||
public $decaysAt;
|
||||
public $decaysAt = [];
|
||||
|
||||
/**
|
||||
* The number of remaining slots.
|
||||
* The number of remaining slots by key.
|
||||
*
|
||||
* @var int
|
||||
* @var array
|
||||
*/
|
||||
public $remaining;
|
||||
public $remaining = [];
|
||||
|
||||
/**
|
||||
* Create a new request throttler.
|
||||
*
|
||||
* @param \Illuminate\Cache\RateLimiter $limiter
|
||||
* @param \Illuminate\Contracts\Redis\Factory $redis
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Redis $redis)
|
||||
public function __construct(RateLimiter $limiter, Redis $redis)
|
||||
{
|
||||
parent::__construct($limiter);
|
||||
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
@@ -45,29 +49,30 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param int|string $maxAttempts
|
||||
* @param float|int $decayMinutes
|
||||
* @param string $prefix
|
||||
* @return mixed
|
||||
* @param array $limits
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
$key = $prefix.$this->resolveRequestSignature($request);
|
||||
|
||||
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
|
||||
|
||||
if ($this->tooManyAttempts($key, $maxAttempts, $decayMinutes)) {
|
||||
throw $this->buildException($key, $maxAttempts);
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
return $this->addHeaders(
|
||||
$response, $maxAttempts,
|
||||
$this->calculateRemainingAttempts($key, $maxAttempts)
|
||||
);
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +89,8 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
$this->redis, $key, $maxAttempts, $decayMinutes * 60
|
||||
);
|
||||
|
||||
return tap(! $limiter->acquire(), function () use ($limiter) {
|
||||
[$this->decaysAt, $this->remaining] = [
|
||||
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
||||
[$this->decaysAt[$key], $this->remaining[$key]] = [
|
||||
$limiter->decaysAt, $limiter->remaining,
|
||||
];
|
||||
});
|
||||
@@ -101,11 +106,7 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*/
|
||||
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
|
||||
{
|
||||
if (is_null($retryAfter)) {
|
||||
return $this->remaining;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return is_null($retryAfter) ? $this->remaining[$key] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +117,6 @@ class ThrottleRequestsWithRedis extends ThrottleRequests
|
||||
*/
|
||||
protected function getTimeUntilNextRetry($key)
|
||||
{
|
||||
return $this->decaysAt - $this->currentTime();
|
||||
return $this->decaysAt[$key] - $this->currentTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ class ValidateSignature
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $relative
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Routing\Exceptions\InvalidSignatureException
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle($request, Closure $next, $relative = null)
|
||||
{
|
||||
if ($request->hasValidSignature()) {
|
||||
if ($request->hasValidSignature($relative !== 'relative')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user