tursodatabase/turso · error · anyhow::Error

HTTP request length overflows

Error message

HTTP request length overflows: {content_length}

What it means

TursoSyncServer parses HTTP by hand; request_end computes header_end + 4 + content_length with checked_add because, as the code comment states, the client fully controls Content-Length. This error fires when that sum would overflow usize - i.e. a client sent a Content-Length near usize::MAX (2^64-1 on 64-bit). It is a hardening guard against absurd headers rather than a normal failure.

Solutions

  1. Fix the client to send a Content-Length equal to the actual body byte count
  2. If exposing the server, front it with a proxy that enforces sane body-size limits
  3. When embedding the server, reject requests with Content-Length above a MAX_BODY_BYTES cap before reading the body

Example fix

// before (client): stale or wrapped length
let len = body.capacity(); // wrong: capacity, not len
write!(stream, "Content-Length: {len}\r\n\r\n")?;

// after: derive from the exact body
write!(stream, "Content-Length: {}\r\n\r\n", body.len())?;
stream.write_all(&body)?;
Defensive patterns

Strategy: validation

Validate before calling

// Cap Content-Length before computing the body end:
const MAX_BODY_BYTES: usize = 64 * 1024 * 1024;
fn content_length_acceptable(content_length: usize) -> bool {
    content_length <= MAX_BODY_BYTES
}

Try / catch

let total_expected = match request_end(header_end, content_length) {
    Ok(total) => total,
    Err(err) if err.to_string().starts_with("HTTP request length overflows") => {
        // hostile or broken client: answer 400 and close
        return Err(err);
    }
    Err(err) => return Err(err),
};

Prevention

When it happens

Trigger: handle_connection reads headers containing e.g. 'Content-Length: 18446744073709551615'; parse_content_length parses it into usize and (header_end + 4).checked_add(content_length) returns None in request_end.

Common situations: Port scanners or hostile probes sending overflow-style headers; buggy hand-rolled HTTP clients computing Content-Length with wrapped/negative arithmetic; fuzzers targeting the sync port.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20). Data as JSON: /api/errors/0a693c631eeb9044. Report an issue: GitHub.

Appendix: source

Thrown at cli/sync_server.rs:1265

    let mut page = vec![0u8; PAGE_SIZE];
    if conn.try_wal_watermark_read_page(1, &mut page, Some(max_frame))? {
        Ok(db_size_from_page(&page) as u64)
    } else {
        Ok(0)
    }
}

fn db_size_from_page(page: &[u8]) -> u32 {
    u32::from_be_bytes(page[28..32].try_into().unwrap())
}

/// A client controls Content-Length, so the end of the body has to be
/// computed without trusting it to fit.
fn request_end(header_end: usize, content_length: usize) -> Result<usize> {
    (header_end + 4)
        .checked_add(content_length)
        .ok_or_else(|| anyhow!("HTTP request length overflows: {content_length}"))
}

fn find_header_end(data: &[u8], start: usize) -> Option<usize> {
    (start..data.len().saturating_sub(3)).find(|&i| &data[i..i + 4] == b"\r\n\r\n")
}

fn parse_content_length(headers: &str) -> Option<usize> {
    for line in headers.lines() {
        let lower = line.to_lowercase();
        if lower.starts_with("content-length:") {
            let value = line.split(':').nth(1)?.trim();
            return value.parse().ok();
        }
    }
    None
}

fn parse_http_request(data: &[u8]) -> Result<(String, String, Vec<u8>)> {

View on GitHub (pinned to 492c4a71cd)