server: add ddos protection

This commit is contained in:
nym21
2025-06-08 17:06:36 +02:00
parent e6934cd5e2
commit 5a6b71cbeb
16 changed files with 85 additions and 12 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ use super::AppState;
mod explorer;
mod query;
pub use query::DTS;
pub use query::Bridge;
pub trait ApiRoutes {
fn add_api_routes(self) -> Self;
@@ -7,12 +7,12 @@ use crate::{VERSION, Website};
const SCRIPTS: &str = "scripts";
#[allow(clippy::upper_case_acronyms)]
pub trait DTS {
fn generate_dts_file(&self, website: Website, websites_path: &Path) -> io::Result<()>;
pub trait Bridge {
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()>;
}
impl DTS for Query<'static> {
fn generate_dts_file(&self, website: Website, websites_path: &Path) -> io::Result<()> {
impl Bridge for Query<'static> {
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()> {
if website.is_none() {
return Ok(());
}
+25 -4
View File
@@ -5,14 +5,18 @@ use axum::{
response::{IntoResponse, Response},
};
use brk_query::{Format, Index, Output, Params};
use brk_vec::{CollectableVec, StoredVec};
use color_eyre::eyre::eyre;
use crate::traits::{HeaderMapExtended, ModifiedState, ResponseExtended};
use super::AppState;
mod dts;
mod bridge;
pub use dts::*;
pub use bridge::*;
const MAX_WEIGHT: usize = 320_000;
pub async fn handler(
headers: HeaderMap,
@@ -48,6 +52,23 @@ fn req_to_response_res(
&values.iter().map(|v| v.as_str()).collect::<Vec<_>>(),
);
if vecs.is_empty() {
return Ok(Json(vec![] as Vec<usize>).into_response());
}
let weight = vecs
.iter()
.map(|(_, v)| {
let len = v.len();
let count = StoredVec::<usize, usize>::range_count(from, to, len);
count * v.value_type_to_size_of()
})
.sum::<usize>();
if weight > MAX_WEIGHT {
return Err(eyre!("Request is too heavy, max weight is {MAX_WEIGHT}"));
}
let mut date_modified_opt = None;
if to.is_none() {
@@ -75,8 +96,8 @@ fn req_to_response_res(
Output::TSV(s) => s.into_response(),
Output::Json(v) => match v {
brk_query::Value::Single(v) => Json(v).into_response(),
brk_query::Value::List(l) => Json(l).into_response(),
brk_query::Value::Matrix(m) => Json(m).into_response(),
brk_query::Value::List(v) => Json(v).into_response(),
brk_query::Value::Matrix(v) => Json(v).into_response(),
},
Output::MD(s) => s.into_response(),
};
+2 -2
View File
@@ -10,7 +10,7 @@ use std::{
time::Duration,
};
use api::{ApiRoutes, DTS};
use api::{ApiRoutes, Bridge};
use axum::{
Json, Router,
body::Body,
@@ -89,7 +89,7 @@ impl Server {
downloaded_websites_path
};
query.generate_dts_file(website, websites_path.as_path())?;
query.generate_bridge_file(website, websites_path.as_path())?;
Some(websites_path)
} else {
+1
View File
@@ -13,6 +13,7 @@ pub trait AnyVec: Send + Sync {
}
fn modified_time(&self) -> Result<Duration>;
fn index_type_to_string(&self) -> String;
fn value_type_to_size_of(&self) -> usize;
}
pub trait AnyIterableVec<I, T>: AnyVec {
+6
View File
@@ -34,6 +34,12 @@ where
}
}
fn range_count(from: Option<i64>, to: Option<i64>, len: usize) -> usize {
let from = from.map(|i| Self::i64_to_usize(i, len));
let to = to.map(|i| Self::i64_to_usize(i, len));
(from.unwrap_or_default()..to.unwrap_or(len)).count()
}
#[doc(hidden)]
fn collect_signed_range(&self, from: Option<i64>, to: Option<i64>) -> Result<Vec<T>> {
let len = self.len();
@@ -361,6 +361,11 @@ where
fn index_type_to_string(&self) -> String {
I::to_string()
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T> Clone for CompressedVec<I, T> {
+5
View File
@@ -255,6 +255,11 @@ where
ComputedVec::LazyFrom3(v) => v.modified_time(),
}
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
pub enum ComputedVecIterator<'a, I, T, S1I, S1T, S2I, S2T, S3I, S3T> {
+5
View File
@@ -1298,6 +1298,11 @@ where
fn index_type_to_string(&self) -> String {
I::to_string()
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T> AnyIterableVec<I, T> for EagerVec<I, T>
+5
View File
@@ -127,6 +127,11 @@ where
fn index_type_to_string(&self) -> String {
I::to_string()
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
pub trait AnyIndexedVec: AnyVec {
+5
View File
@@ -146,6 +146,11 @@ where
fn modified_time(&self) -> Result<std::time::Duration> {
self.source.modified_time()
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T, S1I, S1T> AnyIterableVec<I, T> for LazyVecFrom1<I, T, S1I, S1T>
+5
View File
@@ -194,6 +194,11 @@ where
.modified_time()?
.min(self.source2.modified_time()?))
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T, S1I, S1T, S2I, S2T> AnyIterableVec<I, T> for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
+5
View File
@@ -229,6 +229,11 @@ where
.min(self.source2.modified_time()?)
.min(self.source3.modified_time()?))
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> AnyIterableVec<I, T>
+5
View File
@@ -195,6 +195,11 @@ where
fn index_type_to_string(&self) -> String {
I::to_string()
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
impl<I, T> Clone for RawVec<I, T> {
+5
View File
@@ -149,6 +149,11 @@ where
StoredVec::Compressed(v) => v.name(),
}
}
#[inline]
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
}
#[derive(Debug)]
+1 -1
View File
@@ -2,7 +2,7 @@
// File auto-generated, any modifications will be overwritten
//
export const VERSION = "v0.0.45";
export const VERSION = "v0.0.46";
/** @typedef {0} DateIndex */
/** @typedef {1} DecadeIndex */