From 81b61d335f0ecf434a2931f8950ca7b3d502bd8d Mon Sep 17 00:00:00 2001 From: robert Date: Wed, 11 Mar 2026 05:49:01 -0400 Subject: [PATCH] Content-length not required if request body is empty --- mongoose.c | 19 ++++++------------- src/http.c | 19 ++++++------------- test/unit_test.c | 6 ++---- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/mongoose.c b/mongoose.c index a83fa7e6..3d50c3e6 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1999,17 +1999,9 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { // responses. If HTTP response does not have Content-Length set, then // body is read until socket is closed, i.e. body.len is infinite (~0). // - // For HTTP requests though, according to - // http://tools.ietf.org/html/rfc7231#section-8.1.3, - // only POST and PUT methods have defined body semantics. - // Therefore, if Content-Length is not specified and methods are - // not one of PUT or POST, set body length to 0. - // - // So, if it is HTTP request, and Content-Length is not set, - // and method is not (PUT or POST) then reset body length to zero. - if (hm->body.len == (size_t) ~0 && !is_response && - mg_strcasecmp(hm->method, mg_str("PUT")) != 0 && - mg_strcasecmp(hm->method, mg_str("POST")) != 0) { + // For HTTP requests though, if Content-Length is not specified + // set body length to 0. + if (hm->body.len == (size_t) ~0 && !is_response) { hm->body.len = 0; hm->message.len = (size_t) req_len; } @@ -2740,9 +2732,10 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 || mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) { // POST and PUT should include an entity body. Therefore, they should - // contain a Content-length header. Other requests can also contain a + // contain a Content-length header (unless the body length is 0, in + // which case it can be omitted). Other requests can also contain a // body, but their content has no defined semantics (RFC 7231) - require_content_len = true; + if (hm.body.len != 0) require_content_len = true; ofs += (size_t) n; // this request has been processed } else if (is_response) { // HTTP spec 7.2 Entity body: All other responses must include a body diff --git a/src/http.c b/src/http.c index c0360dfb..d52ac85f 100644 --- a/src/http.c +++ b/src/http.c @@ -317,17 +317,9 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { // responses. If HTTP response does not have Content-Length set, then // body is read until socket is closed, i.e. body.len is infinite (~0). // - // For HTTP requests though, according to - // http://tools.ietf.org/html/rfc7231#section-8.1.3, - // only POST and PUT methods have defined body semantics. - // Therefore, if Content-Length is not specified and methods are - // not one of PUT or POST, set body length to 0. - // - // So, if it is HTTP request, and Content-Length is not set, - // and method is not (PUT or POST) then reset body length to zero. - if (hm->body.len == (size_t) ~0 && !is_response && - mg_strcasecmp(hm->method, mg_str("PUT")) != 0 && - mg_strcasecmp(hm->method, mg_str("POST")) != 0) { + // For HTTP requests though, if Content-Length is not specified + // set body length to 0. + if (hm->body.len == (size_t) ~0 && !is_response) { hm->body.len = 0; hm->message.len = (size_t) req_len; } @@ -1058,9 +1050,10 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 || mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) { // POST and PUT should include an entity body. Therefore, they should - // contain a Content-length header. Other requests can also contain a + // contain a Content-length header (unless the body length is 0, in + // which case it can be omitted). Other requests can also contain a // body, but their content has no defined semantics (RFC 7231) - require_content_len = true; + if (hm.body.len != 0) require_content_len = true; ofs += (size_t) n; // this request has been processed } else if (is_response) { // HTTP spec 7.2 Entity body: All other responses must include a body diff --git a/test/unit_test.c b/test/unit_test.c index 82b8e450..5a7028e8 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1672,14 +1672,12 @@ static void test_http_no_content_length(void) { // 12348 is in TIME_WAIT, use another port mg_mgr_init(&mgr); mg_http_listen(&mgr, url2, f41, (void *) NULL); - ASSERT(fetch(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 411); + ASSERT(fetch(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 200); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 200\r\n\r\n") == 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 100\r\n\r\n") != 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 304\r\n\r\n") != 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 305\r\n\r\n") == 411); ASSERT(fetch(&mgr, buf, url2, post_req) != 411); - // Check it is processed only once (see #2811) - ASSERT(fpr(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 411); mg_mgr_free(&mgr); ASSERT(mgr.conns == NULL); } @@ -1844,7 +1842,7 @@ static void test_http_parse(void) { { const char *s = "POST /x HTTP/1.0\n\n"; ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s)); - ASSERT(req.body.len == (size_t) ~0); + ASSERT(req.body.len == 0); } {