mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-02 09:11:22 +00:00
5c6258390c
* Adding a way to GetComments for a community given its name only. * Adding getcomments to api docs. * A first pass at locally working isomorphic integration. * Testing out cargo-husky. * Testing a fail hook. * Revert "Testing a fail hook." This reverts commit0941cf1736
. * Moving server to top level, now that UI is gone. * Running cargo fmt using old way. * Adding nginx, fixing up docker-compose files, fixing docs. * Trying to re-add API tests. * Fixing prod dockerfile. * Redoing nightly fmt * Trying to fix private message api test. * Adding CommunityJoin, PostJoin instead of joins from GetComments, etc. - Fixes #1122 * Fixing fmt. * Fixing up docs. * Removing translations. * Adding apps / clients to readme. * Fixing main image. * Using new lemmy-isomorphic-ui with better javascript disabled. * Try to fix image uploads in federation test * Revert "Try to fix image uploads in federation test" This reverts commita2ddf2a90b
. * Fix post url federation * Adding some more tests, some still broken. * Don't need gitattributes anymore. * Update local federation test setup * Fixing tests. * Fixing travis build. * Fixing travis build, again. * Changing lemmy-isomorphic-ui to lemmy-ui * Error in travis build again. Co-authored-by: Felix Ableitner <me@nutomic.com>
114 lines
2.8 KiB
Rust
114 lines
2.8 KiB
Rust
use lemmy_utils::{APIError, IPAddr, LemmyError};
|
|
use log::debug;
|
|
use std::{collections::HashMap, time::SystemTime};
|
|
use strum::IntoEnumIterator;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct RateLimitBucket {
|
|
last_checked: SystemTime,
|
|
allowance: f64,
|
|
}
|
|
|
|
#[derive(Eq, PartialEq, Hash, Debug, EnumIter, Copy, Clone, AsRefStr)]
|
|
pub enum RateLimitType {
|
|
Message,
|
|
Register,
|
|
Post,
|
|
Image,
|
|
}
|
|
|
|
/// Rate limiting based on rate type and IP addr
|
|
#[derive(Debug, Clone)]
|
|
pub struct RateLimiter {
|
|
pub buckets: HashMap<RateLimitType, HashMap<IPAddr, RateLimitBucket>>,
|
|
}
|
|
|
|
impl Default for RateLimiter {
|
|
fn default() -> Self {
|
|
Self {
|
|
buckets: HashMap::<RateLimitType, HashMap<IPAddr, RateLimitBucket>>::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RateLimiter {
|
|
fn insert_ip(&mut self, ip: &str) {
|
|
for rate_limit_type in RateLimitType::iter() {
|
|
if self.buckets.get(&rate_limit_type).is_none() {
|
|
self.buckets.insert(rate_limit_type, HashMap::new());
|
|
}
|
|
|
|
if let Some(bucket) = self.buckets.get_mut(&rate_limit_type) {
|
|
if bucket.get(ip).is_none() {
|
|
bucket.insert(
|
|
ip.to_string(),
|
|
RateLimitBucket {
|
|
last_checked: SystemTime::now(),
|
|
allowance: -2f64,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::float_cmp)]
|
|
pub(super) fn check_rate_limit_full(
|
|
&mut self,
|
|
type_: RateLimitType,
|
|
ip: &str,
|
|
rate: i32,
|
|
per: i32,
|
|
check_only: bool,
|
|
) -> Result<(), LemmyError> {
|
|
self.insert_ip(ip);
|
|
if let Some(bucket) = self.buckets.get_mut(&type_) {
|
|
if let Some(rate_limit) = bucket.get_mut(ip) {
|
|
let current = SystemTime::now();
|
|
let time_passed = current.duration_since(rate_limit.last_checked)?.as_secs() as f64;
|
|
|
|
// The initial value
|
|
if rate_limit.allowance == -2f64 {
|
|
rate_limit.allowance = rate as f64;
|
|
};
|
|
|
|
rate_limit.last_checked = current;
|
|
rate_limit.allowance += time_passed * (rate as f64 / per as f64);
|
|
if !check_only && rate_limit.allowance > rate as f64 {
|
|
rate_limit.allowance = rate as f64;
|
|
}
|
|
|
|
if rate_limit.allowance < 1.0 {
|
|
debug!(
|
|
"Rate limited type: {}, IP: {}, time_passed: {}, allowance: {}",
|
|
type_.as_ref(),
|
|
ip,
|
|
time_passed,
|
|
rate_limit.allowance
|
|
);
|
|
Err(
|
|
APIError {
|
|
message: format!(
|
|
"Too many requests. type: {}, IP: {}, {} per {} seconds",
|
|
type_.as_ref(),
|
|
ip,
|
|
rate,
|
|
per
|
|
),
|
|
}
|
|
.into(),
|
|
)
|
|
} else {
|
|
if !check_only {
|
|
rate_limit.allowance -= 1.0;
|
|
}
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|