forked from mirrors/relay
Use custom threadpool for client signatures
This commit is contained in:
parent
8ff4961ded
commit
d97cc4e5a4
7 changed files with 189 additions and 23 deletions
58
Cargo.lock
generated
58
Cargo.lock
generated
|
@ -409,6 +409,7 @@ dependencies = [
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"flume",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http-signature-normalization-actix",
|
"http-signature-normalization-actix",
|
||||||
"lru",
|
"lru",
|
||||||
|
@ -1165,6 +1166,19 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flume"
|
||||||
|
version = "0.10.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"nanorand",
|
||||||
|
"pin-project",
|
||||||
|
"spin 0.9.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -1315,8 +1329,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1444,9 +1460,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-signature-normalization-actix"
|
name = "http-signature-normalization-actix"
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dc95d9ca3b4e2f93a97e5ccf9f26992c69a272e0abad8807180f0a9e9b59e31"
|
checksum = "218124b6b0c6ef27526493f50faf00b7cf8a3840bb1d5268f6ee8eef753b8225"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
|
@ -1677,7 +1693,7 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"spin",
|
"spin 0.5.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1943,6 +1959,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nanorand"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "never"
|
name = "never"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2685,7 +2710,7 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"spin",
|
"spin 0.5.2",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winapi",
|
"winapi",
|
||||||
|
@ -2848,9 +2873,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.1"
|
version = "0.101.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e"
|
checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
|
@ -2892,18 +2917,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.175"
|
version = "1.0.176"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b"
|
checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.175"
|
version = "1.0.176"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4"
|
checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2912,9 +2937,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.103"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
|
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -3063,6 +3088,15 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spki"
|
name = "spki"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
|
|
@ -40,6 +40,7 @@ config = "0.13.0"
|
||||||
console-subscriber = { version = "0.1", optional = true }
|
console-subscriber = { version = "0.1", optional = true }
|
||||||
dashmap = "5.1.0"
|
dashmap = "5.1.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
flume = "0.10.14"
|
||||||
futures-util = "0.3.17"
|
futures-util = "0.3.17"
|
||||||
lru = "0.11.0"
|
lru = "0.11.0"
|
||||||
metrics = "0.21.0"
|
metrics = "0.21.0"
|
||||||
|
@ -88,7 +89,7 @@ default-features = false
|
||||||
features = ["background-jobs-actix", "error-logging"]
|
features = ["background-jobs-actix", "error-logging"]
|
||||||
|
|
||||||
[dependencies.http-signature-normalization-actix]
|
[dependencies.http-signature-normalization-actix]
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["client", "server", "sha-2"]
|
features = ["client", "server", "sha-2"]
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl State {
|
||||||
self.node_cache.clone()
|
self.node_cache.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn requests(&self, config: &Config) -> Requests {
|
pub(crate) fn requests(&self, config: &Config, spawner: crate::requests::Spawner) -> Requests {
|
||||||
Requests::new(
|
Requests::new(
|
||||||
config.generate_url(UrlKind::MainKey).to_string(),
|
config.generate_url(UrlKind::MainKey).to_string(),
|
||||||
self.private_key.clone(),
|
self.private_key.clone(),
|
||||||
|
@ -49,6 +49,7 @@ impl State {
|
||||||
self.last_online.clone(),
|
self.last_online.clone(),
|
||||||
config.client_pool_size(),
|
config.client_pool_size(),
|
||||||
config.client_timeout(),
|
config.client_timeout(),
|
||||||
|
spawner,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,3 +242,9 @@ impl From<rsa::errors::Error> for ErrorKind {
|
||||||
ErrorKind::Rsa(e)
|
ErrorKind::Rsa(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<http_signature_normalization_actix::Canceled> for ErrorKind {
|
||||||
|
fn from(_: http_signature_normalization_actix::Canceled) -> Self {
|
||||||
|
Self::Canceled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
data::{ActorCache, MediaCache, NodeCache, State},
|
data::{ActorCache, MediaCache, NodeCache, State},
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
jobs::{process_listeners::Listeners, record_last_online::RecordLastOnline},
|
jobs::{process_listeners::Listeners, record_last_online::RecordLastOnline},
|
||||||
requests::Requests,
|
requests::{Requests, Spawner},
|
||||||
};
|
};
|
||||||
use background_jobs::{
|
use background_jobs::{
|
||||||
memory_storage::{ActixTimer, Storage},
|
memory_storage::{ActixTimer, Storage},
|
||||||
|
@ -44,6 +44,7 @@ pub(crate) fn create_workers(
|
||||||
actors: ActorCache,
|
actors: ActorCache,
|
||||||
media: MediaCache,
|
media: MediaCache,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
spawner: Spawner,
|
||||||
) -> JobServer {
|
) -> JobServer {
|
||||||
let deliver_concurrency = config.deliver_concurrency();
|
let deliver_concurrency = config.deliver_concurrency();
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ pub(crate) fn create_workers(
|
||||||
JobServer::new(queue_handle),
|
JobServer::new(queue_handle),
|
||||||
media.clone(),
|
media.clone(),
|
||||||
config.clone(),
|
config.clone(),
|
||||||
|
spawner.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.register::<Deliver>()
|
.register::<Deliver>()
|
||||||
|
@ -110,9 +112,10 @@ impl JobState {
|
||||||
job_server: JobServer,
|
job_server: JobServer,
|
||||||
media: MediaCache,
|
media: MediaCache,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
spawner: Spawner,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
JobState {
|
JobState {
|
||||||
requests: state.requests(&config),
|
requests: state.requests(&config, spawner),
|
||||||
node_cache: state.node_cache(),
|
node_cache: state.node_cache(),
|
||||||
actors,
|
actors,
|
||||||
config,
|
config,
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -33,6 +33,8 @@ mod requests;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod telegram;
|
mod telegram;
|
||||||
|
|
||||||
|
use crate::requests::Spawner;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
args::Args,
|
args::Args,
|
||||||
config::Config,
|
config::Config,
|
||||||
|
@ -257,12 +259,19 @@ async fn do_server_main(
|
||||||
|
|
||||||
let keys = config.open_keys()?;
|
let keys = config.open_keys()?;
|
||||||
|
|
||||||
|
let spawner = Spawner::build()?;
|
||||||
|
|
||||||
let bind_address = config.bind_address();
|
let bind_address = config.bind_address();
|
||||||
let server = HttpServer::new(move || {
|
let server = HttpServer::new(move || {
|
||||||
let requests = state.requests(&config);
|
let requests = state.requests(&config, spawner.clone());
|
||||||
|
|
||||||
let job_server =
|
let job_server = create_workers(
|
||||||
create_workers(state.clone(), actors.clone(), media.clone(), config.clone());
|
state.clone(),
|
||||||
|
actors.clone(),
|
||||||
|
media.clone(),
|
||||||
|
config.clone(),
|
||||||
|
spawner.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let app = App::new()
|
let app = App::new()
|
||||||
.app_data(web::Data::new(db.clone()))
|
.app_data(web::Data::new(db.clone()))
|
||||||
|
|
120
src/requests.rs
120
src/requests.rs
|
@ -7,7 +7,7 @@ use actix_web::http::header::Date;
|
||||||
use awc::{error::SendRequestError, Client, ClientResponse, Connector};
|
use awc::{error::SendRequestError, Client, ClientResponse, Connector};
|
||||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use http_signature_normalization_actix::prelude::*;
|
use http_signature_normalization_actix::{prelude::*, Canceled, Spawn};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rsa::{
|
use rsa::{
|
||||||
pkcs1v15::SigningKey,
|
pkcs1v15::SigningKey,
|
||||||
|
@ -16,7 +16,12 @@ use rsa::{
|
||||||
RsaPrivateKey,
|
RsaPrivateKey,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
panic::AssertUnwindSafe,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::JoinHandle,
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
use tracing_awc::Tracing;
|
use tracing_awc::Tracing;
|
||||||
|
@ -145,7 +150,7 @@ pub(crate) struct Requests {
|
||||||
key_id: String,
|
key_id: String,
|
||||||
user_agent: String,
|
user_agent: String,
|
||||||
private_key: RsaPrivateKey,
|
private_key: RsaPrivateKey,
|
||||||
config: Config,
|
config: Config<Spawner>,
|
||||||
breakers: Breakers,
|
breakers: Breakers,
|
||||||
last_online: Arc<LastOnline>,
|
last_online: Arc<LastOnline>,
|
||||||
}
|
}
|
||||||
|
@ -192,6 +197,7 @@ impl Requests {
|
||||||
last_online: Arc<LastOnline>,
|
last_online: Arc<LastOnline>,
|
||||||
pool_size: usize,
|
pool_size: usize,
|
||||||
timeout_seconds: u64,
|
timeout_seconds: u64,
|
||||||
|
spawner: Spawner,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Requests {
|
Requests {
|
||||||
pool_size,
|
pool_size,
|
||||||
|
@ -199,7 +205,7 @@ impl Requests {
|
||||||
key_id,
|
key_id,
|
||||||
user_agent,
|
user_agent,
|
||||||
private_key,
|
private_key,
|
||||||
config: Config::default().mastodon_compat(),
|
config: Config::new().mastodon_compat().spawner(spawner),
|
||||||
breakers,
|
breakers,
|
||||||
last_online,
|
last_online,
|
||||||
}
|
}
|
||||||
|
@ -415,3 +421,109 @@ impl Signer {
|
||||||
Ok(STANDARD.encode(signature.to_bytes().as_ref()))
|
Ok(STANDARD.encode(signature.to_bytes().as_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signature_thread(
|
||||||
|
receiver: flume::Receiver<Box<dyn FnOnce() + Send>>,
|
||||||
|
shutdown: flume::Receiver<()>,
|
||||||
|
) {
|
||||||
|
let stopping = AtomicBool::new(false);
|
||||||
|
while !stopping.load(Ordering::Acquire) {
|
||||||
|
flume::Selector::new()
|
||||||
|
.recv(&receiver, |res| match res {
|
||||||
|
Ok(f) => {
|
||||||
|
let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||||
|
(f)();
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
tracing::warn!("Signature fn panicked: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
tracing::warn!("Receive error, stopping");
|
||||||
|
stopping.store(true, Ordering::Release)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.recv(&shutdown, |_| {
|
||||||
|
tracing::warn!("Stopping");
|
||||||
|
stopping.store(true, Ordering::Release)
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct Spawner {
|
||||||
|
sender: flume::Sender<Box<dyn FnOnce() + Send>>,
|
||||||
|
threads: Option<Arc<Vec<JoinHandle<()>>>>,
|
||||||
|
shutdown: flume::Sender<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spawner {
|
||||||
|
pub(crate) fn build() -> std::io::Result<Self> {
|
||||||
|
let threads = std::thread::available_parallelism()
|
||||||
|
.map(usize::from)
|
||||||
|
.unwrap_or(1);
|
||||||
|
|
||||||
|
let (sender, receiver) = flume::bounded(8);
|
||||||
|
let (shutdown, shutdown_rx) = flume::bounded(threads);
|
||||||
|
|
||||||
|
let threads = (0..threads)
|
||||||
|
.map(|i| {
|
||||||
|
let receiver = receiver.clone();
|
||||||
|
let shutdown_rx = shutdown_rx.clone();
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name(format!("signature-thread-{i}"))
|
||||||
|
.spawn(move || {
|
||||||
|
signature_thread(receiver, shutdown_rx);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok(Spawner {
|
||||||
|
sender,
|
||||||
|
threads: Some(Arc::new(threads)),
|
||||||
|
shutdown,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Spawner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(threads) = self.threads.take().and_then(Arc::into_inner) {
|
||||||
|
for _ in &threads {
|
||||||
|
let _ = self.shutdown.send(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for thread in threads {
|
||||||
|
let _ = thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spawn for Spawner {
|
||||||
|
type Future<T> = std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, Canceled>>>>;
|
||||||
|
|
||||||
|
fn spawn_blocking<Func, Out>(&self, func: Func) -> Self::Future<Out>
|
||||||
|
where
|
||||||
|
Func: FnOnce() -> Out + Send + 'static,
|
||||||
|
Out: Send + 'static,
|
||||||
|
{
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
|
||||||
|
let _ = sender
|
||||||
|
.send_async(Box::new(move || {
|
||||||
|
if tx.send((func)()).is_err() {
|
||||||
|
tracing::warn!("Requestor hung up");
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
rx.recv_async().await.map_err(|_| Canceled)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue