actix-web icon indicating copy to clipboard operation
actix-web copied to clipboard

POST request with no body trouble

Open smokingplaya opened this issue 1 year ago • 1 comments

Looking like this issue

Expected Behavior

The request should be processed by actix_web and output something

Current Behavior

If I run a POST request with an empty body, actix_web starts outputting the following:

[2024-11-16T14:23:01Z DEBUG actix_http::h1::decoder] no Content-Length specified for HTTP/1.0 POST request
[2024-11-16T14:23:01Z ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided

Possible Solution

i have no ideas

Steps to Reproduce (for bugs)

  1. Make a POST endpoint via actix-web
  2. Send POST request to that endpoint with empty body
  3. Profit

Your Environment

  • Rust Version (I.e, output of rustc -V): 1.81.0 (eeb90cda1 2024-09-04)
  • Actix Web Version: 4.9.0

smokingplaya avatar Nov 16 '24 14:11 smokingplaya

It turns out, this is correct behavior.

[2024-11-16T14:23:01Z DEBUG actix_http::h1::decoder] no Content-Length specified for HTTP/1.0 POST request

From HTTP1.0 [specification](

A valid Content-Length is required on all HTTP/1.0 POST requests. An HTTP/1.0 server should respond with a 400 (bad request) message if it cannot determine the length of the request message's content.

I actually even written tests, to verify that actix actually behaves as it should.

// lib.rs
use std::net::TcpListener;

use actix_web::{rt::spawn, App, HttpServer, Responder};

#[actix_web::post("/")]
async fn index() -> impl Responder {
    "Hello world!"
}

pub fn spawn_test_server() -> u16 {
    let listener = TcpListener::bind("0.0.0.0:0").expect("Failed to bind to local address");
    let addr = listener.local_addr().unwrap();
    let srv = HttpServer::new(|| App::new().service(index))
        .listen(listener)
        .unwrap()
        .run();
    spawn(srv);
    addr.port()
}

#[cfg(test)]
mod tests {
    use reqwest::{Client, StatusCode, Version};

    use super::*;

    // https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length
    // > A user agent SHOULD send Content-Length in a request when the method defines a
    // > meaning for enclosed content and it is not sending Transfer-Encoding
    // MY NOTE: SHOULD not MUST
    #[actix_web::test]
    async fn http_11_post_without_content_length_is_ok() {
        let port = spawn_test_server();
        let resp = Client::new()
            .post(format!("http://localhost:{port}"))
            .version(Version::HTTP_11)
            .send()
            .await
            .unwrap();
        assert_eq!(resp.status(), StatusCode::OK);
    }

    // https://www.w3.org/Protocols/HTTP/1.0/spec.html
    // > A valid Content-Length is required on all HTTP/1.0 POST requests.
    // MY NOTE: this document doesn't follow rfc2119, so required is equal to MUST
    #[actix_web::test]
    async fn http_10_post_with_content_length_is_ok() {
        let port = spawn_test_server();
        let resp = Client::new()
            .post(format!("http://localhost:{port}"))
            .version(Version::HTTP_10)
            .header("Content-Length", 0)
            .send()
            .await
            .unwrap();
        assert_eq!(resp.status(), StatusCode::OK);
    }

    // > An HTTP/1.0 server should respond with a 400 (bad request) message if it
    // > cannot determine the length of the request message's content.
    #[actix_web::test]
    async fn http_10_post_without_content_length_is_bad_request() {
        let port = spawn_test_server();
        let resp = Client::new()
            .post(format!("http://localhost:{port}"))
            .version(Version::HTTP_10)
            .send()
            .await
            .unwrap();
        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    }
}
# Cargo.toml
[dependencies]
actix-web = "4.9.0"
reqwest = { version = "0.12.12" }

fraillt avatar Feb 06 '25 10:02 fraillt