From 5153eebc9b618f739ae0f05ce01676615ebe72a3 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka <valenok@gmail.com> Date: Tue, 1 Oct 2013 17:47:32 +0100 Subject: [PATCH] mg_read() does not block on content_len=0. mg_read() reads until socket is closed if content-length is not provided --- build/src/mongoose.c | 16 ++++++-------- mongoose.c | 16 ++++++-------- test/test.pl | 3 ++- test/unit_test.c | 50 +++++++++++++++++++++++++------------------- 4 files changed, 43 insertions(+), 42 deletions(-) diff --git a/build/src/mongoose.c b/build/src/mongoose.c index 00ca8db14..fcb293617 100644 --- a/build/src/mongoose.c +++ b/build/src/mongoose.c @@ -734,10 +734,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) { int n, buffered_len, nread = 0; int64_t left; - // If Content-Length is not set, read until socket is closed if (conn->content_len <= 0) { - conn->content_len = INT64_MAX; - conn->must_close = 1; + return 0; } // conn->buf body @@ -1790,7 +1788,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, expect = mg_get_header(conn, "Expect"); assert(fp != NULL); - if (conn->content_len == -1) { + if (conn->content_len == INT64_MAX) { send_http_error(conn, 411, "Length Required", "%s", ""); } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { send_http_error(conn, 417, "Expectation Failed", "%s", ""); @@ -3644,14 +3642,12 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) { &conn->request_info) <= 0) { snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf); } else { - // Request is valid + // Request is valid. Set content_len attribute by parsing Content-Length + // If Content-Length is absent, instruct mg_read() to read from the socket + // until socket is closed. + conn->content_len = INT64_MAX; if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) { conn->content_len = strtoll(cl, NULL, 10); - } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") || - !mg_strcasecmp(conn->request_info.request_method, "PUT")) { - conn->content_len = -1; - } else { - conn->content_len = 0; } conn->birth_time = time(NULL); } diff --git a/mongoose.c b/mongoose.c index 8a424ecf0..35ce41a96 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2045,10 +2045,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) { int n, buffered_len, nread = 0; int64_t left; - // If Content-Length is not set, read until socket is closed if (conn->content_len <= 0) { - conn->content_len = INT64_MAX; - conn->must_close = 1; + return 0; } // conn->buf body @@ -3101,7 +3099,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, expect = mg_get_header(conn, "Expect"); assert(fp != NULL); - if (conn->content_len == -1) { + if (conn->content_len == INT64_MAX) { send_http_error(conn, 411, "Length Required", "%s", ""); } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { send_http_error(conn, 417, "Expectation Failed", "%s", ""); @@ -4955,14 +4953,12 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) { &conn->request_info) <= 0) { snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf); } else { - // Request is valid + // Request is valid. Set content_len attribute by parsing Content-Length + // By default, in the absence of Content-Length, instruct mg_read() + // to read from the socket until the socket is closed. + conn->content_len = INT64_MAX; if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) { conn->content_len = strtoll(cl, NULL, 10); - } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") || - !mg_strcasecmp(conn->request_info.request_method, "PUT")) { - conn->content_len = -1; - } else { - conn->content_len = 0; } conn->birth_time = time(NULL); } diff --git a/test/test.pl b/test/test.pl index cdc8bd86c..3f7b5b650 100644 --- a/test/test.pl +++ b/test/test.pl @@ -180,7 +180,8 @@ o("GET /dir%20with%20spaces/æ¡Œé¢/ HTTP/1.0\r\n\r\n", 'куку!', 'Non-ascii chars in path'); o("GET /hello.txt HTTP/1.1\nConnection: close\nRange: bytes=3-50\r\n\r\n", 'Content-Length: 15\s', 'Range past the file end'); -o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n", +o("GET /hello.txt HTTP/1.1\nContent-Length: 0\n\n ". + "GET /hello.txt HTTP/1.0\nContent-Length: 0\n\n", 'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close', 'Request pipelining', 2); diff --git a/test/unit_test.c b/test/unit_test.c index b12b244c6..632c25ce6 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -220,6 +220,15 @@ static int event_handler(struct mg_event *event) { return 1; } + if (!strcmp(ri->uri, "/zerolen")) { + char buf[100]; + mg_printf(event->conn, "%s", + "HTTP/1.0 200 OK\r\nContent-Length: 2\r\n\r\nok"); + printf("[%d]\n", mg_read(event->conn, buf, sizeof(buf))); + ASSERT(mg_read(event->conn, buf, sizeof(buf)) == 0); + return 1; + } + if (!strcmp(ri->uri, "/upload")) { test_upload(event->conn, "lua_5.2.1.h", "./f1.txt"); test_upload(event->conn, "lsqlite3.c", "./f2.txt"); @@ -281,6 +290,16 @@ static void test_mg_download(void) { "GET / HTTP/1.0\r\n\r\n")) != NULL); mg_close_connection(conn); + // POST with "Content-Length: 0", must not block + ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, + ebuf, sizeof(ebuf), "%s", + "POST /zerolen HTTP/1.1\r\n" + "Content-Lengh: 0\r\n\r\n ")) != NULL); + ASSERT((p1 = read_conn(conn, &len1)) != NULL); + ASSERT(len1 = 2); + ASSERT(memcmp(p1, "ok", 2) == 0); + mg_close_connection(conn); + // Fetch main.c, should succeed ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", "GET /main.c HTTP/1.0\r\n\r\n")) != NULL); @@ -333,27 +352,16 @@ static void test_mg_upload(void) { ASSERT((file2_data = read_file("lsqlite3.c", &file2_len)) != NULL); post_data = NULL; post_data_len = alloc_printf(&post_data, 0, - // First file - "--%s\r\n" - "Content-Disposition: form-data; " - "name=\"file\"; " - "filename=\"%s\"\r\n\r\n" - "%.*s\r\n" - - // Second file - "--%s\r\n" - "Content-Disposition: form-data; " - "name=\"file\"; " - "filename=\"%s\"\r\n\r\n" - "%.*s\r\n" - - // Final boundary - "--%s--\r\n", - boundary, "f1.txt", - file_len, file_data, - boundary, "f2.txt", - file2_len, file2_data, - boundary); + // First file + "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; " + "filename=\"%s\"\r\n\r\n" "%.*s\r\n" + // Second file + "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; " + "filename=\"%s\"\r\n\r\n" "%.*s\r\n" + // Final boundary + "--%s--\r\n", + boundary, "f1.txt", file_len, file_data, boundary, "f2.txt", + file2_len, file2_data, boundary); ASSERT(post_data_len > 0); ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, ebuf, sizeof(ebuf), -- GitLab