Commit 5153eebc authored by Sergey Lyubka's avatar Sergey Lyubka

mg_read() does not block on content_len=0. mg_read() reads until socket is...

mg_read() does not block on content_len=0. mg_read() reads until socket is closed if content-length is not provided
parent 70154f6c
...@@ -734,10 +734,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) { ...@@ -734,10 +734,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
int n, buffered_len, nread = 0; int n, buffered_len, nread = 0;
int64_t left; int64_t left;
// If Content-Length is not set, read until socket is closed
if (conn->content_len <= 0) { if (conn->content_len <= 0) {
conn->content_len = INT64_MAX; return 0;
conn->must_close = 1;
} }
// conn->buf body // conn->buf body
...@@ -1790,7 +1788,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, ...@@ -1790,7 +1788,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
expect = mg_get_header(conn, "Expect"); expect = mg_get_header(conn, "Expect");
assert(fp != NULL); assert(fp != NULL);
if (conn->content_len == -1) { if (conn->content_len == INT64_MAX) {
send_http_error(conn, 411, "Length Required", "%s", ""); send_http_error(conn, 411, "Length Required", "%s", "");
} else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
send_http_error(conn, 417, "Expectation Failed", "%s", ""); 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) { ...@@ -3644,14 +3642,12 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
&conn->request_info) <= 0) { &conn->request_info) <= 0) {
snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf); snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
} else { } 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) { if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
conn->content_len = strtoll(cl, NULL, 10); 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); conn->birth_time = time(NULL);
} }
......
...@@ -2045,10 +2045,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) { ...@@ -2045,10 +2045,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
int n, buffered_len, nread = 0; int n, buffered_len, nread = 0;
int64_t left; int64_t left;
// If Content-Length is not set, read until socket is closed
if (conn->content_len <= 0) { if (conn->content_len <= 0) {
conn->content_len = INT64_MAX; return 0;
conn->must_close = 1;
} }
// conn->buf body // conn->buf body
...@@ -3101,7 +3099,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, ...@@ -3101,7 +3099,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
expect = mg_get_header(conn, "Expect"); expect = mg_get_header(conn, "Expect");
assert(fp != NULL); assert(fp != NULL);
if (conn->content_len == -1) { if (conn->content_len == INT64_MAX) {
send_http_error(conn, 411, "Length Required", "%s", ""); send_http_error(conn, 411, "Length Required", "%s", "");
} else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
send_http_error(conn, 417, "Expectation Failed", "%s", ""); 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) { ...@@ -4955,14 +4953,12 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
&conn->request_info) <= 0) { &conn->request_info) <= 0) {
snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf); snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
} else { } 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) { if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
conn->content_len = strtoll(cl, NULL, 10); 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); conn->birth_time = time(NULL);
} }
......
...@@ -180,7 +180,8 @@ o("GET /dir%20with%20spaces/桌面/ HTTP/1.0\r\n\r\n", 'куку!', ...@@ -180,7 +180,8 @@ o("GET /dir%20with%20spaces/桌面/ HTTP/1.0\r\n\r\n", 'куку!',
'Non-ascii chars in path'); 'Non-ascii chars in path');
o("GET /hello.txt HTTP/1.1\nConnection: close\nRange: bytes=3-50\r\n\r\n", 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'); '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', 'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
'Request pipelining', 2); 'Request pipelining', 2);
......
...@@ -220,6 +220,15 @@ static int event_handler(struct mg_event *event) { ...@@ -220,6 +220,15 @@ static int event_handler(struct mg_event *event) {
return 1; 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")) { if (!strcmp(ri->uri, "/upload")) {
test_upload(event->conn, "lua_5.2.1.h", "./f1.txt"); test_upload(event->conn, "lua_5.2.1.h", "./f1.txt");
test_upload(event->conn, "lsqlite3.c", "./f2.txt"); test_upload(event->conn, "lsqlite3.c", "./f2.txt");
...@@ -281,6 +290,16 @@ static void test_mg_download(void) { ...@@ -281,6 +290,16 @@ static void test_mg_download(void) {
"GET / HTTP/1.0\r\n\r\n")) != NULL); "GET / HTTP/1.0\r\n\r\n")) != NULL);
mg_close_connection(conn); 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 // Fetch main.c, should succeed
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /main.c HTTP/1.0\r\n\r\n")) != NULL); "GET /main.c HTTP/1.0\r\n\r\n")) != NULL);
...@@ -333,27 +352,16 @@ static void test_mg_upload(void) { ...@@ -333,27 +352,16 @@ static void test_mg_upload(void) {
ASSERT((file2_data = read_file("lsqlite3.c", &file2_len)) != NULL); ASSERT((file2_data = read_file("lsqlite3.c", &file2_len)) != NULL);
post_data = NULL; post_data = NULL;
post_data_len = alloc_printf(&post_data, 0, post_data_len = alloc_printf(&post_data, 0,
// First file // First file
"--%s\r\n" "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; "
"Content-Disposition: form-data; " "filename=\"%s\"\r\n\r\n" "%.*s\r\n"
"name=\"file\"; " // Second file
"filename=\"%s\"\r\n\r\n" "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; "
"%.*s\r\n" "filename=\"%s\"\r\n\r\n" "%.*s\r\n"
// Final boundary
// Second file "--%s--\r\n",
"--%s\r\n" boundary, "f1.txt", file_len, file_data, boundary, "f2.txt",
"Content-Disposition: form-data; " file2_len, file2_data, boundary);
"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(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf), ebuf, sizeof(ebuf),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment