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

  1. Treat as transient: log and continue (the listener loop should accept the next connection).
  2. If caused by health checks, point the checker at a dedicated endpoint and configure it to send a full request.
  3. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/e17c1f0affd392ae. Report an issue: GitHub.