Giter VIP home page Giter VIP logo

actix-governor's Introduction

CI Docs crates.io

Actix Governor

A middleware for actix-web that provides rate-limiting backed by governor.

Features:

  • Simple to use
  • High customizability
  • High performance
  • Robust yet flexible API

Example

use actix_governor::{Governor, GovernorConfigBuilder};
use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
    "Hello world!"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Allow bursts with up to five requests per IP address
    // and replenishes one element every two seconds
    let governor_conf = GovernorConfigBuilder::default()
        .per_second(2)
        .burst_size(5)
        .finish()
        .unwrap();

    HttpServer::new(move || {
        App::new()
            // Enable Governor middleware
            .wrap(Governor::new(&governor_conf))
            // Route hello world service
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Add this to your Cargo.toml:

[dependencies]
actix-governor = "0.4"

actix-governor's People

Contributors

aaronerhardt avatar blasrodri avatar dependabot-preview[bot] avatar dsferruzza avatar evenorog avatar iyzana avatar photonquantum avatar robjtede avatar theawiteb avatar tobiasdebruijn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

actix-governor's Issues

Apply the ratelimit to only certain request methods?

Hi there,

I've run into the following scenario:

  1. Ratelimit with Actix-governor
  2. Browser makes too many requests
  3. Eventually, an OPTIONS request will fail (and causes CORS errors),
  4. Lots of errors

I think a potential solution would be to allow users to config the request method the ratelimit should apply to. E.g I might only want it on GET and POST, and maybe a different ratelimit for OPTIONS.

I could not see a way to currently do this, if there is any, please do tell!

A method to implement it could look something like this for the end user

GovernorConfigBuilder::default()
        .method(vec![Method::Get, Method::Post])
        .per_second(2)
        .burst_size(5)
        .finish()
        .unwrap(); // Save because the config is hardcoded

Where it by default includes all methods

Thanks!

Unable to use custom KeyExtractor with governor 0.5.1

code:

pub struct BiliUserToken;

impl KeyExtractor for BiliUserToken {
    type Key = String;
    type KeyExtractionError = SimpleKeyExtractionError<&'static str>;

    fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
        let key = match QString::from(req.query_string()).get("access_key") {
            Option::Some(key) => key.to_string(),
            _ => {
                match req.headers().get("X-Real-IP") {
                    Some(value) => value.to_str().unwrap().to_owned(),
                    None => format!("{:?}",req.peer_addr()),
                }
            },
             //req.headers().get("X-Real-IP").unwrap().to_str().unwrap().to_owned(),
        };
        Ok(key)
    }

    fn exceed_rate_limit_response(
        &self,
        negative:&governor::NotUntil<governor::clock::QuantaInstant>,
        mut response: actix_web::HttpResponseBuilder,
    ) -> actix_web::HttpResponse {
        let wait_time = negative
            .wait_time_from(DefaultClock::default().now())
            .as_secs();
        response
            .content_type(ContentType::json())
            .body(format!(
                r#"{{"code":-429,"message":"请求过快,请{wait_time}后重试"}}"#
        ))
    }
}

it told me that expected struct `governor::gcra::NotUntil`, found struct `NotUntil` | help: change the parameter type to match the trait: `&governor::gcra::NotUntil<governor::clock::quanta::QuantaInstant>

then I changed my code to this:

fn exceed_rate_limit_response(
        &self,
        negative:&governor::gcra::NotUntil<governor::clock::quanta::QuantaInstant>,
        mut response: actix_web::HttpResponseBuilder,
    ) -> actix_web::HttpResponse {
        let wait_time = negative
            .wait_time_from(DefaultClock::default().now())
            .as_secs();
        response
            .content_type(ContentType::json())
            .body(format!(
                r#"{{"code":-429,"message":"请求过快,请{wait_time}后重试"}}"#
        ))
    }

But still fails to compile

my dependencies:

curl = "0.4.44"
actix-web = "4.2.1"
actix-files = "0.6.2"
qstring = "0.7.2"
md5 = "0.7.0"
serde_json = "1.0.89"
serde_yaml = "0.9.14"
serde = {version ="1.0.147",features = ["derive"]}
deadpool = "0.9.5"
deadpool-redis = "0.11.0"
async-channel = "1.7.1"
futures = "0.3.25"
tokio = { version = "1.22.0", features = ["full"] }
pcre2 = "0.2.3"
ctrlc = "3.2.3"
rand = "0.8.5"
actix-governor = "0.4.0-beta.2"
governor = "0.5.1"
urlencoding = "2.1.2"
lazy_static = "1.4.0"
log = "0.4"
env_logger = "0.10.0"

IP Whitelist?

Would it be any way to allow a specific set of IPs to not have any throttling at all? Haven't found any way to do that yet. Thanks!

0.3.1 has a breaking change.

Hi there,

One of my CI jobs failed today with the following compilation error:

error[E0107]: this struct takes 2 generic arguments but 1 generic argument was supplied
   --> rest/src/main.rs:116:24
    |
116 | fn ratelimit_post() -> GovernorConfig<PeerIpKeyExtractor> {
    |                        ^^^^^^^^^^^^^^ ------------------ supplied 1 generic argument
    |                        |
    |                        expected 2 generic arguments
    |
note: struct defined here, with 2 generic parameters: `K`, `M`
   --> /root/.cargo/registry/src/index.crates.io-e139d0d48fed7772/actix-governor-0.3.1/src/lib.rs:388:12
    |
388 | pub struct GovernorConfig<K: KeyExtractor, M: RateLimitingMiddleware<QuantaInstant>> {
    |            ^^^^^^^^^^^^^^ -                -
help: add missing generic argument
    |
116 | fn ratelimit_post() -> GovernorConfig<PeerIpKeyExtractor, M> {
    |                                                         +++

This compiled fine on 0.3.0, but not on 0,3.1. Code written for 0.3.0 should be compatible with 0.3.1 according to semver (I think).

IMO 0.3.1 should be yanked and republished as 0.4.0.

Thanks!

TooManyRequests error

The TooManyRequests error is returned as a text, which is not good for middleware in the API. Is it better to add a feature called json_response or make it a method like with_headers?

Async extract for KeyExtractor

Hello! Would it be possible to support an async extract for the key extractor? I would like to access a tokio mutex when extracting the key. I cannot use the blocking lock as I am using an async runtime. Thanks!

Also, it would be great if the recent key whitelist additions could be pushed to crates.io :)

const_per_second missing

// Create governor config with recovery time and burst size.
fn create_config(
    s: u64,
    bs: u32,
) -> GovernorConfig<RateLimit, NoOpMiddleware<QuantaInstant>> {
    GovernorConfigBuilder::default()
        .key_extractor(RateLimit)
        .const_per_second(s)
        .burst_size(bs)
        .finish()
        .unwrap()
}
error[E0599]: no method named `const_per_second` found for struct `GovernorConfigBuilder<RateLimit, NoOpMiddleware<QuantaInstant>>` in the current scope
  --> src/lib.rs:50:10
   |
50 |         .const_per_second(s)
   |          ^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `per_second`

For more information about this error, try `rustc --explain E0599`.

Was it removed or am I missing something? It says it's still available in v0.4 in docs.
I am trying to rate limit register endpoint to 2 successful requests in 10 minutes.

fn users() -> actix_web::Scope {
    let mut sc = scope("/api/v1/users");
    let governor_conf = create_config(600, 2);

    sc = sc.service(
        resource("")
            .route(get().to(users::all))
            .route(post().to(users::register))
            .wrap(Governor::new(&governor_conf)),
    );
 ...
[dependencies]
actix-governor = "0.4.0-beta.3"

Allow to configure the rate limiter's key

Hi!

I see that the current rate limiter's key is the peer IP address:

let ip = if let Some(addr) = req.peer_addr() {
match self.limiter.check_key(&ip) {

I feel like this is sub-optimal for two reasons:

  1. If I deploy my Actix Web app behind a reverse proxy, rate-limiting will be applied as if all incoming requests are from the same IP address (which is in fact the reverse proxy address)
  2. It seems to be a good practice to set multiple rate-limiting configuration at the same time. For example, something like:
    • 1000 req/s for all requests
    • 100 req/s for requests coming from the same IP
    • 10 req/s for requests using the same API token

Concerning the first issue, Actix Web has a method to extract the real user's IP if it is set by the reverse proxy in the Forwarded and X-Forwarded-For headers, but, as stated in the documentation, it is not safe to use it if we do not check if the peer address is trusted → only a trusted reverse proxy must be allowed to set these headers or else any user can fake its IP.

Because this would add quite a lot complexity to this lib, and because of my second issue, I'm thinking maybe it would be interesting to:

  1. make your middleware generic over the type T: Eq of the rate limiter's key
  2. add a key field in GovernorConfig that can take a value of type Fn(&ServiceRequest) -> T so that the middleware can use it to extract any key from the request

Does this make sense? What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.