Description
This is something I've considered while working on the original RateLimiter PR, but decided not to do (yet). Yet, ever since the component is merged, I'm wondering if we shouldn't add it.
Currently, LimiterInterface#consume()
returns a boolean. This means that, in the case of false
, the process (or client) has no idea when it should retry. The token bucket limiter has the benefit of being able to "reserve" tokens in the future, allowing IO blocking ($limiter->reserve()->wait()
), the fixed window limiter does not have this benefit.
Use-cases of exposing rate limiting state
- There is an IETF Internet-draft on rate limiting HTTP headers (
RateLimit-Limit
,RateLimit-Remaining
,RateLimit-Reset
). Exposing the state allows implementing these headers in your application. - The login throttler at this moment says "[...] please try again later.". It would be much more user friendly to be able to say "please try again in ... minutes".
- Processes that "poll" the limiter if they are denied, the block time is less arbitrary and can be controlled by the limiter.
We can see that for instance the Laravel rate limiter implementation is exposing the state for these cases (ref).
Reasons not to expose rate limiter state
-
The values should never be trusted. The hitcount can be increased by other clients between fetching it from the limiter and using it to render a response. Also, any wait time returned cannot guarantee that the process will succeed afterwards, maybe other processes already burst the limit before the current process.
-
Exposing the limiter state to clients can result in malicious usage, as also described by the Internet-Draft:
7.6. Denial of Service
"RateLimit" header fields may assume unexpected values by chance or purpose. For example, an excessively high "RateLimit-Remaining" value may be:
- used by a malicious intermediary to trigger a Denial of Service attack or consume client resources boosting its requests;
- passed by a misconfigured server;
or an high "RateLimit-Reset" value could inhibit clients to contact the server.
Clients MUST validate the received values to mitigate those risks.
-
The DX of the limiter will be a bit less great, as the object needs to be transformed to a boolean:
// before if ($limiter->consume()) { // after if ($limiter->consume()->ok()) {