From b63458816eeca5a00fb43206c2321f902305263b Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 01:21:21 +0000 Subject: [PATCH] =?UTF-8?q?metrics:=20set=20read/write=20timeouts=20on=20e?= =?UTF-8?q?ach=20scrape=20connection=20=E2=80=94=20the=20single-threaded?= =?UTF-8?q?=20accept=20loop=20did=20an=20untimed=20read,=20so=20one=20clie?= =?UTF-8?q?nt=20that=20connected=20and=20never=20sent=20blocked=20every=20?= =?UTF-8?q?future=20scrape=20(slowloris)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/metrics.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/metrics.rs b/src/modules/metrics.rs index f953a0e..db3ee67 100644 --- a/src/modules/metrics.rs +++ b/src/modules/metrics.rs @@ -75,6 +75,11 @@ pub fn maybe_start(cfg: &Config) { fn serve(listener: TcpListener, metrics: Arc) { for stream in listener.incoming() { let Ok(mut s) = stream else { continue }; + // Bound how long one (possibly slow/hostile) client can hold this + // single-threaded scrape loop — without a timeout a client that connects + // and never sends would block every future scrape (slowloris). + let _ = s.set_read_timeout(Some(std::time::Duration::from_secs(5))); + let _ = s.set_write_timeout(Some(std::time::Duration::from_secs(5))); // read (and ignore) the request head, then reply — this is a scrape, no routing let mut buf = [0u8; 1024]; let _ = s.read(&mut buf);