Completed
Push — master ( cd6aa3...db07f0 )
by Jitendra
13s
created

Throttle::handle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 0
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace PhalconExt\Http\Middleware;
4
5
use Phalcon\Http\Request;
1 ignored issue
show
Bug introduced by
The type Phalcon\Http\Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Phalcon\Http\Response;
1 ignored issue
show
Bug introduced by
The type Phalcon\Http\Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use PhalconExt\Http\BaseMiddleware;
8
9
class Throttle extends BaseMiddleware
10
{
11
    /** @var string */
12
    protected $redis;
13
14
    protected $configKey = 'throttle';
15
16
    /**
17
     * Handle the throttle.
18
     *
19
     * @param Request  $request
20
     * @param Response $response
21
     *
22
     * @return bool
23
     */
24
    public function before(Request $request, Response $response): bool
25
    {
26
        if (null === $retryKey = $this->findRetryKey($request)) {
27
            return true;
28
        }
29
30
        $this->disableView();
31
32
        $after = \ceil($this->di('redis')->getTtl($retryKey) / 60);
33
34
        $response
35
            ->setContent("Too many requests. Try again in $after min.")
36
            ->setHeader('Retry-After', $after)
37
            ->setStatusCode(429)
38
            ->send();
39
40
        return false;
41
    }
42
43
    /**
44
     * Find the redis key that contains hits counter which has exceeded threshold for throttle.
45
     *
46
     * @param Request $request
47
     *
48
     * @return null|string
49
     */
50
    protected function findRetryKey(Request $request): ?string
51
    {
52
        $retryKey = null;
53
        $redis    = $this->di('redis');
54
        $baseKey  = $this->getKey($request);
55
56
        foreach ($this->config['maxHits'] as $minutes => $maxHits) {
57
            $key  = "$baseKey:$minutes";
58
            $hits = $redis->exists($key) ? $redis->get($key) : 0;
59
            $ttl  = $hits ? $redis->getTtl($key) : $minutes * 60;
60
61
            if (null === $retryKey && $hits >= $maxHits) {
62
                $retryKey = $key;
63
64
                continue;
65
            }
66
67
            $redis->save($key, $hits + 1, $ttl);
68
        }
69
70
        return $retryKey;
71
    }
72
73
    /**
74
     * Get the unique key for this client.
75
     *
76
     * @param Request $request
77
     *
78
     * @return string
79
     */
80
    protected function getKey(Request $request): string
81
    {
82
        $key = $request->getClientAddress(true);
83
84
        if ($this->config['checkUserAgent'] ?? false) {
85
            $key .= '_' . \md5($request->getUserAgent());
86
        }
87
88
        return ($this->config['prefix'] ?? '') . $key;
89
    }
90
}
91