affaan-m/ECC · warning · anyhow::Error
Unexpected EOF while reading HTTP request
Error message
Unexpected EOF while reading HTTP request
What it means
The built-in HTTP server's request reader loops reading from the client socket until it finds the `\r\n\r\n` header terminator. If `stream.read` returns 0 — the peer closed the connection before sending complete headers — it bails with this error. It is a guard against truncated/malformed requests from misbehaving clients.
Solutions
- Retry the request from the client once the connection is stable.
- Identify clients that probe with connect-then-close and configure them to send a real HTTP request (e.g. `GET /health`).
- If it occurs from your own code, ensure the client writes and flushes the full request before closing the socket.
Example fix
// before (client closes early) let mut s = TcpStream::connect(addr)?; drop(s); // EOF on server // after let mut s = TcpStream::connect(addr)?; write!(s, "GET /health HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")?;
Defensive patterns
Strategy: try-catch
Validate before calling
// Client: ensure the request is fully written before any early return/close
assert!(request.contains("\r\n\r\n"), "request missing header terminator"); Try / catch
// Server caller side
match read_http_request(&mut stream) {
Ok(req) => handle(req),
Err(e) if e.to_string().contains("Unexpected EOF") => {
log::debug!("client closed connection early: {e}"); // non-fatal, drop connection
}
Err(e) => return Err(e),
} Prevention
- Configure health checks to send a real HTTP request rather than connect-and-close probes.
- Retry idempotent requests on transient connection drops.
- Monitor logs: isolated EOFs are noise; a burst indicates a client or network problem.
When it happens
Trigger: A client opens a TCP connection to the local HTTP endpoint and closes it (or the connection drops) before sending the full request headers; a health-check probe that connects and disconnects; a port scanner or half-open connection timing out.
Common situations: Load balancer or monitoring health checks that only test TCP connect; curl aborted with Ctrl-C mid-request; network interruption between the client and the locally spawned server; a client sending a request smaller than the buffer read granularity that stalls and gets reset.
Related errors
- Unexpected EOF while reading HTTP request body
- Control-plane request failed
- download declares an invalid or excessive size
- download exceeds maximum allowed size
- download length does not match declared size
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/e17c1f0affd392ae.
Report an issue: GitHub.
Appendix: 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 8321021c54)