affaan-m/ECC · warning · anyhow::Error
Unexpected EOF while reading HTTP request
Error message
Unexpected EOF while reading HTTP request
What it means
Raised by `read_http_request` when `stream.read(&mut temp)` returns `0` before the `\r\n\r\n` header terminator has been observed. A zero-length read means the peer closed (or half-closed) the connection without finishing the request. The helper is part of ECC's built-in HTTP listener (likely the local comms/control surface), so this surfaces from inbound HTTP handling, not outbound requests.
Source
Thrown at ecc2/src/main.rs:4198
}
_ => write_http_response(
stream,
404,
"application/json",
&serde_json::json!({"error": "not found"}).to_string(),
),
}
}
fn read_http_request(
stream: &mut TcpStream,
) -> Result<(String, String, BTreeMap<String, String>, Vec<u8>)> {
let mut buffer = Vec::new();
let mut temp = [0_u8; 1024];
let header_end = loop {
let read = stream.read(&mut temp)?;
if read == 0 {
anyhow::bail!("Unexpected EOF while reading HTTP request");
}
buffer.extend_from_slice(&temp[..read]);
if let Some(index) = buffer.windows(4).position(|window| window == b"\r\n\r\n") {
break index + 4;
}
if buffer.len() > 64 * 1024 {
anyhow::bail!("HTTP request headers too large");
}
};
let header_text = String::from_utf8(buffer[..header_end].to_vec())
.context("HTTP request headers were not valid UTF-8")?;
let mut lines = header_text.split("\r\n");
let request_line = lines
.next()
.filter(|line| !line.trim().is_empty())
.ok_or_else(|| anyhow::anyhow!("Missing HTTP request line"))?;
let mut request_parts = request_line.split_whitespace();View on GitHub (pinned to 01e15490f0)
Solutions
- Treat as transient: log and continue (the listener loop should accept the next connection).
- If caused by health checks, point the checker at a dedicated endpoint and configure it to send a full request.
- Raise listener robustness by catching this error per-connection so one bad client does not kill the server.
Example fix
// before: error propagates and may abort the accept loop
let req = read_http_request(&mut stream)?;
// after: per-connection guard so a dropped client is logged, not fatal
match read_http_request(&mut stream) {
Ok(req) => handle(req),
Err(e) if e.to_string().contains("Unexpected EOF") => {
tracing::warn!("client closed before sending a full request");
return;
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
// Cheap pre-check: peek the stream with a short timeout to detect an immediate close stream.set_read_timeout(Some(Duration::from_millis(500)))?; // If read returns 0 immediately, the client closed; do not treat as fatal.
Try / catch
// Per-connection: do not let a dropped client kill the accept loop
loop {
let (mut stream, _) = listener.accept()?;
if let Err(e) = handle_connection(&mut stream) {
let msg = e.to_string();
if msg.contains("Unexpected EOF") || msg.contains("headers too large") {
tracing::warn!("dropping malformed connection: {msg}");
continue;
} else {
tracing::error!("connection error: {e}");
}
}
} Prevention
- Always wrap per-connection handling so one bad client does not crash the listener.
- Set reasonable read timeouts to surface half-open connections promptly.
- Log these as warnings, not errors — they are routine on any public-ish port.
When it happens
Trigger: A client opens a TCP connection to the ECC HTTP port and closes it without sending a complete request (port scan, health probe, cancelled curl). A reverse proxy/keepalive connection that times out mid-request. A malformed client that sends headers slowly and disconnects. Telnet/manual probing that exits before finishing.
Common situations: Load balancer health checks that open and immediately close. Browsers issuing preflight/cancelled requests. Network instability or aggressive NAT timeouts dropping idle keepalive sockets. Security scanners probing the port.
Related errors
- open failed (HTTP ${res.statusCode})
- HTTP request headers too large
- HTTP ${res.status}
- plan-canvas server did not become healthy on port ${port}; c
- fetch %s: %w
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/e17c1f0affd392ae.
Report an issue: GitHub.