{"record":{"id":"fb8b44a6a51291ee","repo":"tursodatabase/turso","slug":"empty-request","errorCode":null,"errorMessage":"Empty request","messagePattern":"Empty request","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"cli/sync_server.rs","lineNumber":1175,"sourceCode":"fn parse_content_length(headers: &str) -> Option<usize> {\n    for line in headers.lines() {\n        let lower = line.to_lowercase();\n        if lower.starts_with(\"content-length:\") {\n            let value = line.split(':').nth(1)?.trim();\n            return value.parse().ok();\n        }\n    }\n    None\n}\n\nfn parse_http_request(data: &[u8]) -> Result<(String, String, Vec<u8>)> {\n    let header_end = find_header_end(data, 0).ok_or_else(|| anyhow!(\"Invalid HTTP request\"))?;\n    let headers = String::from_utf8_lossy(&data[..header_end]);\n\n    let first_line = headers\n        .lines()\n        .next()\n        .ok_or_else(|| anyhow!(\"Empty request\"))?;\n    let parts: Vec<&str> = first_line.split_whitespace().collect();\n\n    if parts.len() < 2 {\n        return Err(anyhow!(\"Invalid request line\"));\n    }\n\n    let method = parts[0].to_string();\n    let path = parts[1].to_string();\n    let body = data[header_end + 4..].to_vec();\n\n    Ok((method, path, body))\n}\n\nfn format_http_response(resp: &HttpResponse) -> Vec<u8> {\n    let status_text = match resp.status {\n        200 => \"OK\",\n        204 => \"No Content\",\n        404 => \"Not Found\",","sourceCodeStart":1157,"sourceCodeEnd":1193,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/cli/sync_server.rs#L1157-L1193","documentation":"The header block was found (data contains \\r\\n\\r\\n) but its first line is missing — in practice the data begins immediately with the terminator, i.e. leading blank lines before the request line, making lines().next() return None. RFC 7230 says servers SHOULD ignore at least one empty line before the request-line; this minimal parser does not, so it rejects. Keep-alive or telnet-style newline pings produce exactly this shape.","triggerScenarios":"parse_http_request receives data starting with \\r\\n\\r\\n or whose header section before the terminator is only empty lines — a client that flushes a stray CRLF before the request line, or manual telnet testing that presses Enter first.","commonSituations":"Manual telnet/netcat testing with a leading blank line; HTTP clients that emit a leftover CRLF from a previous keep-alive exchange; load balancers or wrappers injecting empty lines; scripts writing '\\r\\nGET ...'.","solutions":["Client-side: send the request line first, with no leading blank lines.","Server-side: skip leading empty lines before parsing (RFC 7230 recommends tolerating at least one).","Check for keep-alive connection reuse bugs that leak a stray CRLF between requests."],"exampleFix":"// before (server)\nlet first_line = headers.lines().next().ok_or_else(|| anyhow!(\"Empty request\"))?;\n\n// after (server): tolerate leading empty lines per RFC 7230\nlet first_line = headers\n    .lines()\n    .find(|line| !line.trim().is_empty())\n    .ok_or_else(|| anyhow!(\"Empty request\"))?;","handlingStrategy":"validation","validationCode":"fn has_request_line(data: &[u8]) -> bool {\n    let Some(header_end) = (0..data.len().saturating_sub(3))\n        .find(|&i| &data[i..i + 4] == b\"\\r\\n\\r\\n\")\n    else { return false; };\n    let headers = String::from_utf8_lossy(&data[..header_end]);\n    headers.lines().any(|line| !line.trim().is_empty())\n}\n// before parsing:\nanyhow::ensure!(has_request_line(&request_data), \"no request line before terminator\");","typeGuard":"fn has_request_line(data: &[u8]) -> bool {\n    let Some(header_end) = (0..data.len().saturating_sub(3))\n        .find(|&i| &data[i..i + 4] == b\"\\r\\n\\r\\n\")\n    else { return false; };\n    String::from_utf8_lossy(&data[..header_end])\n        .lines()\n        .any(|line| !line.trim().is_empty())\n}","tryCatchPattern":"match parse_http_request(&request_data) {\n    Ok((method, path, body)) => { /* dispatch */ }\n    Err(err) if err.to_string() == \"Empty request\" => {\n        // only blank lines before the terminator: reply 400 or tolerate per RFC 7230\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Clients: never flush leading CRLFs before the request line.","Servers: skip at least one leading empty line before parsing, as RFC 7230 suggests.","Audit keep-alive client code for stray newlines leaked between requests.","For manual tests, type the request line immediately after connecting."],"tags":["http","request-parsing","sync-server","protocol"],"backgroundTag":"malformed-http-request","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}