You've already forked AstralRinth
forked from didirus/AstralRinth
Shulkers of fixes (#327)
* Shulkers of fixes * Fix validation message * Update deps * Bump docker image version
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
//! Errors that can occur during middleware processing stage
|
||||
use crate::models::error::ApiError;
|
||||
use actix_web::ResponseError;
|
||||
use log::*;
|
||||
use thiserror::Error;
|
||||
@@ -11,14 +12,14 @@ use thiserror::Error;
|
||||
pub enum ARError {
|
||||
/// Read/Write error on store
|
||||
#[error("read/write operatiion failed: {0}")]
|
||||
ReadWriteError(String),
|
||||
ReadWrite(String),
|
||||
|
||||
/// Identifier error
|
||||
#[error("client identification failed")]
|
||||
IdentificationError,
|
||||
Identification,
|
||||
/// Limited Error
|
||||
#[error("You are being ratelimited. Please wait {reset} seconds. {remaining}/{max_requests} remaining.")]
|
||||
LimitedError {
|
||||
Limited {
|
||||
max_requests: usize,
|
||||
remaining: usize,
|
||||
reset: u64,
|
||||
@@ -28,7 +29,7 @@ pub enum ARError {
|
||||
impl ResponseError for ARError {
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
match self {
|
||||
Self::LimitedError {
|
||||
Self::Limited {
|
||||
max_requests,
|
||||
remaining,
|
||||
reset,
|
||||
@@ -44,10 +45,17 @@ impl ResponseError for ARError {
|
||||
));
|
||||
response
|
||||
.insert_header(("x-ratelimit-reset", reset.to_string()));
|
||||
response.body(self.to_string())
|
||||
response.json(ApiError {
|
||||
error: "ratelimit_error",
|
||||
description: &self.to_string(),
|
||||
})
|
||||
}
|
||||
_ => actix_web::HttpResponse::build(self.status_code())
|
||||
.body(self.to_string()),
|
||||
_ => actix_web::HttpResponse::build(self.status_code()).json(
|
||||
ApiError {
|
||||
error: "ratelimit_error",
|
||||
description: &self.to_string(),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +107,11 @@ impl Handler<ActorMessage> for MemoryStoreActor {
|
||||
let new_val = val_mut.0;
|
||||
ActorResponse::Update(Box::pin(future::ready(Ok(new_val))))
|
||||
}
|
||||
None => {
|
||||
return ActorResponse::Update(Box::pin(future::ready(Err(
|
||||
ARError::ReadWriteError(
|
||||
"memory store: read failed!".to_string(),
|
||||
),
|
||||
))))
|
||||
}
|
||||
None => ActorResponse::Update(Box::pin(future::ready(Err(
|
||||
ARError::ReadWrite(
|
||||
"memory store: read failed!".to_string(),
|
||||
),
|
||||
)))),
|
||||
},
|
||||
ActorMessage::Get(key) => {
|
||||
if self.inner.contains_key(&key) {
|
||||
@@ -121,7 +119,7 @@ impl Handler<ActorMessage> for MemoryStoreActor {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return ActorResponse::Get(Box::pin(future::ready(
|
||||
Err(ARError::ReadWriteError(
|
||||
Err(ARError::ReadWrite(
|
||||
"memory store: read failed!".to_string(),
|
||||
)),
|
||||
)))
|
||||
@@ -138,7 +136,7 @@ impl Handler<ActorMessage> for MemoryStoreActor {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
return ActorResponse::Expire(Box::pin(future::ready(
|
||||
Err(ARError::ReadWriteError(
|
||||
Err(ARError::ReadWrite(
|
||||
"memory store: read failed!".to_string(),
|
||||
)),
|
||||
)))
|
||||
@@ -156,7 +154,7 @@ impl Handler<ActorMessage> for MemoryStoreActor {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return ActorResponse::Remove(Box::pin(future::ready(
|
||||
Err(ARError::ReadWriteError(
|
||||
Err(ARError::ReadWrite(
|
||||
"memory store: remove failed!".to_string(),
|
||||
)),
|
||||
)))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//! RateLimiter middleware for actix application
|
||||
use crate::ratelimit::errors::ARError;
|
||||
use crate::ratelimit::{ActorMessage, ActorResponse};
|
||||
use actix::dev::*;
|
||||
@@ -19,28 +18,9 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// Type that implements the ratelimit middleware.
|
||||
///
|
||||
/// This accepts _interval_ which specifies the
|
||||
/// window size, _max_requests_ which specifies the maximum number of requests in that window, and
|
||||
/// _store_ which is essentially a data store used to store client access information. Entry is removed from
|
||||
/// the store after _interval_.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// use actix_ratelimit::{MemoryStore, MemoryStoreActor};
|
||||
/// use actix_ratelimit::RateLimiter;
|
||||
///
|
||||
/// #[actix_rt::main]
|
||||
/// async fn main() {
|
||||
/// let store = MemoryStore::new();
|
||||
/// let ratelimiter = RateLimiter::new(
|
||||
/// MemoryStoreActor::from(store.clone()).start())
|
||||
/// .with_interval(Duration::from_secs(60))
|
||||
/// .with_max_requests(100);
|
||||
/// }
|
||||
/// ```
|
||||
type RateLimiterIdentifier =
|
||||
Rc<Box<dyn Fn(&ServiceRequest) -> Result<String, ARError> + 'static>>;
|
||||
|
||||
pub struct RateLimiter<T>
|
||||
where
|
||||
T: Handler<ActorMessage> + Send + Sync + 'static,
|
||||
@@ -49,7 +29,7 @@ where
|
||||
interval: Duration,
|
||||
max_requests: usize,
|
||||
store: Addr<T>,
|
||||
identifier: Rc<Box<dyn Fn(&ServiceRequest) -> Result<String, ARError>>>,
|
||||
identifier: RateLimiterIdentifier,
|
||||
ignore_ips: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -62,9 +42,8 @@ where
|
||||
pub fn new(store: Addr<T>) -> Self {
|
||||
let identifier = |req: &ServiceRequest| {
|
||||
let connection_info = req.connection_info();
|
||||
let ip = connection_info
|
||||
.peer_addr()
|
||||
.ok_or(ARError::IdentificationError)?;
|
||||
let ip =
|
||||
connection_info.peer_addr().ok_or(ARError::Identification)?;
|
||||
Ok(String::from(ip))
|
||||
};
|
||||
RateLimiter {
|
||||
@@ -144,8 +123,7 @@ where
|
||||
// Exists here for the sole purpose of knowing the max_requests and interval from RateLimiter
|
||||
max_requests: usize,
|
||||
interval: u64,
|
||||
identifier:
|
||||
Rc<Box<dyn Fn(&ServiceRequest) -> Result<String, ARError> + 'static>>,
|
||||
identifier: RateLimiterIdentifier,
|
||||
ignore_ips: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -187,7 +165,7 @@ where
|
||||
let remaining: ActorResponse = store
|
||||
.send(ActorMessage::Get(String::from(&identifier)))
|
||||
.await
|
||||
.map_err(|_| ARError::IdentificationError)?;
|
||||
.map_err(|_| ARError::Identification)?;
|
||||
match remaining {
|
||||
ActorResponse::Get(opt) => {
|
||||
let opt = opt.await?;
|
||||
@@ -199,7 +177,7 @@ where
|
||||
)))
|
||||
.await
|
||||
.map_err(|_| {
|
||||
ARError::ReadWriteError(
|
||||
ARError::ReadWrite(
|
||||
"Setting timeout".to_string(),
|
||||
)
|
||||
})?;
|
||||
@@ -209,7 +187,7 @@ where
|
||||
};
|
||||
if c == 0 {
|
||||
info!("Limit exceeded for client: {}", &identifier);
|
||||
Err(ARError::LimitedError {
|
||||
Err(ARError::Limited {
|
||||
max_requests,
|
||||
remaining: c,
|
||||
reset: reset.as_secs(),
|
||||
@@ -224,7 +202,7 @@ where
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
ARError::ReadWriteError(
|
||||
ARError::ReadWrite(
|
||||
"Decrementing ratelimit".to_string(),
|
||||
)
|
||||
})?;
|
||||
@@ -270,7 +248,7 @@ where
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
ARError::ReadWriteError(
|
||||
ARError::ReadWrite(
|
||||
"Creating store entry".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Reference in New Issue
Block a user