Rate-limiting Middleware for Warp
Rate-limiting middleware for Warp. Implements RFC 6585 for "429 Too many Requests" response and RFC 7231 for Retry After header.
This crate provides RFC 6585 and RFC 7231 compliant in-memory rate limiting with configurable windows and limits as lightweight middleware for Warp web applications.
It provides a Filter you can add to your routes that exposes rate-limiting
information to your handlers, and a rate limited Rejection type for error recovery.
It does not yet provide persistence, nor is the HashMap that stores IPs bounded. Both of these may be changed in a future version.
Quickstart
- Include the crate:
cargo add warp-rate-limit
- Define one or more rate limit configurations. Following are some examples of available builder methods. The variable names are arbitrary:
// Limit: 60 requests per 60 Earth seconds
let public_routes_rate_limit = default;
// Limit: 100 requests per 60 Earth seconds
let partner_routes_rate_limit = max_per_minute;
// Limit: 10 requests per 20 Earth seconds
let static_route_limit = max_per_window;
- Add the rate-limiting
Filterto your route, which exposes aRateLimitInfostruct to your handler:
let my_route = path!
.and
// - - -- --- ----- -------- ------------- ---------------------
.and
// - - -- --- ----- -------- ------------- ---------------------
.and_then
.recover
- Use the
RateLimitInfodata in your request handler. If you don't want to use rate-limiting information related to the IP address associated with this request, you can skip this part (but warp requires that you still account for theRateLimitInfoin your handler signature):
// Example route handler
async
- Handle rate limit errors in your rejection handler:
// Example rejection handler
async
Builder methods
| Usage | Description |
|---|---|
RateLimitConfig::default() |
Max requests: 60/minute |
RateLimitConfig::max_per_minute(x:u32) |
Max requests: x/minute |
RateLimitConfig::max_per_window(max:u32,window:u64) |
Max requests: max/window (in seconds) |
Reference
with_rate_limit(config: RateLimitConfig): given yourRateLimitConfig, injects aFilterinto your route that exposes aRateLimitInfostruct to your handler.add_rate_limit_headers(&mut HeaderMap, &RateLimitInfo): given a mutable reference to the headers of aResponse(e.g.,response.headers_mut()) and a reference to a populatedRateLimitInfostruct, adds headers related to rate-limiting to theResponsereference provided. Headers can be included in both successful replies (e.g.,200) as well as rate-limited responses (e.g.,429). The requiredRateLimitInfostruct comes from either theFilterthat injects it into your handler, or manually in your rejection recovery handler viaget_rate_limit_info().get_rate_limit_info(&RateLimitRejection): given aRejectionthat includes aRateLimitRejection(e.g.,if let Some(rate_limited_rejection) = rejection.find::<RateLimitRejection>()), return aRateLimitInfostruct that contains information related to the currently rate-limited IP address. This is useful for letting the requestor know that they are being rate-limited, as well as when their rate limit will be released.
Rate-limited headers
An example of headers provided in response to a rate-limited requesting IP:
HTTP/1.1 429 Too Many Requests
retry-after: Wed, 1 Jan 2025 00:01:00 GMT
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
x-ratelimit-reset: 1704067260
Error handling
The Quickstart example shows a form of error handling appropriate in situations
where you do not care to handle errors that may occur in this library. Following are more
error-handling examples, straight from the basic.rs example:
// If you really don't care about the error, you can do something like this:
let _ = add_rate_limit_headers;
// If you care about the fact that it errored but not necessarily
// the specific error itself, you can do something like this:
if !add_rate_limit_headers.is_ok
// If you want full control over error handling, you can do something like this:
if let Err = add_rate_limit_headers
You only need to call add_rate_limit_headers() once; the above example illustrates
three different ways to do the same thing, with varying levels of library error recovery
comfort.
License
Released under MIT License.
LICENSE
Copyright (c) 2024 Jesse Lawson.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Contributing
Enhancements and fixes are welcome and encouraged. Please open an Issue in this repository to discuss changes that would significantly alter how this library is used.
Testing
Run the test suite:
Try the examples: