affaan-m/ECC · error · anyhow::Error

Unexpected EOF while reading HTTP request body

Error message

Unexpected EOF while reading HTTP request body

What it means

Thrown while reading the HTTP request body: the Content-Length header declared N bytes, the server has read fewer than N so far, and stream.read() returned 0 (EOF) before the body was complete. The body loop cannot satisfy Content-Length so it bails rather than return a truncated request.

Source

Thrown at ecc2/src/main.rs:4244

    let mut headers = BTreeMap::new();
    for line in lines {
        if line.is_empty() {
            break;
        }
        if let Some((key, value)) = line.split_once(':') {
            headers.insert(key.trim().to_ascii_lowercase(), value.trim().to_string());
        }
    }

    let content_length = headers
        .get("content-length")
        .and_then(|value| value.parse::<usize>().ok())
        .unwrap_or(0);
    let mut body = buffer[header_end..].to_vec();
    while body.len() < content_length {
        let read = stream.read(&mut temp)?;
        if read == 0 {
            anyhow::bail!("Unexpected EOF while reading HTTP request body");
        }
        body.extend_from_slice(&temp[..read]);
    }
    body.truncate(content_length);

    Ok((method, path, headers, body))
}

fn write_http_response(
    stream: &mut TcpStream,
    status: u16,
    content_type: &str,
    body: &str,
) -> Result<()> {
    let status_text = match status {
        200 => "OK",
        202 => "Accepted",
        400 => "Bad Request",

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Confirm the client actually sends the full number of bytes declared in Content-Length (compare with tcpdump or client-side logging).
  2. If the body legitimately may be absent, have the client omit Content-Length or send Content-Length: 0 for empty bodies.
  3. Raise the read/idle timeout on any reverse proxy or load balancer in front of the listener.
  4. If the client uses chunked Transfer-Encoding, note this parser only supports Content-Length; switch the client to Content-Length or add chunked-decoding support.
  5. Catch the error at the accept loop and respond with HTTP 400 so one bad upload does not kill the server.

Example fix

// caller: do not propagate body-read EOFs as server failures
match read_http_request(&mut stream) {
    Ok(req) => handle(req),
    Err(e) => {
        let msg = format!("{e:#}");
        let code = if msg.contains("EOF while reading HTTP request body") { 400 } else { 500 };
        let _ = write_http_response(&mut stream, code, "text/plain", "request read error");
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Client side: never declare a Content-Length you will not fulfill.
// Validate before sending:
let body = serialize_body();
assert_eq!(body.len(), declared_content_length, "content-length mismatch");

Try / catch

match read_http_request(&mut stream) {
    Ok(req) => handle(req),
    Err(e) if e.to_string().contains("EOF while reading HTTP request body") => {
        let _ = write_http_response(&mut stream, 400, "text/plain", "incomplete body");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A client sends a valid header with 'Content-Length: 100' but closes the connection after only 40 body bytes. Also caused by a reverse proxy that times out mid-upload, a client crash, an MTU/network drop, or a misconfigured proxy that forwards Content-Length but strips or truncates the body. Sending chunked Transfer-Encoding to this parser (which only supports Content-Length) and closing early also surfaces here.

Common situations: Flaky mobile networks dropping large POSTs; an upstream proxy with an aggressive read timeout; a client that sends Content-Length then streams slowly and gets killed; curl interrupted mid-upload (Ctrl-C).

Related errors


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