mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-25 05:50:19 +00:00
* Live reload rate limit settings (fixes #2508) * fix tests
This commit is contained in:
parent
b16df59373
commit
24756af84b
15 changed files with 132 additions and 76 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2255,6 +2255,7 @@ dependencies = [
|
||||||
"reqwest-retry",
|
"reqwest-retry",
|
||||||
"reqwest-tracing",
|
"reqwest-tracing",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
|
@ -2295,6 +2296,7 @@ dependencies = [
|
||||||
"smart-default",
|
"smart-default",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"typed-builder",
|
"typed-builder",
|
||||||
|
|
|
@ -73,3 +73,4 @@ console-subscriber = { version = "0.1.8", optional = true }
|
||||||
opentelemetry = { version = "0.17.0", features = ["rt-tokio"], optional = true }
|
opentelemetry = { version = "0.17.0", features = ["rt-tokio"], optional = true }
|
||||||
opentelemetry-otlp = { version = "0.10.0", optional = true }
|
opentelemetry-otlp = { version = "0.10.0", optional = true }
|
||||||
tracing-opentelemetry = { version = "0.17.2", optional = true }
|
tracing-opentelemetry = { version = "0.17.2", optional = true }
|
||||||
|
tokio = "1.21.2"
|
||||||
|
|
|
@ -2,8 +2,4 @@
|
||||||
# https://join-lemmy.org/docs/en/administration/configuration.html
|
# https://join-lemmy.org/docs/en/administration/configuration.html
|
||||||
{
|
{
|
||||||
hostname: lemmy-alpha
|
hostname: lemmy-alpha
|
||||||
database: {
|
|
||||||
# Username to connect to postgres
|
|
||||||
user: "&££%^!£*!:::!"£:!:"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use lemmy_api_common::{
|
||||||
utils::{
|
utils::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
local_site_rate_limit_to_rate_limit_config,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
site_description_length_check,
|
site_description_length_check,
|
||||||
},
|
},
|
||||||
|
@ -139,6 +140,13 @@ impl PerformCrud for CreateSite {
|
||||||
|
|
||||||
let site_view = SiteView::read_local(context.pool()).await?;
|
let site_view = SiteView::read_local(context.pool()).await?;
|
||||||
|
|
||||||
|
let rate_limit_config =
|
||||||
|
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
||||||
|
context
|
||||||
|
.settings_updated_channel()
|
||||||
|
.send(rate_limit_config)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(SiteResponse { site_view })
|
Ok(SiteResponse { site_view })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use lemmy_api_common::{
|
||||||
utils::{
|
utils::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
local_site_rate_limit_to_rate_limit_config,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
site_description_length_check,
|
site_description_length_check,
|
||||||
},
|
},
|
||||||
|
@ -176,6 +177,13 @@ impl PerformCrud for EditSite {
|
||||||
|
|
||||||
let site_view = SiteView::read_local(context.pool()).await?;
|
let site_view = SiteView::read_local(context.pool()).await?;
|
||||||
|
|
||||||
|
let rate_limit_config =
|
||||||
|
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
||||||
|
context
|
||||||
|
.settings_updated_channel()
|
||||||
|
.send(rate_limit_config)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let res = SiteResponse { site_view };
|
let res = SiteResponse { site_view };
|
||||||
|
|
||||||
context.chat_server().do_send(SendAllMessage {
|
context.chat_server().do_send(SendAllMessage {
|
||||||
|
|
|
@ -60,13 +60,12 @@ pub(crate) mod tests {
|
||||||
use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
|
use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::LemmyError,
|
error::LemmyError,
|
||||||
rate_limit::{rate_limiter::RateLimiter, RateLimit, RateLimitConfig},
|
rate_limit::{RateLimitCell, RateLimitConfig},
|
||||||
settings::SETTINGS,
|
settings::SETTINGS,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
||||||
use reqwest::{Client, Request, Response};
|
use reqwest::{Client, Request, Response};
|
||||||
use reqwest_middleware::{ClientBuilder, Middleware, Next};
|
use reqwest_middleware::{ClientBuilder, Middleware, Next};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use task_local_extensions::Extensions;
|
use task_local_extensions::Extensions;
|
||||||
|
|
||||||
struct BlockedMiddleware;
|
struct BlockedMiddleware;
|
||||||
|
@ -105,22 +104,25 @@ pub(crate) mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let rate_limit_config = RateLimitConfig::builder().build();
|
let rate_limit_config = RateLimitConfig::builder().build();
|
||||||
|
let rate_limit_cell = RateLimitCell::new(rate_limit_config).await;
|
||||||
let rate_limiter = RateLimit {
|
|
||||||
rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
|
|
||||||
rate_limit_config,
|
|
||||||
};
|
|
||||||
|
|
||||||
let chat_server = ChatServer::startup(
|
let chat_server = ChatServer::startup(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
rate_limiter,
|
|
||||||
|_, _, _, _| Box::pin(x()),
|
|_, _, _, _| Box::pin(x()),
|
||||||
|_, _, _, _| Box::pin(x()),
|
|_, _, _, _| Box::pin(x()),
|
||||||
client.clone(),
|
client.clone(),
|
||||||
settings.clone(),
|
settings.clone(),
|
||||||
secret.clone(),
|
secret.clone(),
|
||||||
|
rate_limit_cell.clone(),
|
||||||
)
|
)
|
||||||
.start();
|
.start();
|
||||||
LemmyContext::create(pool, chat_server, client, settings, secret)
|
LemmyContext::create(
|
||||||
|
pool,
|
||||||
|
chat_server,
|
||||||
|
client,
|
||||||
|
settings,
|
||||||
|
secret,
|
||||||
|
rate_limit_cell.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,17 @@ use actix_web::{
|
||||||
use futures::stream::{Stream, StreamExt};
|
use futures::stream::{Stream, StreamExt};
|
||||||
use lemmy_api_common::utils::get_local_user_view_from_jwt;
|
use lemmy_api_common::utils::get_local_user_view_from_jwt;
|
||||||
use lemmy_db_schema::source::local_site::LocalSite;
|
use lemmy_db_schema::source::local_site::LocalSite;
|
||||||
use lemmy_utils::{claims::Claims, rate_limit::RateLimit, REQWEST_TIMEOUT};
|
use lemmy_utils::{claims::Claims, rate_limit::RateLimitCell, REQWEST_TIMEOUT};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use reqwest::Body;
|
use reqwest::Body;
|
||||||
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
|
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig, client: ClientWithMiddleware, rate_limit: &RateLimit) {
|
pub fn config(
|
||||||
|
cfg: &mut web::ServiceConfig,
|
||||||
|
client: ClientWithMiddleware,
|
||||||
|
rate_limit: &RateLimitCell,
|
||||||
|
) {
|
||||||
cfg
|
cfg
|
||||||
.app_data(web::Data::new(client))
|
.app_data(web::Data::new(client))
|
||||||
.service(
|
.service(
|
||||||
|
|
|
@ -45,6 +45,7 @@ rosetta-i18n = "0.1.2"
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
typed-builder = "0.10.0"
|
typed-builder = "0.10.0"
|
||||||
percent-encoding = "2.2.0"
|
percent-encoding = "2.2.0"
|
||||||
|
tokio = "1.21.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rosetta-build = "0.1.2"
|
rosetta-build = "0.1.2"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{error::LemmyError, utils::get_ip, IpAddr};
|
use crate::{error::LemmyError, utils::get_ip, IpAddr};
|
||||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||||
use futures::future::{ok, Ready};
|
use futures::future::{ok, Ready};
|
||||||
use rate_limiter::{RateLimitType, RateLimiter};
|
use rate_limiter::{RateLimitStorage, RateLimitType};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
|
@ -10,6 +10,7 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
use tokio::sync::{mpsc, mpsc::Sender, OnceCell};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
pub mod rate_limiter;
|
pub mod rate_limiter;
|
||||||
|
@ -55,65 +56,102 @@ pub struct RateLimitConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RateLimit {
|
struct RateLimit {
|
||||||
// it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
|
pub rate_limiter: RateLimitStorage,
|
||||||
// across await points
|
|
||||||
pub rate_limiter: Arc<Mutex<RateLimiter>>,
|
|
||||||
pub rate_limit_config: RateLimitConfig,
|
pub rate_limit_config: RateLimitConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RateLimited {
|
pub struct RateLimitedGuard {
|
||||||
rate_limiter: Arc<Mutex<RateLimiter>>,
|
rate_limit: Arc<Mutex<RateLimit>>,
|
||||||
rate_limit_config: RateLimitConfig,
|
|
||||||
type_: RateLimitType,
|
type_: RateLimitType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RateLimitedMiddleware<S> {
|
/// Single instance of rate limit config and buckets, which is shared across all threads.
|
||||||
rate_limited: RateLimited,
|
#[derive(Clone)]
|
||||||
service: Rc<S>,
|
pub struct RateLimitCell {
|
||||||
|
tx: Sender<RateLimitConfig>,
|
||||||
|
rate_limit: Arc<Mutex<RateLimit>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RateLimit {
|
impl RateLimitCell {
|
||||||
pub fn message(&self) -> RateLimited {
|
/// Initialize cell if it wasnt initialized yet. Otherwise returns the existing cell.
|
||||||
|
pub async fn new(rate_limit_config: RateLimitConfig) -> &'static Self {
|
||||||
|
static LOCAL_INSTANCE: OnceCell<RateLimitCell> = OnceCell::const_new();
|
||||||
|
LOCAL_INSTANCE
|
||||||
|
.get_or_init(|| async {
|
||||||
|
let (tx, mut rx) = mpsc::channel::<RateLimitConfig>(4);
|
||||||
|
let rate_limit = Arc::new(Mutex::new(RateLimit {
|
||||||
|
rate_limiter: Default::default(),
|
||||||
|
rate_limit_config,
|
||||||
|
}));
|
||||||
|
let rate_limit2 = rate_limit.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(r) = rx.recv().await {
|
||||||
|
rate_limit2
|
||||||
|
.lock()
|
||||||
|
.expect("Failed to lock rate limit mutex for updating")
|
||||||
|
.rate_limit_config = r;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
RateLimitCell { tx, rate_limit }
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call this when the config was updated, to update all in-memory cells.
|
||||||
|
pub async fn send(&self, config: RateLimitConfig) -> Result<(), LemmyError> {
|
||||||
|
self.tx.send(config).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Message)
|
self.kind(RateLimitType::Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post(&self) -> RateLimited {
|
pub fn post(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Post)
|
self.kind(RateLimitType::Post)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(&self) -> RateLimited {
|
pub fn register(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Register)
|
self.kind(RateLimitType::Register)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image(&self) -> RateLimited {
|
pub fn image(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Image)
|
self.kind(RateLimitType::Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn comment(&self) -> RateLimited {
|
pub fn comment(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Comment)
|
self.kind(RateLimitType::Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(&self) -> RateLimited {
|
pub fn search(&self) -> RateLimitedGuard {
|
||||||
self.kind(RateLimitType::Search)
|
self.kind(RateLimitType::Search)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind(&self, type_: RateLimitType) -> RateLimited {
|
fn kind(&self, type_: RateLimitType) -> RateLimitedGuard {
|
||||||
RateLimited {
|
RateLimitedGuard {
|
||||||
rate_limiter: self.rate_limiter.clone(),
|
rate_limit: self.rate_limit.clone(),
|
||||||
rate_limit_config: self.rate_limit_config.clone(),
|
|
||||||
type_,
|
type_,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RateLimited {
|
pub struct RateLimitedMiddleware<S> {
|
||||||
|
rate_limited: RateLimitedGuard,
|
||||||
|
service: Rc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RateLimitedGuard {
|
||||||
/// Returns true if the request passed the rate limit, false if it failed and should be rejected.
|
/// Returns true if the request passed the rate limit, false if it failed and should be rejected.
|
||||||
pub 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,
|
// 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
|
// and the operation here locks only long enough to clone
|
||||||
let rate_limit = self.rate_limit_config;
|
let mut guard = self
|
||||||
|
.rate_limit
|
||||||
|
.lock()
|
||||||
|
.expect("Failed to lock rate limit mutex for reading");
|
||||||
|
let rate_limit = &guard.rate_limit_config;
|
||||||
|
|
||||||
let (kind, interval) = match self.type_ {
|
let (kind, interval) = match self.type_ {
|
||||||
RateLimitType::Message => (rate_limit.message, rate_limit.message_per_second),
|
RateLimitType::Message => (rate_limit.message, rate_limit.message_per_second),
|
||||||
|
@ -123,13 +161,13 @@ impl RateLimited {
|
||||||
RateLimitType::Comment => (rate_limit.comment, rate_limit.comment_per_second),
|
RateLimitType::Comment => (rate_limit.comment, rate_limit.comment_per_second),
|
||||||
RateLimitType::Search => (rate_limit.search, rate_limit.search_per_second),
|
RateLimitType::Search => (rate_limit.search, rate_limit.search_per_second),
|
||||||
};
|
};
|
||||||
let mut limiter = self.rate_limiter.lock().expect("mutex poison error");
|
let limiter = &mut guard.rate_limiter;
|
||||||
|
|
||||||
limiter.check_rate_limit_full(self.type_, &ip_addr, kind, interval)
|
limiter.check_rate_limit_full(self.type_, &ip_addr, kind, interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Transform<S, ServiceRequest> for RateLimited
|
impl<S> Transform<S, ServiceRequest> for RateLimitedGuard
|
||||||
where
|
where
|
||||||
S: Service<ServiceRequest, Response = ServiceResponse, Error = actix_web::Error> + 'static,
|
S: Service<ServiceRequest, Response = ServiceResponse, Error = actix_web::Error> + 'static,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
|
|
|
@ -21,11 +21,11 @@ pub(crate) enum RateLimitType {
|
||||||
|
|
||||||
/// Rate limiting based on rate type and IP addr
|
/// Rate limiting based on rate type and IP addr
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct RateLimiter {
|
pub struct RateLimitStorage {
|
||||||
buckets: HashMap<RateLimitType, HashMap<IpAddr, RateLimitBucket>>,
|
buckets: HashMap<RateLimitType, HashMap<IpAddr, RateLimitBucket>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RateLimiter {
|
impl RateLimitStorage {
|
||||||
fn insert_ip(&mut self, ip: &IpAddr) {
|
fn insert_ip(&mut self, ip: &IpAddr) {
|
||||||
for rate_limit_type in RateLimitType::iter() {
|
for rate_limit_type in RateLimitType::iter() {
|
||||||
if self.buckets.get(&rate_limit_type).is_none() {
|
if self.buckets.get(&rate_limit_type).is_none() {
|
||||||
|
|
|
@ -17,7 +17,7 @@ use lemmy_db_schema::{
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::LemmyError,
|
error::LemmyError,
|
||||||
location_info,
|
location_info,
|
||||||
rate_limit::RateLimit,
|
rate_limit::RateLimitCell,
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
IpAddr,
|
IpAddr,
|
||||||
|
@ -76,9 +76,6 @@ pub struct ChatServer {
|
||||||
/// The Secrets
|
/// The Secrets
|
||||||
pub(super) secret: Secret,
|
pub(super) secret: Secret,
|
||||||
|
|
||||||
/// Rate limiting based on rate type and IP addr
|
|
||||||
pub(super) rate_limiter: RateLimit,
|
|
||||||
|
|
||||||
/// A list of the current captchas
|
/// A list of the current captchas
|
||||||
pub(super) captchas: Vec<CaptchaItem>,
|
pub(super) captchas: Vec<CaptchaItem>,
|
||||||
|
|
||||||
|
@ -87,6 +84,8 @@ pub struct ChatServer {
|
||||||
|
|
||||||
/// An HTTP Client
|
/// An HTTP Client
|
||||||
client: ClientWithMiddleware,
|
client: ClientWithMiddleware,
|
||||||
|
|
||||||
|
rate_limit_cell: RateLimitCell,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SessionInfo {
|
pub struct SessionInfo {
|
||||||
|
@ -101,12 +100,12 @@ impl ChatServer {
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
pub fn startup(
|
pub fn startup(
|
||||||
pool: DbPool,
|
pool: DbPool,
|
||||||
rate_limiter: RateLimit,
|
|
||||||
message_handler: MessageHandlerType,
|
message_handler: MessageHandlerType,
|
||||||
message_handler_crud: MessageHandlerCrudType,
|
message_handler_crud: MessageHandlerCrudType,
|
||||||
client: ClientWithMiddleware,
|
client: ClientWithMiddleware,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
secret: Secret,
|
secret: Secret,
|
||||||
|
rate_limit_cell: RateLimitCell,
|
||||||
) -> ChatServer {
|
) -> ChatServer {
|
||||||
ChatServer {
|
ChatServer {
|
||||||
sessions: HashMap::new(),
|
sessions: HashMap::new(),
|
||||||
|
@ -116,13 +115,13 @@ impl ChatServer {
|
||||||
user_rooms: HashMap::new(),
|
user_rooms: HashMap::new(),
|
||||||
rng: rand::thread_rng(),
|
rng: rand::thread_rng(),
|
||||||
pool,
|
pool,
|
||||||
rate_limiter,
|
|
||||||
captchas: Vec::new(),
|
captchas: Vec::new(),
|
||||||
message_handler,
|
message_handler,
|
||||||
message_handler_crud,
|
message_handler_crud,
|
||||||
client,
|
client,
|
||||||
settings,
|
settings,
|
||||||
secret,
|
secret,
|
||||||
|
rate_limit_cell,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +445,6 @@ impl ChatServer {
|
||||||
msg: StandardMessage,
|
msg: StandardMessage,
|
||||||
ctx: &mut Context<Self>,
|
ctx: &mut Context<Self>,
|
||||||
) -> impl Future<Output = Result<String, LemmyError>> {
|
) -> impl Future<Output = Result<String, LemmyError>> {
|
||||||
let rate_limiter = self.rate_limiter.clone();
|
|
||||||
|
|
||||||
let ip: IpAddr = match self.sessions.get(&msg.id) {
|
let ip: IpAddr = match self.sessions.get(&msg.id) {
|
||||||
Some(info) => info.ip.to_owned(),
|
Some(info) => info.ip.to_owned(),
|
||||||
None => IpAddr("blank_ip".to_string()),
|
None => IpAddr("blank_ip".to_string()),
|
||||||
|
@ -459,9 +456,11 @@ impl ChatServer {
|
||||||
client: self.client.to_owned(),
|
client: self.client.to_owned(),
|
||||||
settings: self.settings.to_owned(),
|
settings: self.settings.to_owned(),
|
||||||
secret: self.secret.to_owned(),
|
secret: self.secret.to_owned(),
|
||||||
|
rate_limit_cell: self.rate_limit_cell.to_owned(),
|
||||||
};
|
};
|
||||||
let message_handler_crud = self.message_handler_crud;
|
let message_handler_crud = self.message_handler_crud;
|
||||||
let message_handler = self.message_handler;
|
let message_handler = self.message_handler;
|
||||||
|
let rate_limiter = self.rate_limit_cell.clone();
|
||||||
async move {
|
async move {
|
||||||
let json: Value = serde_json::from_str(&msg.msg)?;
|
let json: Value = serde_json::from_str(&msg.msg)?;
|
||||||
let data = &json["data"].to_string();
|
let data = &json["data"].to_string();
|
||||||
|
|
|
@ -6,6 +6,7 @@ use actix::Addr;
|
||||||
use lemmy_db_schema::{source::secret::Secret, utils::DbPool};
|
use lemmy_db_schema::{source::secret::Secret, utils::DbPool};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::LemmyError,
|
error::LemmyError,
|
||||||
|
rate_limit::RateLimitCell,
|
||||||
settings::{structs::Settings, SETTINGS},
|
settings::{structs::Settings, SETTINGS},
|
||||||
};
|
};
|
||||||
use reqwest_middleware::ClientWithMiddleware;
|
use reqwest_middleware::ClientWithMiddleware;
|
||||||
|
@ -23,6 +24,7 @@ pub struct LemmyContext {
|
||||||
client: ClientWithMiddleware,
|
client: ClientWithMiddleware,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
secret: Secret,
|
secret: Secret,
|
||||||
|
rate_limit_cell: RateLimitCell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LemmyContext {
|
impl LemmyContext {
|
||||||
|
@ -32,6 +34,7 @@ impl LemmyContext {
|
||||||
client: ClientWithMiddleware,
|
client: ClientWithMiddleware,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
secret: Secret,
|
secret: Secret,
|
||||||
|
settings_updated_channel: RateLimitCell,
|
||||||
) -> LemmyContext {
|
) -> LemmyContext {
|
||||||
LemmyContext {
|
LemmyContext {
|
||||||
pool,
|
pool,
|
||||||
|
@ -39,6 +42,7 @@ impl LemmyContext {
|
||||||
client,
|
client,
|
||||||
settings,
|
settings,
|
||||||
secret,
|
secret,
|
||||||
|
rate_limit_cell: settings_updated_channel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn pool(&self) -> &DbPool {
|
pub fn pool(&self) -> &DbPool {
|
||||||
|
@ -56,6 +60,9 @@ impl LemmyContext {
|
||||||
pub fn secret(&self) -> &Secret {
|
pub fn secret(&self) -> &Secret {
|
||||||
&self.secret
|
&self.secret
|
||||||
}
|
}
|
||||||
|
pub fn settings_updated_channel(&self) -> &RateLimitCell {
|
||||||
|
&self.rate_limit_cell
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for LemmyContext {
|
impl Clone for LemmyContext {
|
||||||
|
@ -66,6 +73,7 @@ impl Clone for LemmyContext {
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
settings: self.settings.clone(),
|
settings: self.settings.clone(),
|
||||||
secret: self.secret.clone(),
|
secret: self.secret.clone(),
|
||||||
|
rate_limit_cell: self.rate_limit_cell.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use lemmy_utils::{rate_limit::RateLimit, utils::get_ip, ConnectionId, IpAddr};
|
use lemmy_utils::{rate_limit::RateLimitCell, utils::get_ip, ConnectionId, IpAddr};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ pub async fn chat_route(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
stream: web::Payload,
|
stream: web::Payload,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
rate_limiter: web::Data<RateLimit>,
|
rate_limiter: web::Data<RateLimitCell>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
ws::start(
|
ws::start(
|
||||||
WsSession {
|
WsSession {
|
||||||
|
@ -44,7 +44,7 @@ struct WsSession {
|
||||||
/// otherwise we drop connection.
|
/// otherwise we drop connection.
|
||||||
hb: Instant,
|
hb: Instant,
|
||||||
/// A rate limiter for websocket joins
|
/// A rate limiter for websocket joins
|
||||||
rate_limiter: RateLimit,
|
rate_limiter: RateLimitCell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WsSession {
|
impl Actor for WsSession {
|
||||||
|
|
|
@ -10,11 +10,11 @@ use lemmy_api_common::{
|
||||||
websocket::*,
|
websocket::*,
|
||||||
};
|
};
|
||||||
use lemmy_api_crud::PerformCrud;
|
use lemmy_api_crud::PerformCrud;
|
||||||
use lemmy_utils::rate_limit::RateLimit;
|
use lemmy_utils::rate_limit::RateLimitCell;
|
||||||
use lemmy_websocket::{routes::chat_route, LemmyContext};
|
use lemmy_websocket::{routes::chat_route, LemmyContext};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
cfg.service(
|
cfg.service(
|
||||||
web::scope("/api/v3")
|
web::scope("/api/v3")
|
||||||
// Websocket
|
// Websocket
|
||||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -29,7 +29,7 @@ use lemmy_server::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::LemmyError,
|
error::LemmyError,
|
||||||
rate_limit::{rate_limiter::RateLimiter, RateLimit},
|
rate_limit::RateLimitCell,
|
||||||
settings::{structs::Settings, SETTINGS},
|
settings::{structs::Settings, SETTINGS},
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
||||||
|
@ -37,12 +37,7 @@ use reqwest::Client;
|
||||||
use reqwest_middleware::ClientBuilder;
|
use reqwest_middleware::ClientBuilder;
|
||||||
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
|
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
|
||||||
use reqwest_tracing::TracingMiddleware;
|
use reqwest_tracing::TracingMiddleware;
|
||||||
use std::{
|
use std::{env, thread, time::Duration};
|
||||||
env,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
|
|
||||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||||
|
@ -107,13 +102,7 @@ async fn main() -> Result<(), LemmyError> {
|
||||||
// Set up the rate limiter
|
// Set up the rate limiter
|
||||||
let rate_limit_config =
|
let rate_limit_config =
|
||||||
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
||||||
|
let rate_limit_cell = RateLimitCell::new(rate_limit_config).await;
|
||||||
// TODO this isn't live-updating
|
|
||||||
// https://github.com/LemmyNet/lemmy/issues/2508
|
|
||||||
let rate_limiter = RateLimit {
|
|
||||||
rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
|
|
||||||
rate_limit_config,
|
|
||||||
};
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Starting http server at {}:{}",
|
"Starting http server at {}:{}",
|
||||||
|
@ -144,12 +133,12 @@ async fn main() -> Result<(), LemmyError> {
|
||||||
|
|
||||||
let chat_server = ChatServer::startup(
|
let chat_server = ChatServer::startup(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
rate_limiter.clone(),
|
|
||||||
|c, i, o, d| Box::pin(match_websocket_operation(c, i, o, d)),
|
|c, i, o, d| Box::pin(match_websocket_operation(c, i, o, d)),
|
||||||
|c, i, o, d| Box::pin(match_websocket_operation_crud(c, i, o, d)),
|
|c, i, o, d| Box::pin(match_websocket_operation_crud(c, i, o, d)),
|
||||||
client.clone(),
|
client.clone(),
|
||||||
settings.clone(),
|
settings.clone(),
|
||||||
secret.clone(),
|
secret.clone(),
|
||||||
|
rate_limit_cell.clone(),
|
||||||
)
|
)
|
||||||
.start();
|
.start();
|
||||||
|
|
||||||
|
@ -162,15 +151,15 @@ async fn main() -> Result<(), LemmyError> {
|
||||||
client.clone(),
|
client.clone(),
|
||||||
settings.to_owned(),
|
settings.to_owned(),
|
||||||
secret.to_owned(),
|
secret.to_owned(),
|
||||||
|
rate_limit_cell.clone(),
|
||||||
);
|
);
|
||||||
let rate_limiter = rate_limiter.clone();
|
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(actix_web::middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.wrap(TracingLogger::<QuieterRootSpanBuilder>::new())
|
.wrap(TracingLogger::<QuieterRootSpanBuilder>::new())
|
||||||
.app_data(Data::new(context))
|
.app_data(Data::new(context))
|
||||||
.app_data(Data::new(rate_limiter.clone()))
|
.app_data(Data::new(rate_limit_cell.clone()))
|
||||||
// The routes
|
// The routes
|
||||||
.configure(|cfg| api_routes::config(cfg, &rate_limiter))
|
.configure(|cfg| api_routes::config(cfg, rate_limit_cell))
|
||||||
.configure(|cfg| {
|
.configure(|cfg| {
|
||||||
if federation_enabled {
|
if federation_enabled {
|
||||||
lemmy_apub::http::routes::config(cfg);
|
lemmy_apub::http::routes::config(cfg);
|
||||||
|
@ -178,7 +167,7 @@ async fn main() -> Result<(), LemmyError> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.configure(feeds::config)
|
.configure(feeds::config)
|
||||||
.configure(|cfg| images::config(cfg, pictrs_client.clone(), &rate_limiter))
|
.configure(|cfg| images::config(cfg, pictrs_client.clone(), rate_limit_cell))
|
||||||
.configure(nodeinfo::config)
|
.configure(nodeinfo::config)
|
||||||
})
|
})
|
||||||
.bind((settings_bind.bind, settings_bind.port))?
|
.bind((settings_bind.bind, settings_bind.port))?
|
||||||
|
|
Loading…
Reference in a new issue