mirror of
				https://git.asonix.dog/asonix/relay.git
				synced 2025-10-30 21:12:42 +00:00 
			
		
		
		
	Use custom threadpool for client signatures
This commit is contained in:
		
							parent
							
								
									8ff4961ded
								
							
						
					
					
						commit
						d97cc4e5a4
					
				
							
								
								
									
										58
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										58
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -409,6 +409,7 @@ dependencies = [ | |||
|  "console-subscriber", | ||||
|  "dashmap", | ||||
|  "dotenv", | ||||
|  "flume", | ||||
|  "futures-util", | ||||
|  "http-signature-normalization-actix", | ||||
|  "lru", | ||||
|  | @ -1165,6 +1166,19 @@ dependencies = [ | |||
|  "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]] | ||||
| name = "fnv" | ||||
| version = "1.0.7" | ||||
|  | @ -1315,8 +1329,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "js-sys", | ||||
|  "libc", | ||||
|  "wasi", | ||||
|  "wasm-bindgen", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1444,9 +1460,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "http-signature-normalization-actix" | ||||
| version = "0.8.0" | ||||
| version = "0.9.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1dc95d9ca3b4e2f93a97e5ccf9f26992c69a272e0abad8807180f0a9e9b59e31" | ||||
| checksum = "218124b6b0c6ef27526493f50faf00b7cf8a3840bb1d5268f6ee8eef753b8225" | ||||
| dependencies = [ | ||||
|  "actix-http", | ||||
|  "actix-rt", | ||||
|  | @ -1677,7 +1693,7 @@ version = "1.4.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||
| dependencies = [ | ||||
|  "spin", | ||||
|  "spin 0.5.2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1943,6 +1959,15 @@ dependencies = [ | |||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "nanorand" | ||||
| version = "0.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" | ||||
| dependencies = [ | ||||
|  "getrandom", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "never" | ||||
| version = "0.1.0" | ||||
|  | @ -2685,7 +2710,7 @@ dependencies = [ | |||
|  "cc", | ||||
|  "libc", | ||||
|  "once_cell", | ||||
|  "spin", | ||||
|  "spin 0.5.2", | ||||
|  "untrusted", | ||||
|  "web-sys", | ||||
|  "winapi", | ||||
|  | @ -2848,9 +2873,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "rustls-webpki" | ||||
| version = "0.101.1" | ||||
| version = "0.101.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" | ||||
| checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" | ||||
| dependencies = [ | ||||
|  "ring", | ||||
|  "untrusted", | ||||
|  | @ -2892,18 +2917,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" | |||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.175" | ||||
| version = "1.0.176" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" | ||||
| checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.175" | ||||
| version = "1.0.176" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" | ||||
| checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -2912,9 +2937,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.103" | ||||
| version = "1.0.104" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" | ||||
| checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "ryu", | ||||
|  | @ -3063,6 +3088,15 @@ version = "0.5.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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]] | ||||
| name = "spki" | ||||
| version = "0.7.2" | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ config = "0.13.0" | |||
| console-subscriber = { version = "0.1", optional = true } | ||||
| dashmap = "5.1.0" | ||||
| dotenv = "0.15.0" | ||||
| flume = "0.10.14" | ||||
| futures-util = "0.3.17" | ||||
| lru = "0.11.0" | ||||
| metrics = "0.21.0" | ||||
|  | @ -88,7 +89,7 @@ default-features = false | |||
| features = ["background-jobs-actix", "error-logging"] | ||||
| 
 | ||||
| [dependencies.http-signature-normalization-actix] | ||||
| version = "0.8.0" | ||||
| version = "0.9.1" | ||||
| default-features = false | ||||
| features = ["client", "server", "sha-2"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ impl State { | |||
|         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( | ||||
|             config.generate_url(UrlKind::MainKey).to_string(), | ||||
|             self.private_key.clone(), | ||||
|  | @ -49,6 +49,7 @@ impl State { | |||
|             self.last_online.clone(), | ||||
|             config.client_pool_size(), | ||||
|             config.client_timeout(), | ||||
|             spawner, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -242,3 +242,9 @@ impl From<rsa::errors::Error> for ErrorKind { | |||
|         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}, | ||||
|     error::{Error, ErrorKind}, | ||||
|     jobs::{process_listeners::Listeners, record_last_online::RecordLastOnline}, | ||||
|     requests::Requests, | ||||
|     requests::{Requests, Spawner}, | ||||
| }; | ||||
| use background_jobs::{ | ||||
|     memory_storage::{ActixTimer, Storage}, | ||||
|  | @ -44,6 +44,7 @@ pub(crate) fn create_workers( | |||
|     actors: ActorCache, | ||||
|     media: MediaCache, | ||||
|     config: Config, | ||||
|     spawner: Spawner, | ||||
| ) -> JobServer { | ||||
|     let deliver_concurrency = config.deliver_concurrency(); | ||||
| 
 | ||||
|  | @ -54,6 +55,7 @@ pub(crate) fn create_workers( | |||
|             JobServer::new(queue_handle), | ||||
|             media.clone(), | ||||
|             config.clone(), | ||||
|             spawner.clone(), | ||||
|         ) | ||||
|     }) | ||||
|     .register::<Deliver>() | ||||
|  | @ -110,9 +112,10 @@ impl JobState { | |||
|         job_server: JobServer, | ||||
|         media: MediaCache, | ||||
|         config: Config, | ||||
|         spawner: Spawner, | ||||
|     ) -> Self { | ||||
|         JobState { | ||||
|             requests: state.requests(&config), | ||||
|             requests: state.requests(&config, spawner), | ||||
|             node_cache: state.node_cache(), | ||||
|             actors, | ||||
|             config, | ||||
|  |  | |||
							
								
								
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							|  | @ -33,6 +33,8 @@ mod requests; | |||
| mod routes; | ||||
| mod telegram; | ||||
| 
 | ||||
| use crate::requests::Spawner; | ||||
| 
 | ||||
| use self::{ | ||||
|     args::Args, | ||||
|     config::Config, | ||||
|  | @ -257,12 +259,19 @@ async fn do_server_main( | |||
| 
 | ||||
|     let keys = config.open_keys()?; | ||||
| 
 | ||||
|     let spawner = Spawner::build()?; | ||||
| 
 | ||||
|     let bind_address = config.bind_address(); | ||||
|     let server = HttpServer::new(move || { | ||||
|         let requests = state.requests(&config); | ||||
|         let requests = state.requests(&config, spawner.clone()); | ||||
| 
 | ||||
|         let job_server = | ||||
|             create_workers(state.clone(), actors.clone(), media.clone(), config.clone()); | ||||
|         let job_server = create_workers( | ||||
|             state.clone(), | ||||
|             actors.clone(), | ||||
|             media.clone(), | ||||
|             config.clone(), | ||||
|             spawner.clone(), | ||||
|         ); | ||||
| 
 | ||||
|         let app = App::new() | ||||
|             .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 base64::{engine::general_purpose::STANDARD, Engine}; | ||||
| use dashmap::DashMap; | ||||
| use http_signature_normalization_actix::prelude::*; | ||||
| use http_signature_normalization_actix::{prelude::*, Canceled, Spawn}; | ||||
| use rand::thread_rng; | ||||
| use rsa::{ | ||||
|     pkcs1v15::SigningKey, | ||||
|  | @ -16,7 +16,12 @@ use rsa::{ | |||
|     RsaPrivateKey, | ||||
| }; | ||||
| use std::{ | ||||
|     sync::Arc, | ||||
|     panic::AssertUnwindSafe, | ||||
|     sync::{ | ||||
|         atomic::{AtomicBool, Ordering}, | ||||
|         Arc, | ||||
|     }, | ||||
|     thread::JoinHandle, | ||||
|     time::{Duration, SystemTime}, | ||||
| }; | ||||
| use tracing_awc::Tracing; | ||||
|  | @ -145,7 +150,7 @@ pub(crate) struct Requests { | |||
|     key_id: String, | ||||
|     user_agent: String, | ||||
|     private_key: RsaPrivateKey, | ||||
|     config: Config, | ||||
|     config: Config<Spawner>, | ||||
|     breakers: Breakers, | ||||
|     last_online: Arc<LastOnline>, | ||||
| } | ||||
|  | @ -192,6 +197,7 @@ impl Requests { | |||
|         last_online: Arc<LastOnline>, | ||||
|         pool_size: usize, | ||||
|         timeout_seconds: u64, | ||||
|         spawner: Spawner, | ||||
|     ) -> Self { | ||||
|         Requests { | ||||
|             pool_size, | ||||
|  | @ -199,7 +205,7 @@ impl Requests { | |||
|             key_id, | ||||
|             user_agent, | ||||
|             private_key, | ||||
|             config: Config::default().mastodon_compat(), | ||||
|             config: Config::new().mastodon_compat().spawner(spawner), | ||||
|             breakers, | ||||
|             last_online, | ||||
|         } | ||||
|  | @ -415,3 +421,109 @@ impl Signer { | |||
|         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 New Issue
	
	Block a user
	 asonix
						asonix