rate limiting

This commit is contained in:
Tyler Murphy 2023-01-31 00:00:13 -05:00
parent 215c806b9a
commit ef24cfedbf
8 changed files with 322 additions and 44 deletions

265
Cargo.lock generated
View file

@ -112,6 +112,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.3.0" version = "1.3.0"
@ -150,6 +156,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -160,6 +175,19 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "dashmap"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.6" version = "0.10.6"
@ -208,25 +236,58 @@ dependencies = [
] ]
[[package]] [[package]]
name = "futures-channel" name = "futures"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
[[package]]
name = "futures-executor"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -235,25 +296,35 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
[[package]]
name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io",
"futures-macro", "futures-macro",
"futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
@ -277,7 +348,25 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "governor"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c390a940a5d157878dd057c78680a33ce3415bcd05b4799509ea44210914b4d5"
dependencies = [
"cfg-if",
"dashmap",
"futures",
"futures-timer",
"no-std-compat",
"nonzero_ext",
"parking_lot",
"quanta",
"rand",
"smallvec",
] ]
[[package]] [[package]]
@ -401,6 +490,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -443,6 +541,15 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.0" version = "0.7.0"
@ -479,16 +586,28 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys", "windows-sys",
] ]
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]] [[package]]
name = "nonempty" name = "nonempty"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.46.0" version = "0.46.0"
@ -603,6 +722,22 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "quanta"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
dependencies = [
"crossbeam-utils",
"libc",
"mach",
"once_cell",
"raw-cpuid",
"wasi 0.10.2+wasi-snapshot-preview1",
"web-sys",
"winapi",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.23" version = "1.0.23"
@ -642,6 +777,15 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "raw-cpuid"
version = "10.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.16" version = "0.2.16"
@ -976,6 +1120,26 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tower_governor"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6be418f6d18863291f0a7fa1da1de71495a19a54b5fb44969136f731a47e86"
dependencies = [
"axum",
"forwarded-header-value",
"futures",
"futures-core",
"governor",
"http",
"pin-project",
"thiserror",
"tokio",
"tower",
"tower-layer",
"tracing",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.37" version = "0.1.37"
@ -1090,12 +1254,82 @@ dependencies = [
"try-lock", "try-lock",
] ]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1192,6 +1426,7 @@ dependencies = [
"tower", "tower",
"tower-cookies", "tower-cookies",
"tower-http", "tower-http",
"tower_governor",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]

View file

@ -6,16 +6,17 @@ edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.23.0", features = ["full"] } tokio = { version = "1.23.0", features = ["full"] }
axum = { version = "0.6.4", features = ["headers"] } axum = { version = "0.6.4", features = ["headers"] }
axum-client-ip = "0.3.1"
tower-http = { version = "0.3.5", features = ["fs"] } tower-http = { version = "0.3.5", features = ["fs"] }
tower_governor = "0.0.4"
tower-cookies = "0.8.0" tower-cookies = "0.8.0"
tower = "0.4.13" tower = "0.4.13"
tracing = "0.1.37"
tracing-subscriber = "0.3.16"
bytes = "1.3.0" bytes = "1.3.0"
axum-client-ip = "0.3.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["std"] } serde_json = { version = "1.0", features = ["std"] }
rusqlite = { version = "0.28.0", features = ["bundled"] } rusqlite = { version = "0.28.0", features = ["bundled"] }
rand = "0.8.5" rand = "0.8.5"
time = "0.3.17" time = "0.3.17"
lazy_static = "1.4.0" lazy_static = "1.4.0"
tracing = "0.1.37"
tracing-subscriber = "0.3.16"

View file

@ -15,7 +15,7 @@ function header(home, people, user_id) {
</a> </a>
</div> </div>
<a class="pfp" id="profile" hreF="profile"> <a class="pfp" id="profile" hreF="profile">
${pfp(user_id)} ${user_id === undefined ? '' : pfp(user_id)}
</a> </a>
</div> </div>
<div class="spacer"></div> <div class="spacer"></div>

View file

@ -243,7 +243,12 @@ async function load() {
} }
async function init() { async function init() {
data.user = (await loadself()).json let request = (await loadself());
if (request.status === 429) {
header(true, false)
throw new Error("Rate limited");
}
data.user = request.json
header(true, false, data.user.user_id) header(true, false, data.user.user_id)
data.users[data.user.user_id] = data.user data.users[data.user.user_id] = data.user
const posts = await load() const posts = await load()

View file

@ -48,7 +48,12 @@ async function loadMore() {
} }
async function load() { async function load() {
const self = (await loadself()).json let request = await loadself()
if (request.status === 429) {
header(false, true)
throw new Error("Rate limited");
}
const self = request.json
header(false, true, self.user_id) header(false, true, self.user_id)
const users = (await loaduserspage(page)).json const users = (await loaduserspage(page)).json
if (users.length === 0) { if (users.length === 0) {

View file

@ -89,7 +89,14 @@ async function load() {
params[key] = value params[key] = value
} }
data.self = (await loadself()).json; let request = await loadself()
if (request.status === 429) {
header(false, false)
throw new Error("Rate limited");
}
data.self = request.json;
data.users[data.self.user_id] = data.self data.users[data.self.user_id] = data.self
let id; let id;

View file

@ -1,5 +1,48 @@
use crate::types::extract::RouterURI;
use axum::{Extension, Router, BoxError, error_handling::HandleErrorLayer};
use tower::ServiceBuilder;
use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer, errors::display_error, key_extractor::SmartIpKeyExtractor};
pub mod admin; pub mod admin;
pub mod auth; pub mod auth;
pub mod pages; pub mod pages;
pub mod posts; pub mod posts;
pub mod users; pub mod users;
pub fn router() -> Router {
let governor_conf = Box::new(
GovernorConfigBuilder::default()
.burst_size(10)
.per_second(1)
.key_extractor(SmartIpKeyExtractor)
.finish()
.expect("Failed to create rate limiter"),
);
Router::new()
.nest(
"/admin",
admin::router().layer(Extension(RouterURI("/api/admin"))),
)
.nest(
"/auth",
auth::router().layer(Extension(RouterURI("/api/auth"))),
)
.nest(
"/users",
users::router().layer(Extension(RouterURI("/api/users"))),
)
.nest(
"/posts",
posts::router().layer(Extension(RouterURI("/api/posts"))),
)
.layer(
ServiceBuilder::new()
.layer(HandleErrorLayer::new(|e: BoxError| async move {
display_error(e)
}))
.layer(GovernorLayer {
config: Box::leak(governor_conf),
}),
)
}

View file

@ -3,7 +3,7 @@ use axum::{
http::{Request, StatusCode}, http::{Request, StatusCode},
middleware::{self, Next}, middleware::{self, Next},
response::Response, response::Response,
Extension, RequestExt, Router, RequestExt, Router,
}; };
use axum_client_ip::ClientIp; use axum_client_ip::ClientIp;
use std::{net::SocketAddr, process::exit}; use std::{net::SocketAddr, process::exit};
@ -14,10 +14,7 @@ use tracing_subscriber::{
}; };
use types::http::ResponseCode; use types::http::ResponseCode;
use crate::{ use crate::api::pages;
api::{auth, pages, posts, users},
types::extract::RouterURI,
};
mod admin; mod admin;
mod api; mod api;
@ -77,22 +74,7 @@ async fn main() {
.layer(middleware::from_fn(log)) .layer(middleware::from_fn(log))
.layer(middleware::from_fn(serve)) .layer(middleware::from_fn(serve))
.nest("/", pages::router()) .nest("/", pages::router())
.nest( .nest("/api", api::router())
"/api/admin",
api::admin::router().layer(Extension(RouterURI("/api/admin"))),
)
.nest(
"/api/auth",
auth::router().layer(Extension(RouterURI("/api/auth"))),
)
.nest(
"/api/users",
users::router().layer(Extension(RouterURI("/api/users"))),
)
.nest(
"/api/posts",
posts::router().layer(Extension(RouterURI("/api/posts"))),
)
.layer(CookieManagerLayer::new()); .layer(CookieManagerLayer::new());
let Ok(addr) = "[::]:8080".parse::<std::net::SocketAddr>() else { let Ok(addr) = "[::]:8080".parse::<std::net::SocketAddr>() else {