{"record":{"id":"e17c1f0affd392ae","repo":"affaan-m/ECC","slug":"unexpected-eof-while-reading-http-request","errorCode":null,"errorMessage":"Unexpected EOF while reading HTTP request","messagePattern":"Unexpected EOF while reading HTTP request","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"ecc2/src/main.rs","lineNumber":4198,"sourceCode":"        }\n        _ => write_http_response(\n            stream,\n            404,\n            \"application/json\",\n            &serde_json::json!({\"error\": \"not found\"}).to_string(),\n        ),\n    }\n}\n\nfn read_http_request(\n    stream: &mut TcpStream,\n) -> Result<(String, String, BTreeMap<String, String>, Vec<u8>)> {\n    let mut buffer = Vec::new();\n    let mut temp = [0_u8; 1024];\n    let header_end = loop {\n        let read = stream.read(&mut temp)?;\n        if read == 0 {\n            anyhow::bail!(\"Unexpected EOF while reading HTTP request\");\n        }\n        buffer.extend_from_slice(&temp[..read]);\n        if let Some(index) = buffer.windows(4).position(|window| window == b\"\\r\\n\\r\\n\") {\n            break index + 4;\n        }\n        if buffer.len() > 64 * 1024 {\n            anyhow::bail!(\"HTTP request headers too large\");\n        }\n    };\n\n    let header_text = String::from_utf8(buffer[..header_end].to_vec())\n        .context(\"HTTP request headers were not valid UTF-8\")?;\n    let mut lines = header_text.split(\"\\r\\n\");\n    let request_line = lines\n        .next()\n        .filter(|line| !line.trim().is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"Missing HTTP request line\"))?;\n    let mut request_parts = request_line.split_whitespace();","sourceCodeStart":4180,"sourceCodeEnd":4216,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/main.rs#L4180-L4216","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: error propagates and may abort the accept loop\nlet req = read_http_request(&mut stream)?;\n\n// after: per-connection guard so a dropped client is logged, not fatal\nmatch read_http_request(&mut stream) {\n    Ok(req) => handle(req),\n    Err(e) if e.to_string().contains(\"Unexpected EOF\") => {\n        tracing::warn!(\"client closed before sending a full request\");\n        return;\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":"// Cheap pre-check: peek the stream with a short timeout to detect an immediate close\nstream.set_read_timeout(Some(Duration::from_millis(500)))?;\n// If read returns 0 immediately, the client closed; do not treat as fatal.","typeGuard":null,"tryCatchPattern":"// Per-connection: do not let a dropped client kill the accept loop\nloop {\n    let (mut stream, _) = listener.accept()?;\n    if let Err(e) = handle_connection(&mut stream) {\n        let msg = e.to_string();\n        if msg.contains(\"Unexpected EOF\") || msg.contains(\"headers too large\") {\n            tracing::warn!(\"dropping malformed connection: {msg}\");\n            continue;\n        } else {\n            tracing::error!(\"connection error: {e}\");\n        }\n    }\n}","preventionTips":["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."],"tags":["network","http","server","tcp","eof"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}