Server: add actix-governer for rate limiting

This commit is contained in:
asonix 2022-12-28 22:17:25 -06:00
parent 3404b377d3
commit ed1ad7fafc
4 changed files with 291 additions and 29 deletions

242
server/Cargo.lock generated
View file

@ -42,6 +42,18 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "actix-governor"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fbf4afa1e2f7c28040febe2a7199ad0a5fed564dd645da06ab12642c7d22483"
dependencies = [
"actix-http",
"actix-web",
"futures",
"governor",
]
[[package]]
name = "actix-http"
version = "3.2.2"
@ -389,6 +401,12 @@ dependencies = [
"serde",
]
[[package]]
name = "bumpalo"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -514,6 +532,19 @@ dependencies = [
"memchr",
]
[[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 0.9.5",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -541,6 +572,7 @@ dependencies = [
name = "doglinks"
version = "0.1.0"
dependencies = [
"actix-governor",
"actix-web",
"actix-web-lab",
"serde",
@ -600,12 +632,65 @@ dependencies = [
"winapi",
]
[[package]]
name = "futures"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-macro"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.25"
@ -618,14 +703,25 @@ version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]]
name = "futures-util"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@ -658,7 +754,24 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "governor"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19775995ee20209163239355bc3ad2f33f83da35d9ef72dea26e5af753552c87"
dependencies = [
"dashmap",
"futures",
"futures-timer",
"no-std-compat",
"nonzero_ext",
"parking_lot 0.12.1",
"quanta",
"rand",
"smallvec",
]
[[package]]
@ -789,6 +902,15 @@ dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "language-tags"
version = "0.3.2"
@ -844,6 +966,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]]
name = "mediatype"
version = "0.19.11"
@ -898,10 +1029,22 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
name = "num_cpus"
version = "1.14.0"
@ -1005,6 +1148,22 @@ dependencies = [
"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]]
name = "quote"
version = "1.0.23"
@ -1044,6 +1203,15 @@ dependencies = [
"getrandom",
]
[[package]]
name = "raw-cpuid"
version = "10.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -1414,12 +1582,82 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winapi"
version = "0.3.9"

View file

@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-governor = "0.3.2"
actix-web = "4"
actix-web-lab = { version = "0.18.8", features = ["spa"] }
serde = { version = "1", features = ["derive"] }

View file

@ -1,7 +1,11 @@
use std::time::Duration;
use actix_governor::{GlobalKeyExtractor, Governor, GovernorConfigBuilder};
use actix_web::{
body::MessageBody,
dev::{ServiceRequest, ServiceResponse},
error::{ErrorBadRequest, ErrorInternalServerError},
guard,
http::header::LOCATION,
web::{self, Json, Path},
App, HttpResponse, HttpServer,
@ -10,8 +14,7 @@ use actix_web_lab::{
middleware::{from_fn, Next},
web::spa,
};
use sled::{CompareAndSwapError, Config, Db, Tree};
use std::collections::BTreeSet;
use sled::{Config, Db, Tree};
use url::Url;
use uuid::Uuid;
@ -32,8 +35,6 @@ impl State {
}
}
type StateValue = BTreeSet<Uuid>;
impl State {
async fn save_link(&self, link: Url) -> sled::Result<Uuid> {
let link_tree = self.link_tree.clone();
@ -44,29 +45,13 @@ impl State {
uuid_tree.insert(uuid.as_bytes(), link.as_str())?;
let mut old = link_tree.get(link.as_str())?;
let mut url_key = Vec::from(link.as_str().as_bytes());
url_key.push(0);
url_key.extend_from_slice(uuid.as_bytes());
loop {
let new = old
.clone()
.and_then(|ivec| serde_json::from_slice(&ivec).ok())
.or_else(|| Some(StateValue::new()))
.map(|mut bts: StateValue| {
bts.insert(uuid);
bts
})
.and_then(|bts| serde_json::to_vec(&bts).ok());
link_tree.insert(url_key, uuid.as_bytes())?;
match link_tree.compare_and_swap(link.to_string(), old, new)? {
Ok(_) => return Ok(uuid),
Err(CompareAndSwapError {
current,
proposed: _,
}) => {
old = current;
}
}
}
Ok(uuid)
})
.await
.expect("db panicked")
@ -162,12 +147,37 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let state = State::build(db)?;
let limiter_config = GovernorConfigBuilder::const_default()
.const_period(Duration::from_secs(10))
.const_burst_size(1)
.use_headers()
.finish()
.expect("Created a valid rate limit config");
let general_config = GovernorConfigBuilder::default()
.period(Duration::from_millis(500))
.burst_size(4)
.key_extractor(GlobalKeyExtractor)
.use_headers()
.finish()
.expect("Created a valid rate limit config");
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(state.clone()))
.wrap(from_fn(deny_invalid_agents))
.route("/link", web::post().to(new_link))
.route("/link/{uuid}", web::get().to(link_to))
.service(
web::resource("/link")
.guard(guard::Post())
.wrap(Governor::new(&limiter_config))
.wrap(Governor::new(&general_config))
.route(web::post().to(new_link)),
)
.service(
web::resource("/link/{uuid}")
.guard(guard::Get())
.route(web::get().to(link_to)),
)
.service(
spa()
.index_file("./static/index.html")

View file

@ -3,8 +3,21 @@
<head>
<meta charset="utf-8">
<meta content="width=device-width,initial-scale=1" name="viewport">
<title>Doglinks</title>
<script src="/static/main.js"></script>
<style>
body {
background-color: #333;
color: #f5f5f5;
font-family: sans-serif;
margin: 0;
}
* {
box-sizing: border-box;
}
</style>
</head>
<body>