commit - e93f672b2f9c7ce3c8deae5ddbadcd21ba919e33
commit + 2f1f611bf7b48fc9dd5568ccca17c0c36b997200
blob - c578e5c402a0b0258a157edccf97b8984e5ba8df
blob + 3270c1b315d1af44e6355101f20c240b0f670d50
--- src/daemon.rs
+++ src/daemon.rs
use std::io::{BufRead, BufReader, Read, Write};
use std::path::Path;
+use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::time::{Duration, Instant};
line.clear();
// Limit read to MAX_LINE_SIZE to prevent a client from
// exhausting memory with an unbounded request line.
- let n = (&mut reader).take(MAX_LINE_SIZE as u64).read_line(&mut line)?;
+ let n = (&mut reader)
+ .take(MAX_LINE_SIZE as u64)
+ .read_line(&mut line)?;
if n == 0 {
break;
}
// ── HTTP server ─────────────────────────────────────
+/// Maximum concurrent HTTP handler threads.
+const MAX_HTTP_THREADS: usize = 8;
+
/// Minimal HTTP server. Serves pastes at /<hash> or
/// /<hash>/<enckey>. Queries the daemon via Unix socket
/// so it can access DHT-replicated data too.
log::info!("http: listening on {addr}");
+ let active = Arc::new(std::sync::atomic::AtomicUsize::new(0));
+ let sock_owned = sock_path.to_path_buf();
+
while !shutdown.load(Ordering::Relaxed) {
match listener.accept() {
Ok((stream, _)) => {
+ if active.load(Ordering::Relaxed) >= MAX_HTTP_THREADS {
+ log::warn!("http: max connections reached, rejecting");
+ let mut s = stream;
+ let _ = s.write_all(
+ b"HTTP/1.1 503 Service Unavailable\r\n\
+ Connection: close\r\n\r\n",
+ );
+ continue;
+ }
let store = store.clone();
- handle_http(stream, &store, sock_path);
+ let sock = sock_owned.clone();
+ let counter = Arc::clone(&active);
+ counter.fetch_add(1, Ordering::Relaxed);
+ std::thread::spawn(move || {
+ handle_http(stream, &store, &sock);
+ counter.fetch_sub(1, Ordering::Relaxed);
+ });
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
std::thread::sleep(Duration::from_millis(50));