//! Regression: SSE connection-pool leak via missing `EventSource.close()`. //! //! Every HTML template that opens `new EventSource('/events')` must //! also close it on `pagehide`. Without the close, Chrome keeps the //! socket in a "draining" state after navigation; after ~6 rapid //! back-and-forth navigations all 6 per-origin pool slots are draining //! and new requests stall with `blocked` timings of 15-30 seconds //! (confirmed by HAR capture on 2026-04-20). use std::fs; use std::path::Path; #[test] fn every_event_source_template_closes_it_on_pagehide() { let template_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("templates"); let entries = fs::read_dir(&template_dir).expect("templates directory readable"); let mut checked = 0; let mut missing: Vec = Vec::new(); for entry in entries { let path = entry.unwrap().path(); if path.extension().and_then(|s| s.to_str()) != Some("html") { continue; } let content = fs::read_to_string(&path).unwrap(); if !content.contains("new EventSource('/events')") { continue; } checked += 1; let has_pagehide = content.contains("pagehide"); let has_close = content.contains(".close()"); if !(has_pagehide && has_close) { missing.push(path.file_name().unwrap().to_string_lossy().into_owned()); } } assert!( checked >= 1, "test itself is stale: no template contains EventSource('/events')", ); assert!( missing.is_empty(), "templates open EventSource but lack `pagehide` + `.close()` cleanup: {missing:?} \ — this leaks Chrome socket-pool slots and stalls navigation after ~6 cycles", ); }