-use crate::{settings::structs::RateLimitConfig, utils::get_ip, IpAddr};
+use crate::{utils::get_ip, IpAddr};
use actix_web::{
dev::{Service, ServiceRequest, ServiceResponse, Transform},
HttpResponse,
};
use futures::future::{ok, Ready};
use rate_limiter::{RateLimitType, RateLimiter};
+use serde::{Deserialize, Serialize};
use std::{
future::Future,
pin::Pin,
rc::Rc,
- sync::Arc,
+ sync::{Arc, Mutex},
task::{Context, Poll},
};
-use tokio::sync::Mutex;
+use typed_builder::TypedBuilder;
pub mod rate_limiter;
+#[derive(Debug, Deserialize, Serialize, Clone, TypedBuilder)]
+pub struct RateLimitConfig {
+ #[builder(default = 180)]
+ /// Maximum number of messages created in interval
+ pub message: i32,
+ #[builder(default = 60)]
+ /// Interval length for message limit, in seconds
+ pub message_per_second: i32,
+ #[builder(default = 6)]
+ /// Maximum number of posts created in interval
+ pub post: i32,
+ #[builder(default = 300)]
+ /// Interval length for post limit, in seconds
+ pub post_per_second: i32,
+ #[builder(default = 3)]
+ /// Maximum number of registrations in interval
+ pub register: i32,
+ #[builder(default = 3600)]
+ /// Interval length for registration limit, in seconds
+ pub register_per_second: i32,
+ #[builder(default = 6)]
+ /// Maximum number of image uploads in interval
+ pub image: i32,
+ #[builder(default = 3600)]
+ /// Interval length for image uploads, in seconds
+ pub image_per_second: i32,
+ #[builder(default = 6)]
+ /// Maximum number of comments created in interval
+ pub comment: i32,
+ #[builder(default = 600)]
+ /// Interval length for comment limit, in seconds
+ pub comment_per_second: i32,
+ #[builder(default = 60)]
+ /// Maximum number of searches created in interval
+ pub search: i32,
+ #[builder(default = 600)]
+ /// Interval length for search limit, in seconds
+ pub search_per_second: i32,
+}
+
#[derive(Debug, Clone)]
pub struct RateLimit {
// it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
self.kind(RateLimitType::Comment)
}
+ pub fn search(&self) -> RateLimited {
+ self.kind(RateLimitType::Search)
+ }
+
fn kind(&self, type_: RateLimitType) -> RateLimited {
RateLimited {
rate_limiter: self.rate_limiter.clone(),
impl RateLimited {
/// Returns true if the request passed the rate limit, false if it failed and should be rejected.
- pub async fn check(self, ip_addr: IpAddr) -> bool {
+ pub fn check(self, ip_addr: IpAddr) -> bool {
// Does not need to be blocking because the RwLock in settings never held across await points,
// and the operation here locks only long enough to clone
let rate_limit = self.rate_limit_config;
- let mut limiter = self.rate_limiter.lock().await;
-
let (kind, interval) = match self.type_ {
RateLimitType::Message => (rate_limit.message, rate_limit.message_per_second),
RateLimitType::Post => (rate_limit.post, rate_limit.post_per_second),
RateLimitType::Register => (rate_limit.register, rate_limit.register_per_second),
RateLimitType::Image => (rate_limit.image, rate_limit.image_per_second),
RateLimitType::Comment => (rate_limit.comment, rate_limit.comment_per_second),
+ RateLimitType::Search => (rate_limit.search, rate_limit.search_per_second),
};
+ let mut limiter = self.rate_limiter.lock().expect("mutex poison error");
+
limiter.check_rate_limit_full(self.type_, &ip_addr, kind, interval)
}
}
let service = self.service.clone();
Box::pin(async move {
- if rate_limited.check(ip_addr).await {
+ if rate_limited.check(ip_addr) {
service.call(req).await
} else {
let (http_req, _) = req.into_parts();