1 | <?php |
||
19 | class RateLimitAnnotationListener extends BaseListener |
||
20 | { |
||
21 | |||
22 | /** |
||
23 | * @var EventDispatcherInterface | LegacyEventDispatcherInterface |
||
24 | */ |
||
25 | protected $eventDispatcher; |
||
26 | |||
27 | /** |
||
28 | * @var \Noxlogic\RateLimitBundle\Service\RateLimitService |
||
29 | */ |
||
30 | protected $rateLimitService; |
||
31 | |||
32 | /** |
||
33 | * @var \Noxlogic\RateLimitBundle\Util\PathLimitProcessor |
||
34 | */ |
||
35 | protected $pathLimitProcessor; |
||
36 | |||
37 | /** |
||
38 | * @param RateLimitService $rateLimitService |
||
39 | */ |
||
40 | 19 | public function __construct( |
|
49 | |||
50 | /** |
||
51 | * @param ProxyFilterControllerEvent $event |
||
52 | */ |
||
53 | 15 | public function onKernelController(ProxyFilterControllerEvent $event) |
|
54 | { |
||
55 | // Skip if the bundle isn't enabled (for instance in test environment) |
||
56 | 15 | if( ! $this->getParameter('enabled', true)) { |
|
57 | 1 | return; |
|
58 | } |
||
59 | |||
60 | // Skip if we aren't the main request |
||
61 | 14 | if ($event->getRequestType() != HttpKernelInterface::MASTER_REQUEST) { |
|
62 | 1 | return; |
|
63 | } |
||
64 | |||
65 | // Find the best match |
||
66 | 13 | $annotations = $event->getRequest()->attributes->get('_x-rate-limit', array()); |
|
67 | 13 | $rateLimit = $this->findBestMethodMatch($event->getRequest(), $annotations); |
|
68 | |||
69 | // Another treatment before applying RateLimit ? |
||
70 | 13 | $checkedRateLimitEvent = new CheckedRateLimitEvent($event->getRequest(), $rateLimit); |
|
71 | 13 | $this->dispatch(RateLimitEvents::CHECKED_RATE_LIMIT, $checkedRateLimitEvent); |
|
72 | 13 | $rateLimit = $checkedRateLimitEvent->getRateLimit(); |
|
73 | |||
74 | // No matching annotation found |
||
75 | 13 | if (! $rateLimit) { |
|
76 | 3 | return; |
|
77 | } |
||
78 | |||
79 | 10 | $key = $this->getKey($event, $rateLimit, $annotations); |
|
80 | |||
81 | // Ratelimit the call |
||
82 | 10 | $rateLimitInfo = $this->rateLimitService->limitRate($key); |
|
83 | 10 | if (! $rateLimitInfo) { |
|
84 | // Create new rate limit entry for this call |
||
85 | 5 | $rateLimitInfo = $this->rateLimitService->createRate($key, $rateLimit->getLimit(), $rateLimit->getPeriod()); |
|
86 | 5 | if (! $rateLimitInfo) { |
|
87 | // @codeCoverageIgnoreStart |
||
88 | return; |
||
89 | // @codeCoverageIgnoreEnd |
||
90 | } |
||
91 | } |
||
92 | |||
93 | |||
94 | // Store the current rating info in the request attributes |
||
95 | 9 | $request = $event->getRequest(); |
|
96 | 9 | $request->attributes->set('rate_limit_info', $rateLimitInfo); |
|
97 | |||
98 | // Reset the rate limits |
||
99 | 9 | if(time() >= $rateLimitInfo->getResetTimestamp()) { |
|
100 | 1 | $this->rateLimitService->resetRate($key); |
|
101 | 1 | $rateLimitInfo = $this->rateLimitService->createRate($key, $rateLimit->getLimit(), $rateLimit->getPeriod()); |
|
102 | 1 | if (! $rateLimitInfo) { |
|
103 | // @codeCoverageIgnoreStart |
||
104 | return; |
||
105 | // @codeCoverageIgnoreEnd |
||
106 | } |
||
107 | } |
||
108 | |||
109 | // When we exceeded our limit, return a custom error response |
||
110 | 9 | if ($rateLimitInfo->getCalls() > $rateLimitInfo->getLimit()) { |
|
111 | |||
112 | // Throw an exception if configured. |
||
113 | 5 | if ($this->getParameter('rate_response_exception')) { |
|
114 | 2 | $class = $this->getParameter('rate_response_exception'); |
|
115 | |||
116 | 2 | $e = new $class($this->getParameter('rate_response_message'), $this->getParameter('rate_response_code')); |
|
117 | |||
118 | 2 | if ($e instanceof RateLimitExceptionInterface) { |
|
119 | 1 | $e->setPayload($rateLimit->getPayload()); |
|
120 | } |
||
121 | |||
122 | 2 | throw $e; |
|
123 | } |
||
124 | |||
125 | 3 | $message = $this->getParameter('rate_response_message'); |
|
126 | 3 | $code = $this->getParameter('rate_response_code'); |
|
127 | $event->setController(function () use ($message, $code) { |
||
128 | // @codeCoverageIgnoreStart |
||
129 | return new Response($message, $code); |
||
130 | // @codeCoverageIgnoreEnd |
||
131 | 3 | }); |
|
132 | 3 | $event->stopPropagation(); |
|
|
|||
133 | } |
||
134 | |||
135 | 7 | } |
|
136 | |||
137 | |||
138 | /** |
||
139 | * @param RateLimit[] $annotations |
||
140 | */ |
||
141 | 17 | protected function findBestMethodMatch(Request $request, array $annotations) |
|
165 | |||
166 | 10 | private function getKey(ProxyFilterControllerEvent $event, RateLimit $rateLimit, array $annotations) |
|
183 | |||
184 | 9 | private function getAliasForRequest(ProxyFilterControllerEvent $event) |
|
210 | |||
211 | 13 | private function dispatch($eventName, $event) |
|
221 | |||
222 | } |
||
223 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.