mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-26 22:41:03 +08:00
Merge pull request #2205 from cesanta/arbitrary_content-length
improve negative Content-length handling
This commit is contained in:
commit
1ccc9aa72e
74
mongoose.c
74
mongoose.c
@ -1243,6 +1243,22 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
||||
|
||||
|
||||
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val);
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val) {
|
||||
uint64_t result = 0, max = 1844674407370955160 /* (UINT64_MAX-9)/10 */;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
if (i < str.len && str.ptr[i] == '-') return false;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
if (result > max) return false;
|
||||
result *= 10;
|
||||
result += (unsigned) (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
*val = (size_t) result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Chunk deletion marker is the MSB in the "processed" counter
|
||||
#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
|
||||
|
||||
@ -1382,7 +1398,9 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
|
||||
return i >= src_len && j < dst_len ? (int) j : -1;
|
||||
}
|
||||
|
||||
static bool isok(uint8_t c) { return c == '\n' || c == '\r' || c >= ' '; }
|
||||
static bool isok(uint8_t c) {
|
||||
return c == '\n' || c == '\r' || c >= ' ';
|
||||
}
|
||||
|
||||
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
||||
size_t i;
|
||||
@ -1463,9 +1481,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
|
||||
mg_http_parse_headers(s, end, hm->headers,
|
||||
sizeof(hm->headers) / sizeof(hm->headers[0]));
|
||||
if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
|
||||
int64_t content_len = mg_to64(*cl);
|
||||
if(content_len < 0) return -1;
|
||||
hm->body.len = (size_t) content_len;
|
||||
if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
|
||||
hm->message.len = (size_t) req_len + hm->body.len;
|
||||
}
|
||||
|
||||
@ -1662,18 +1678,18 @@ static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
|
||||
return mg_str("text/plain; charset=utf-8");
|
||||
}
|
||||
|
||||
static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
|
||||
static int getrange(struct mg_str *s, size_t *a, size_t *b) {
|
||||
size_t i, numparsed = 0;
|
||||
// MG_INFO(("%.*s", (int) s->len, s->ptr));
|
||||
for (i = 0; i + 6 < s->len; i++) {
|
||||
if (memcmp(&s->ptr[i], "bytes=", 6) == 0) {
|
||||
struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6);
|
||||
if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
|
||||
*a = mg_to64(p);
|
||||
if (!mg_to_size_t(p, a)) return 0;
|
||||
// MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
|
||||
while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--;
|
||||
if (p.len && p.ptr[0] == '-') p.ptr++, p.len--;
|
||||
*b = mg_to64(p);
|
||||
if (!mg_to_size_t(p, b)) return 0;
|
||||
if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
|
||||
// MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
|
||||
break;
|
||||
@ -1725,12 +1741,12 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
} else {
|
||||
int n, status = 200;
|
||||
char range[100];
|
||||
int64_t r1 = 0, r2 = 0, cl = (int64_t) size;
|
||||
|
||||
size_t r1 = 0, r2 = 0, cl = size;
|
||||
|
||||
// Handle Range header
|
||||
struct mg_str *rh = mg_http_get_header(hm, "Range");
|
||||
range[0] = '\0';
|
||||
if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) {
|
||||
if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) {
|
||||
// If range is specified like "400-", set second limit to content len
|
||||
if (n == 1) r2 = cl - 1;
|
||||
if (r1 > r2 || r2 >= cl) {
|
||||
@ -1742,9 +1758,9 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
status = 206;
|
||||
cl = r2 - r1 + 1;
|
||||
mg_snprintf(range, sizeof(range),
|
||||
"Content-Range: bytes %lld-%lld/%lld\r\n", r1, r1 + cl - 1,
|
||||
(int64_t) size);
|
||||
fs->sk(fd->fd, (size_t) r1);
|
||||
"Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1,
|
||||
(uint64_t) (r1 + cl - 1), (uint64_t) size);
|
||||
fs->sk(fd->fd, r1);
|
||||
}
|
||||
}
|
||||
mg_printf(c,
|
||||
@ -1754,8 +1770,8 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
"Content-Length: %llu\r\n"
|
||||
"%s%s%s\r\n",
|
||||
status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
|
||||
etag, cl, gzip ? "Content-Encoding: gzip\r\n" : "", range,
|
||||
opts->extra_headers ? opts->extra_headers : "");
|
||||
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
||||
range, opts->extra_headers ? opts->extra_headers : "");
|
||||
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
|
||||
c->is_draining = 1;
|
||||
c->is_resp = 0;
|
||||
@ -1766,7 +1782,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
sizeof(size_t) * sizeof(size_t)];
|
||||
c->pfn = static_cb;
|
||||
c->pfn_data = fd;
|
||||
*clp = (size_t) cl;
|
||||
*clp = cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5306,32 +5322,6 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mg_tou64(struct mg_str str) {
|
||||
uint64_t result = 0;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
result *= 10;
|
||||
result += (unsigned) (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t mg_to64(struct mg_str str) {
|
||||
int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
if (result > max) return 0;
|
||||
result *= 10;
|
||||
result += (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
return result * neg;
|
||||
}
|
||||
|
||||
char *mg_remove_double_dots(char *s) {
|
||||
char *saved = s, *p = s;
|
||||
while (*s != '\0') {
|
||||
|
@ -829,8 +829,6 @@ char *mg_hex(const void *buf, size_t len, char *dst);
|
||||
void mg_unhex(const char *buf, size_t len, unsigned char *to);
|
||||
unsigned long mg_unhexn(const char *s, size_t len);
|
||||
int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
|
||||
int64_t mg_to64(struct mg_str str);
|
||||
uint64_t mg_tou64(struct mg_str str);
|
||||
char *mg_remove_double_dots(char *s);
|
||||
|
||||
|
||||
|
50
src/http.c
50
src/http.c
@ -1,7 +1,7 @@
|
||||
#include "http.h"
|
||||
#include "arch.h"
|
||||
#include "base64.h"
|
||||
#include "fmt.h"
|
||||
#include "http.h"
|
||||
#include "json.h"
|
||||
#include "log.h"
|
||||
#include "net.h"
|
||||
@ -11,6 +11,22 @@
|
||||
#include "version.h"
|
||||
#include "ws.h"
|
||||
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val);
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val) {
|
||||
uint64_t result = 0, max = 1844674407370955160 /* (UINT64_MAX-9)/10 */;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
if (i < str.len && str.ptr[i] == '-') return false;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
if (result > max) return false;
|
||||
result *= 10;
|
||||
result += (unsigned) (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
*val = (size_t) result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Chunk deletion marker is the MSB in the "processed" counter
|
||||
#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
|
||||
|
||||
@ -150,7 +166,9 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
|
||||
return i >= src_len && j < dst_len ? (int) j : -1;
|
||||
}
|
||||
|
||||
static bool isok(uint8_t c) { return c == '\n' || c == '\r' || c >= ' '; }
|
||||
static bool isok(uint8_t c) {
|
||||
return c == '\n' || c == '\r' || c >= ' ';
|
||||
}
|
||||
|
||||
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
||||
size_t i;
|
||||
@ -231,9 +249,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
|
||||
mg_http_parse_headers(s, end, hm->headers,
|
||||
sizeof(hm->headers) / sizeof(hm->headers[0]));
|
||||
if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
|
||||
int64_t content_len = mg_to64(*cl);
|
||||
if(content_len < 0) return -1;
|
||||
hm->body.len = (size_t) content_len;
|
||||
if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
|
||||
hm->message.len = (size_t) req_len + hm->body.len;
|
||||
}
|
||||
|
||||
@ -430,18 +446,18 @@ static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
|
||||
return mg_str("text/plain; charset=utf-8");
|
||||
}
|
||||
|
||||
static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
|
||||
static int getrange(struct mg_str *s, size_t *a, size_t *b) {
|
||||
size_t i, numparsed = 0;
|
||||
// MG_INFO(("%.*s", (int) s->len, s->ptr));
|
||||
for (i = 0; i + 6 < s->len; i++) {
|
||||
if (memcmp(&s->ptr[i], "bytes=", 6) == 0) {
|
||||
struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6);
|
||||
if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
|
||||
*a = mg_to64(p);
|
||||
if (!mg_to_size_t(p, a)) return 0;
|
||||
// MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
|
||||
while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--;
|
||||
if (p.len && p.ptr[0] == '-') p.ptr++, p.len--;
|
||||
*b = mg_to64(p);
|
||||
if (!mg_to_size_t(p, b)) return 0;
|
||||
if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
|
||||
// MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
|
||||
break;
|
||||
@ -493,12 +509,12 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
} else {
|
||||
int n, status = 200;
|
||||
char range[100];
|
||||
int64_t r1 = 0, r2 = 0, cl = (int64_t) size;
|
||||
|
||||
size_t r1 = 0, r2 = 0, cl = size;
|
||||
|
||||
// Handle Range header
|
||||
struct mg_str *rh = mg_http_get_header(hm, "Range");
|
||||
range[0] = '\0';
|
||||
if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) {
|
||||
if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) {
|
||||
// If range is specified like "400-", set second limit to content len
|
||||
if (n == 1) r2 = cl - 1;
|
||||
if (r1 > r2 || r2 >= cl) {
|
||||
@ -510,9 +526,9 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
status = 206;
|
||||
cl = r2 - r1 + 1;
|
||||
mg_snprintf(range, sizeof(range),
|
||||
"Content-Range: bytes %lld-%lld/%lld\r\n", r1, r1 + cl - 1,
|
||||
(int64_t) size);
|
||||
fs->sk(fd->fd, (size_t) r1);
|
||||
"Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1,
|
||||
(uint64_t) (r1 + cl - 1), (uint64_t) size);
|
||||
fs->sk(fd->fd, r1);
|
||||
}
|
||||
}
|
||||
mg_printf(c,
|
||||
@ -522,8 +538,8 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
"Content-Length: %llu\r\n"
|
||||
"%s%s%s\r\n",
|
||||
status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
|
||||
etag, cl, gzip ? "Content-Encoding: gzip\r\n" : "", range,
|
||||
opts->extra_headers ? opts->extra_headers : "");
|
||||
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
||||
range, opts->extra_headers ? opts->extra_headers : "");
|
||||
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
|
||||
c->is_draining = 1;
|
||||
c->is_resp = 0;
|
||||
@ -534,7 +550,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
sizeof(size_t) * sizeof(size_t)];
|
||||
c->pfn = static_cb;
|
||||
c->pfn_data = fd;
|
||||
*clp = (size_t) cl;
|
||||
*clp = cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/str.c
26
src/str.c
@ -187,32 +187,6 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mg_tou64(struct mg_str str) {
|
||||
uint64_t result = 0;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
result *= 10;
|
||||
result += (unsigned) (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t mg_to64(struct mg_str str) {
|
||||
int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */;
|
||||
size_t i = 0;
|
||||
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
|
||||
if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
|
||||
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
if (result > max) return 0;
|
||||
result *= 10;
|
||||
result += (str.ptr[i] - '0');
|
||||
i++;
|
||||
}
|
||||
return result * neg;
|
||||
}
|
||||
|
||||
char *mg_remove_double_dots(char *s) {
|
||||
char *saved = s, *p = s;
|
||||
while (*s != '\0') {
|
||||
|
@ -35,6 +35,4 @@ char *mg_hex(const void *buf, size_t len, char *dst);
|
||||
void mg_unhex(const char *buf, size_t len, unsigned char *to);
|
||||
unsigned long mg_unhexn(const char *s, size_t len);
|
||||
int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
|
||||
int64_t mg_to64(struct mg_str str);
|
||||
uint64_t mg_tou64(struct mg_str str);
|
||||
char *mg_remove_double_dots(char *s);
|
||||
|
@ -790,10 +790,18 @@ static void test_http_server(void) {
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /..ddot HTTP/1.0\n\n") == 301);
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /..ddot/ HTTP/1.0\n\n") == 200);
|
||||
ASSERT(cmpbody(buf, "hi\n") == 0);
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n"
|
||||
"Content-Length: -123\n\n") == 0);
|
||||
ASSERT(fetch(&mgr, buf, url, "POST /a.txt HTTP/1.0\n"
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"GET /a.txt HTTP/1.0\n"
|
||||
"Content-Length: -123\n\n") == 0);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /a.txt HTTP/1.0\n"
|
||||
"Content-Length: -123\n\n") == 0);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"GET /a.txt HTTP/1.0\n"
|
||||
"Content-Length: 19000000000000000000\n\n") == 0);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /a.txt HTTP/1.0\n"
|
||||
"Content-Length: 19000000000000000000\n\n") == 0);
|
||||
|
||||
{
|
||||
extern char *mg_http_etag(char *, size_t, size_t, time_t);
|
||||
@ -1405,7 +1413,9 @@ static void test_http_range(void) {
|
||||
ASSERT(mgr.conns == NULL);
|
||||
}
|
||||
|
||||
static void f1(void *arg) { (*(int *) arg)++; }
|
||||
static void f1(void *arg) {
|
||||
(*(int *) arg)++;
|
||||
}
|
||||
|
||||
static void test_timer(void) {
|
||||
int v1 = 0, v2 = 0, v3 = 0;
|
||||
@ -1964,14 +1974,19 @@ static void test_util(void) {
|
||||
free(s);
|
||||
}
|
||||
|
||||
ASSERT(mg_to64(mg_str("-9223372036854775809")) == 0);
|
||||
ASSERT(mg_to64(mg_str("9223372036854775800")) == 0);
|
||||
ASSERT(mg_to64(mg_str("9223372036854775700")) > 0);
|
||||
ASSERT(mg_tou64(mg_str("0")) == 0);
|
||||
ASSERT(mg_tou64(mg_str("123")) == 123);
|
||||
ASSERT(mg_tou64(mg_str("")) == 0);
|
||||
ASSERT(mg_tou64(mg_str("-")) == 0);
|
||||
ASSERT(mg_tou64(mg_str("18446744073709551615")) == 18446744073709551615U);
|
||||
{
|
||||
extern bool mg_to_size_t(struct mg_str, size_t *);
|
||||
size_t val = 1;
|
||||
ASSERT(mg_to_size_t(mg_str("0"), &val) && val == 0);
|
||||
ASSERT(mg_to_size_t(mg_str("123"), &val) && val == 123);
|
||||
ASSERT(mg_to_size_t(mg_str(""), &val) && val == 0);
|
||||
ASSERT(mg_to_size_t(mg_str("-"), &val) == false);
|
||||
ASSERT(mg_to_size_t(mg_str("18446744073709551616"), &val) ==
|
||||
false); // range +1
|
||||
ASSERT(mg_to_size_t(mg_str("18446744073709551610"), &val) == false);
|
||||
// TODO(): ASSERT(mg_to_size_t(mg_str("18446744073709551609"), &val) &&
|
||||
// val == 18446744073709551609U); // our max or SIZE_MAX
|
||||
}
|
||||
|
||||
{
|
||||
size_t i;
|
||||
@ -2865,7 +2880,9 @@ static void start_thread(void (*f)(void *), void *p) {
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
#else
|
||||
static void start_thread(void (*f)(void *), void *p) { (void) f, (void) p; }
|
||||
static void start_thread(void (*f)(void *), void *p) {
|
||||
(void) f, (void) p;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_queue(void) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user