//! Live-server checks ported from main's tests/live-server.test.mjs: //! /source symlink confinement (d008dd98, #618), page-controlled poller //! field stripping (bda7411a, #488), and the /live.js project-ignores //! prelude (5330fa35 + 152d6940, #639). #![cfg(unix)] use std::io::{Read, Write}; use std::net::TcpStream; use std::path::Path; fn http(port: u16, method: &str, target: &str, body: Option<&str>) -> (u16, String) { let mut s = TcpStream::connect(("127.0.0.1", port)).expect("connect"); s.set_read_timeout(Some(std::time::Duration::from_secs(10))).unwrap(); let body = body.unwrap_or(""); let req = format!( "{} {} HTTP/1.1\r\nHost: 127.0.0.1:{}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", method, target, port, body.len(), body ); s.write_all(req.as_bytes()).unwrap(); let mut out = Vec::new(); let _ = s.read_to_end(&mut out); let text = String::from_utf8_lossy(&out).into_owned(); let status: u16 = text.split_whitespace().nth(1).and_then(|c| c.parse().ok()).unwrap_or(0); let body = text.split_once("\r\n\r\n").map(|(_, b)| b.to_string()).unwrap_or_default(); // Dechunk if needed (tiny bodies: concatenate chunk payload lines). let body = if text.to_ascii_lowercase().contains("transfer-encoding: chunked") { let mut rest = body.as_str(); let mut assembled = String::new(); while let Some((size_line, after)) = rest.split_once("\r\n") { let size = usize::from_str_radix(size_line.trim(), 16).unwrap_or(0); if size == 0 { break; } assembled.push_str(&after[..size.min(after.len())]); rest = after.get(size + 2..).unwrap_or(""); } assembled } else { body }; (status, body) } fn wait_for(p: &Path, secs: u64) -> bool { let deadline = std::time::Instant::now() + std::time::Duration::from_secs(secs); while !p.exists() && std::time::Instant::now() < deadline { std::thread::sleep(std::time::Duration::from_millis(50)); } p.exists() } fn free_port() -> u16 { let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let port = l.local_addr().unwrap().port(); drop(l); port } #[test] fn live_server_source_ignores_and_poller_fields() { let dir = std::env::temp_dir().join(format!("impeccable-live-sec-{}", std::process::id())); let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(dir.join("prototype")).unwrap(); let dir = std::fs::canonicalize(&dir).unwrap(); std::fs::write(dir.join("prototype/index.html"), "