6 releases (3 breaking)

0.4.0 Aug 7, 2024
0.3.1 Jan 21, 2024
0.2.1 Apr 18, 2022
0.1.1 Apr 15, 2022

#92 in HTTP server

Download history 281/week @ 2024-07-23 291/week @ 2024-07-30 324/week @ 2024-08-06 232/week @ 2024-08-13 181/week @ 2024-08-20 198/week @ 2024-08-27 317/week @ 2024-09-03 284/week @ 2024-09-10 310/week @ 2024-09-17 380/week @ 2024-09-24 354/week @ 2024-10-01 303/week @ 2024-10-08 389/week @ 2024-10-15 178/week @ 2024-10-22 328/week @ 2024-10-29 306/week @ 2024-11-05

1,278 downloads per month
Used in 3 crates

MIT/Apache

60KB
1K SLoC

Actix Extensible Rate Limit

Build status crates.io docs.rs

An attempt at a more flexible rate limiting middleware for actix-web

Allows for:

  • Deriving a custom rate limit key from the request context.
  • Using dynamic rate limits and intervals determined by the request context.
  • Using custom backends (store & algorithm)
  • Setting a custom 429 response.
  • Transforming the response headers based on rate limit results (e.g x-ratelimit-remaining).
  • Rolling back rate limit counts based on response codes.

Provided Backends

Backend Algorithm Store
InMemoryBackend Fixed Window Dashmap
RedisBackend Fixed Window Redis

Getting Started

use actix_web::{App, HttpServer};
use actix_extensible_rate_limit::{
    backend::{memory::InMemoryBackend, SimpleInputFunctionBuilder},
    RateLimiter,
};
use std::time::Duration;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // A backend is responsible for storing rate limit data, and choosing whether to allow/deny requests
    let backend = InMemoryBackend::builder().build();

    HttpServer::new(move || {
        // Assign a limit of 5 requests per minute per client ip address
        let input = SimpleInputFunctionBuilder::new(Duration::from_secs(60), 5)
            .real_ip_key()
            .build();
        let middleware = RateLimiter::builder(backend.clone(), input)
            .add_headers()
            .build();
        App::new().wrap(middleware)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Try it out:

$ curl -v http://127.0.0.1:8080
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.83.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< content-length: 0
< x-ratelimit-limit: 5
< x-ratelimit-reset: 60
< x-ratelimit-remaining: 4
< date: Sun, 21 Jan 2024 16:52:27 GMT
<
* Connection #0 to host 127.0.0.1 left intact

Dependencies

~16–27MB
~456K SLoC