mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-07 01:07:19 +08:00
Implement mg_http_parse_header2()
Which is a replacement of (deprecated) `mg_http_parse_header`, but, similarly to `asprintf`, allocates a new buffer if the client-provided one is not large enough. Also use it throughout mongoose code, and thus some header-related limitations are removed; in particular, https://github.com/cesanta/mongoose/issues/813 is fixed. CL: Mongoose Web Server: Deprecate `mg_http_parse_header()` and implement `mg_http_parse_header2()` instead, which allocates a new buffer if the client-provided one is not large enough (similarly to `asprintf`). CL: Mongoose Web Server: Fix limitations of header value lengths, e.g. when parsing authentication headers such as nonce, etc. PUBLISHED_FROM=c75b1bbbbdb294ea85075ce69b1368f115fdd1ef
This commit is contained in:
parent
a8a7d2cf7e
commit
b9b20c6494
@ -10,6 +10,7 @@ items:
|
||||
- { name: mg_get_http_var.md }
|
||||
- { name: mg_http_check_digest_auth.md }
|
||||
- { name: mg_http_parse_header.md }
|
||||
- { name: mg_http_parse_header2.md }
|
||||
- { name: mg_http_reverse_proxy.md }
|
||||
- { name: mg_http_send_error.md }
|
||||
- { name: mg_http_send_redirect.md }
|
||||
|
@ -7,17 +7,9 @@ signature: |
|
||||
size_t buf_size);
|
||||
---
|
||||
|
||||
Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
|
||||
in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero
|
||||
otherwise.
|
||||
DEPRECATED: use mg_http_parse_header2() instead.
|
||||
|
||||
This function is supposed to parse cookies, authentication headers, etc.
|
||||
Example (error handling omitted):
|
||||
|
||||
char user[20];
|
||||
struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
|
||||
mg_http_parse_header(hdr, "username", user, sizeof(user));
|
||||
|
||||
Returns the length of the variable's value. If buffer is not large enough,
|
||||
or variable not found, 0 is returned.
|
||||
Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
|
||||
`char **`), and thus it cannot allocate a new buffer if the provided one
|
||||
is not enough, and just returns 0 in that case.
|
||||
|
||||
|
30
docs/c-api/mg_http_server.h/mg_http_parse_header2.md
Normal file
30
docs/c-api/mg_http_server.h/mg_http_parse_header2.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "mg_http_parse_header2()"
|
||||
decl_name: "mg_http_parse_header2"
|
||||
symbol_kind: "func"
|
||||
signature: |
|
||||
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||
size_t buf_size);
|
||||
---
|
||||
|
||||
Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
|
||||
in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
|
||||
allocates a buffer of required size and writes it to `*buf`, similar to
|
||||
asprintf(). The caller should always check whether the buffer was updated,
|
||||
and free it if so.
|
||||
|
||||
This function is supposed to parse cookies, authentication headers, etc.
|
||||
Example (error handling omitted):
|
||||
|
||||
char user_buf[20];
|
||||
char *user = user_buf;
|
||||
struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
|
||||
mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
|
||||
// ... do something useful with user
|
||||
if (user != user_buf) {
|
||||
free(user);
|
||||
}
|
||||
|
||||
Returns the length of the variable's value. If variable is not found, 0 is
|
||||
returned.
|
||||
|
@ -59,20 +59,29 @@ static int check_pass(const char *user, const char *pass) {
|
||||
*/
|
||||
static struct session *get_session(struct http_message *hm) {
|
||||
struct mg_str *cookie_header = mg_get_http_header(hm, "cookie");
|
||||
if (cookie_header == NULL) return NULL;
|
||||
char ssid[21];
|
||||
if (!mg_http_parse_header(cookie_header, SESSION_COOKIE_NAME, ssid,
|
||||
sizeof(ssid))) {
|
||||
return NULL;
|
||||
if (cookie_header == NULL) goto clean;
|
||||
char ssid_buf[21];
|
||||
char *ssid = ssid_buf;
|
||||
struct session *ret = NULL;
|
||||
if (!mg_http_parse_header2(cookie_header, SESSION_COOKIE_NAME, &ssid,
|
||||
sizeof(ssid_buf))) {
|
||||
goto clean;
|
||||
}
|
||||
uint64_t sid = strtoull(ssid, NULL, 16);
|
||||
for (int i = 0; i < NUM_SESSIONS; i++) {
|
||||
int i;
|
||||
for (i = 0; i < NUM_SESSIONS; i++) {
|
||||
if (s_sessions[i].id == sid) {
|
||||
s_sessions[i].last_used = mg_time();
|
||||
return &s_sessions[i];
|
||||
ret = &s_sessions[i];
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
clean:
|
||||
if (ssid != ssid_buf) {
|
||||
free(ssid);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -91,7 +100,8 @@ static struct session *create_session(const char *user,
|
||||
/* Find first available slot or use the oldest one. */
|
||||
struct session *s = NULL;
|
||||
struct session *oldest_s = s_sessions;
|
||||
for (int i = 0; i < NUM_SESSIONS; i++) {
|
||||
int i;
|
||||
for (i = 0; i < NUM_SESSIONS; i++) {
|
||||
if (s_sessions[i].id == 0) {
|
||||
s = &s_sessions[i];
|
||||
break;
|
||||
@ -176,7 +186,8 @@ static void logout_handler(struct mg_connection *nc, int ev, void *p) {
|
||||
/* Cleans up sessions that have been idle for too long. */
|
||||
void check_sessions(void) {
|
||||
double threshold = mg_time() - SESSION_TTL;
|
||||
for (int i = 0; i < NUM_SESSIONS; i++) {
|
||||
int i;
|
||||
for (i = 0; i < NUM_SESSIONS; i++) {
|
||||
struct session *s = &s_sessions[i];
|
||||
if (s->id != 0 && s->last_used < threshold) {
|
||||
fprintf(stderr, "Session %" INT64_X_FMT " (%s) closed due to idleness.\n",
|
||||
|
238
mongoose.c
238
mongoose.c
@ -5373,6 +5373,86 @@ out:
|
||||
/* Amalgamated: #include "mg_internal.h" */
|
||||
/* Amalgamated: #include "mg_util.h" */
|
||||
|
||||
/* altbuf {{{ */
|
||||
|
||||
/*
|
||||
* Alternate buffer: fills the client-provided buffer with data; and if it's
|
||||
* not large enough, allocates another buffer (via mbuf), similar to asprintf.
|
||||
*/
|
||||
struct altbuf {
|
||||
struct mbuf m;
|
||||
char *user_buf;
|
||||
size_t len;
|
||||
size_t user_buf_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes altbuf; `buf`, `buf_size` is the client-provided buffer.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) {
|
||||
mbuf_init(&ab->m, 0);
|
||||
ab->user_buf = buf;
|
||||
ab->user_buf_size = buf_size;
|
||||
ab->len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends a single char to the altbuf.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) {
|
||||
if (ab->len < ab->user_buf_size) {
|
||||
/* The data fits into the original buffer */
|
||||
ab->user_buf[ab->len++] = c;
|
||||
} else {
|
||||
/* The data can't fit into the original buffer, so write it to mbuf. */
|
||||
|
||||
/*
|
||||
* First of all, see if that's the first byte which overflows the original
|
||||
* buffer: if so, copy the existing data from there to a newly allocated
|
||||
* mbuf.
|
||||
*/
|
||||
if (ab->len > 0 && ab->m.len == 0) {
|
||||
mbuf_append(&ab->m, ab->user_buf, ab->len);
|
||||
}
|
||||
|
||||
mbuf_append(&ab->m, &c, 1);
|
||||
ab->len = ab->m.len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resets any data previously appended to altbuf.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_reset(struct altbuf *ab) {
|
||||
mbuf_free(&ab->m);
|
||||
ab->len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the additional buffer was allocated (and thus the data
|
||||
* is in the mbuf, not the client-provided buffer)
|
||||
*/
|
||||
MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) {
|
||||
return ab->len > ab->user_buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the actual buffer with data, either the client-provided or a newly
|
||||
* allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first.
|
||||
*/
|
||||
MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) {
|
||||
if (altbuf_reallocated(ab)) {
|
||||
if (trim) {
|
||||
mbuf_trim(&ab->m);
|
||||
}
|
||||
return ab->m.buf;
|
||||
} else {
|
||||
return ab->user_buf;
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
||||
|
||||
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
||||
@ -6182,7 +6262,8 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
struct mg_str *ct;
|
||||
struct mbuf *io = &nc->recv_mbuf;
|
||||
|
||||
char boundary[100];
|
||||
char boundary_buf[100];
|
||||
char *boundary = boundary_buf;
|
||||
int boundary_len;
|
||||
|
||||
ct = mg_get_http_header(hm, "Content-Type");
|
||||
@ -6197,7 +6278,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
}
|
||||
|
||||
boundary_len =
|
||||
mg_http_parse_header(ct, "boundary", boundary, sizeof(boundary));
|
||||
mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf));
|
||||
if (boundary_len == 0) {
|
||||
/*
|
||||
* Content type is multipart, but there is no boundary,
|
||||
@ -6234,7 +6315,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
mbuf_remove(io, req_len);
|
||||
}
|
||||
exit_mp:
|
||||
;
|
||||
if (boundary != boundary_buf) MG_FREE(boundary);
|
||||
}
|
||||
|
||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||
@ -6316,17 +6397,24 @@ static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||
const char *var_name,
|
||||
struct altbuf *ab);
|
||||
|
||||
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
int data_size;
|
||||
const char *boundary, *block_begin;
|
||||
struct mbuf *io = &c->recv_mbuf;
|
||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||
char file_name[100], var_name[100];
|
||||
struct altbuf ab_file_name, ab_var_name;
|
||||
int line_len;
|
||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
||||
data_size = io->len - (block_begin - io->buf);
|
||||
|
||||
altbuf_init(&ab_file_name, NULL, 0);
|
||||
altbuf_init(&ab_var_name, NULL, 0);
|
||||
|
||||
while (data_size > 0 &&
|
||||
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
||||
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
||||
@ -6336,11 +6424,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
|
||||
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
||||
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
||||
mg_http_parse_header(&header, "name", var_name, sizeof(var_name) - 2);
|
||||
mg_http_parse_header(&header, "filename", file_name,
|
||||
sizeof(file_name) - 2);
|
||||
|
||||
altbuf_reset(&ab_var_name);
|
||||
mg_http_parse_header_internal(&header, "name", &ab_var_name);
|
||||
|
||||
altbuf_reset(&ab_file_name);
|
||||
mg_http_parse_header_internal(&header, "filename", &ab_file_name);
|
||||
|
||||
block_begin += line_len;
|
||||
data_size -= line_len;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -6351,10 +6444,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
||||
}
|
||||
|
||||
/* Reserve 2 bytes for "\r\n" in file_name and var_name */
|
||||
altbuf_append(&ab_file_name, '\0');
|
||||
altbuf_append(&ab_file_name, '\0');
|
||||
altbuf_append(&ab_var_name, '\0');
|
||||
altbuf_append(&ab_var_name, '\0');
|
||||
|
||||
MG_FREE((void *) pd->mp_stream.file_name);
|
||||
pd->mp_stream.file_name = strdup(file_name);
|
||||
pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */);
|
||||
MG_FREE((void *) pd->mp_stream.var_name);
|
||||
pd->mp_stream.var_name = strdup(var_name);
|
||||
pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */);
|
||||
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
||||
@ -6367,6 +6466,9 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
|
||||
altbuf_reset(&ab_var_name);
|
||||
altbuf_reset(&ab_file_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -6921,14 +7023,12 @@ void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) {
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size) {
|
||||
int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
|
||||
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||
const char *var_name,
|
||||
struct altbuf *ab) {
|
||||
int ch = ' ', ch1 = ',', n = strlen(var_name);
|
||||
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
||||
|
||||
if (buf != NULL && buf_size > 0) buf[0] = '\0';
|
||||
if (hdr == NULL) return 0;
|
||||
|
||||
/* Find where variable starts */
|
||||
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
||||
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
||||
@ -6942,19 +7042,51 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
ch = ch1 = *s++;
|
||||
}
|
||||
p = s;
|
||||
while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
|
||||
while (p < end && p[0] != ch && p[0] != ch1) {
|
||||
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
||||
buf[len++] = *p++;
|
||||
altbuf_append(ab, *p++);
|
||||
}
|
||||
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
|
||||
len = 0;
|
||||
} else {
|
||||
if (len > 0 && s[len - 1] == ',') len--;
|
||||
if (len > 0 && s[len - 1] == ';') len--;
|
||||
buf[len] = '\0';
|
||||
|
||||
if (ch != ' ' && *p != ch) {
|
||||
altbuf_reset(ab);
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is some data, append a NUL. */
|
||||
if (ab->len > 0) {
|
||||
altbuf_append(ab, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||
size_t buf_size) {
|
||||
struct altbuf ab;
|
||||
altbuf_init(&ab, *buf, buf_size);
|
||||
if (hdr == NULL) return 0;
|
||||
if (*buf != NULL && buf_size > 0) *buf[0] = '\0';
|
||||
|
||||
mg_http_parse_header_internal(hdr, var_name, &ab);
|
||||
|
||||
/*
|
||||
* Get a (trimmed) buffer, and return a len without a NUL byte which might
|
||||
* have been added.
|
||||
*/
|
||||
*buf = altbuf_get_buf(&ab, 1 /* trim */);
|
||||
return ab.len > 0 ? ab.len - 1 : 0;
|
||||
}
|
||||
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size) {
|
||||
char *buf2 = buf;
|
||||
|
||||
int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size);
|
||||
|
||||
if (buf2 != buf) {
|
||||
/* Buffer was not enough and was reallocated: free it and just return 0 */
|
||||
MG_FREE(buf2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -7092,27 +7224,34 @@ static int mg_check_nonce(const char *nonce) {
|
||||
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
int ret = 0;
|
||||
struct mg_str *hdr;
|
||||
char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20],
|
||||
nonce[30];
|
||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||
|
||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||
|
||||
/* Parse "Authorization:" header, fail fast on parse error */
|
||||
if (hm == NULL || fp == NULL ||
|
||||
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
||||
mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 ||
|
||||
mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
|
||||
mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
|
||||
mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
|
||||
mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
|
||||
mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
|
||||
mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) ==
|
||||
0 ||
|
||||
mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) ==
|
||||
0 ||
|
||||
mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 ||
|
||||
mg_check_nonce(nonce) == 0) {
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||
|
||||
return mg_check_digest_auth(
|
||||
ret = mg_check_digest_auth(
|
||||
hm->method,
|
||||
mg_mk_str_n(
|
||||
hm->uri.p,
|
||||
@ -7120,6 +7259,17 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||
fp);
|
||||
|
||||
clean:
|
||||
if (username != username_buf) MG_FREE(username);
|
||||
if (cnonce != cnonce_buf) MG_FREE(cnonce);
|
||||
if (response != response_buf) MG_FREE(response);
|
||||
if (uri != uri_buf) MG_FREE(uri);
|
||||
if (qop != qop_buf) MG_FREE(qop);
|
||||
if (nc != nc_buf) MG_FREE(nc);
|
||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
@ -8189,8 +8339,24 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
||||
struct mg_str header;
|
||||
header.p = buf + n + cdl;
|
||||
header.len = ll - (cdl + 2);
|
||||
mg_http_parse_header(&header, "name", var_name, var_name_len);
|
||||
mg_http_parse_header(&header, "filename", file_name, file_name_len);
|
||||
{
|
||||
char *var_name2 = var_name;
|
||||
mg_http_parse_header2(&header, "name", &var_name2, var_name_len);
|
||||
/* TODO: handle reallocated buffer correctly */
|
||||
if (var_name2 != var_name) {
|
||||
MG_FREE(var_name2);
|
||||
var_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
{
|
||||
char *file_name2 = file_name;
|
||||
mg_http_parse_header2(&header, "filename", &file_name2, file_name_len);
|
||||
/* TODO: handle reallocated buffer correctly */
|
||||
if (file_name2 != file_name) {
|
||||
MG_FREE(file_name2);
|
||||
file_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
35
mongoose.h
35
mongoose.h
@ -4647,21 +4647,42 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
|
||||
|
||||
/*
|
||||
* Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
|
||||
* in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero
|
||||
* otherwise.
|
||||
* in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
|
||||
* allocates a buffer of required size and writes it to `*buf`, similar to
|
||||
* asprintf(). The caller should always check whether the buffer was updated,
|
||||
* and free it if so.
|
||||
*
|
||||
* This function is supposed to parse cookies, authentication headers, etc.
|
||||
* Example (error handling omitted):
|
||||
*
|
||||
* char user[20];
|
||||
* char user_buf[20];
|
||||
* char *user = user_buf;
|
||||
* struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
|
||||
* mg_http_parse_header(hdr, "username", user, sizeof(user));
|
||||
* mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
|
||||
* // ... do something useful with user
|
||||
* if (user != user_buf) {
|
||||
* free(user);
|
||||
* }
|
||||
*
|
||||
* Returns the length of the variable's value. If buffer is not large enough,
|
||||
* or variable not found, 0 is returned.
|
||||
* Returns the length of the variable's value. If variable is not found, 0 is
|
||||
* returned.
|
||||
*/
|
||||
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||
size_t buf_size);
|
||||
|
||||
/*
|
||||
* DEPRECATED: use mg_http_parse_header2() instead.
|
||||
*
|
||||
* Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
|
||||
* `char **`), and thus it cannot allocate a new buffer if the provided one
|
||||
* is not enough, and just returns 0 in that case.
|
||||
*/
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size);
|
||||
size_t buf_size)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((deprecated));
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Gets and parses the Authorization: Basic header
|
||||
|
238
src/mg_http.c
238
src/mg_http.c
@ -9,6 +9,86 @@
|
||||
#include "mg_internal.h"
|
||||
#include "mg_util.h"
|
||||
|
||||
/* altbuf {{{ */
|
||||
|
||||
/*
|
||||
* Alternate buffer: fills the client-provided buffer with data; and if it's
|
||||
* not large enough, allocates another buffer (via mbuf), similar to asprintf.
|
||||
*/
|
||||
struct altbuf {
|
||||
struct mbuf m;
|
||||
char *user_buf;
|
||||
size_t len;
|
||||
size_t user_buf_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes altbuf; `buf`, `buf_size` is the client-provided buffer.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) {
|
||||
mbuf_init(&ab->m, 0);
|
||||
ab->user_buf = buf;
|
||||
ab->user_buf_size = buf_size;
|
||||
ab->len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends a single char to the altbuf.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) {
|
||||
if (ab->len < ab->user_buf_size) {
|
||||
/* The data fits into the original buffer */
|
||||
ab->user_buf[ab->len++] = c;
|
||||
} else {
|
||||
/* The data can't fit into the original buffer, so write it to mbuf. */
|
||||
|
||||
/*
|
||||
* First of all, see if that's the first byte which overflows the original
|
||||
* buffer: if so, copy the existing data from there to a newly allocated
|
||||
* mbuf.
|
||||
*/
|
||||
if (ab->len > 0 && ab->m.len == 0) {
|
||||
mbuf_append(&ab->m, ab->user_buf, ab->len);
|
||||
}
|
||||
|
||||
mbuf_append(&ab->m, &c, 1);
|
||||
ab->len = ab->m.len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resets any data previously appended to altbuf.
|
||||
*/
|
||||
MG_INTERNAL void altbuf_reset(struct altbuf *ab) {
|
||||
mbuf_free(&ab->m);
|
||||
ab->len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the additional buffer was allocated (and thus the data
|
||||
* is in the mbuf, not the client-provided buffer)
|
||||
*/
|
||||
MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) {
|
||||
return ab->len > ab->user_buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the actual buffer with data, either the client-provided or a newly
|
||||
* allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first.
|
||||
*/
|
||||
MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) {
|
||||
if (altbuf_reallocated(ab)) {
|
||||
if (trim) {
|
||||
mbuf_trim(&ab->m);
|
||||
}
|
||||
return ab->m.buf;
|
||||
} else {
|
||||
return ab->user_buf;
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
||||
|
||||
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
||||
@ -818,7 +898,8 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
struct mg_str *ct;
|
||||
struct mbuf *io = &nc->recv_mbuf;
|
||||
|
||||
char boundary[100];
|
||||
char boundary_buf[100];
|
||||
char *boundary = boundary_buf;
|
||||
int boundary_len;
|
||||
|
||||
ct = mg_get_http_header(hm, "Content-Type");
|
||||
@ -833,7 +914,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
}
|
||||
|
||||
boundary_len =
|
||||
mg_http_parse_header(ct, "boundary", boundary, sizeof(boundary));
|
||||
mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf));
|
||||
if (boundary_len == 0) {
|
||||
/*
|
||||
* Content type is multipart, but there is no boundary,
|
||||
@ -870,7 +951,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
||||
mbuf_remove(io, req_len);
|
||||
}
|
||||
exit_mp:
|
||||
;
|
||||
if (boundary != boundary_buf) MG_FREE(boundary);
|
||||
}
|
||||
|
||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||
@ -952,17 +1033,24 @@ static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||
const char *var_name,
|
||||
struct altbuf *ab);
|
||||
|
||||
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
int data_size;
|
||||
const char *boundary, *block_begin;
|
||||
struct mbuf *io = &c->recv_mbuf;
|
||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||
char file_name[100], var_name[100];
|
||||
struct altbuf ab_file_name, ab_var_name;
|
||||
int line_len;
|
||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
||||
data_size = io->len - (block_begin - io->buf);
|
||||
|
||||
altbuf_init(&ab_file_name, NULL, 0);
|
||||
altbuf_init(&ab_var_name, NULL, 0);
|
||||
|
||||
while (data_size > 0 &&
|
||||
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
||||
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
||||
@ -972,11 +1060,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
|
||||
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
||||
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
||||
mg_http_parse_header(&header, "name", var_name, sizeof(var_name) - 2);
|
||||
mg_http_parse_header(&header, "filename", file_name,
|
||||
sizeof(file_name) - 2);
|
||||
|
||||
altbuf_reset(&ab_var_name);
|
||||
mg_http_parse_header_internal(&header, "name", &ab_var_name);
|
||||
|
||||
altbuf_reset(&ab_file_name);
|
||||
mg_http_parse_header_internal(&header, "filename", &ab_file_name);
|
||||
|
||||
block_begin += line_len;
|
||||
data_size -= line_len;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -987,10 +1080,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
||||
}
|
||||
|
||||
/* Reserve 2 bytes for "\r\n" in file_name and var_name */
|
||||
altbuf_append(&ab_file_name, '\0');
|
||||
altbuf_append(&ab_file_name, '\0');
|
||||
altbuf_append(&ab_var_name, '\0');
|
||||
altbuf_append(&ab_var_name, '\0');
|
||||
|
||||
MG_FREE((void *) pd->mp_stream.file_name);
|
||||
pd->mp_stream.file_name = strdup(file_name);
|
||||
pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */);
|
||||
MG_FREE((void *) pd->mp_stream.var_name);
|
||||
pd->mp_stream.var_name = strdup(var_name);
|
||||
pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */);
|
||||
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
||||
@ -1003,6 +1102,9 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
|
||||
altbuf_reset(&ab_var_name);
|
||||
altbuf_reset(&ab_file_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1557,14 +1659,12 @@ void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) {
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size) {
|
||||
int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
|
||||
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||
const char *var_name,
|
||||
struct altbuf *ab) {
|
||||
int ch = ' ', ch1 = ',', n = strlen(var_name);
|
||||
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
||||
|
||||
if (buf != NULL && buf_size > 0) buf[0] = '\0';
|
||||
if (hdr == NULL) return 0;
|
||||
|
||||
/* Find where variable starts */
|
||||
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
||||
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
||||
@ -1578,19 +1678,51 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
ch = ch1 = *s++;
|
||||
}
|
||||
p = s;
|
||||
while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
|
||||
while (p < end && p[0] != ch && p[0] != ch1) {
|
||||
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
||||
buf[len++] = *p++;
|
||||
altbuf_append(ab, *p++);
|
||||
}
|
||||
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
|
||||
len = 0;
|
||||
} else {
|
||||
if (len > 0 && s[len - 1] == ',') len--;
|
||||
if (len > 0 && s[len - 1] == ';') len--;
|
||||
buf[len] = '\0';
|
||||
|
||||
if (ch != ' ' && *p != ch) {
|
||||
altbuf_reset(ab);
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is some data, append a NUL. */
|
||||
if (ab->len > 0) {
|
||||
altbuf_append(ab, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||
size_t buf_size) {
|
||||
struct altbuf ab;
|
||||
altbuf_init(&ab, *buf, buf_size);
|
||||
if (hdr == NULL) return 0;
|
||||
if (*buf != NULL && buf_size > 0) *buf[0] = '\0';
|
||||
|
||||
mg_http_parse_header_internal(hdr, var_name, &ab);
|
||||
|
||||
/*
|
||||
* Get a (trimmed) buffer, and return a len without a NUL byte which might
|
||||
* have been added.
|
||||
*/
|
||||
*buf = altbuf_get_buf(&ab, 1 /* trim */);
|
||||
return ab.len > 0 ? ab.len - 1 : 0;
|
||||
}
|
||||
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size) {
|
||||
char *buf2 = buf;
|
||||
|
||||
int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size);
|
||||
|
||||
if (buf2 != buf) {
|
||||
/* Buffer was not enough and was reallocated: free it and just return 0 */
|
||||
MG_FREE(buf2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -1728,27 +1860,34 @@ static int mg_check_nonce(const char *nonce) {
|
||||
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
int ret = 0;
|
||||
struct mg_str *hdr;
|
||||
char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20],
|
||||
nonce[30];
|
||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||
|
||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||
|
||||
/* Parse "Authorization:" header, fail fast on parse error */
|
||||
if (hm == NULL || fp == NULL ||
|
||||
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
||||
mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 ||
|
||||
mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
|
||||
mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
|
||||
mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
|
||||
mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
|
||||
mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
|
||||
mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) ==
|
||||
0 ||
|
||||
mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) ==
|
||||
0 ||
|
||||
mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 ||
|
||||
mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 ||
|
||||
mg_check_nonce(nonce) == 0) {
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||
|
||||
return mg_check_digest_auth(
|
||||
ret = mg_check_digest_auth(
|
||||
hm->method,
|
||||
mg_mk_str_n(
|
||||
hm->uri.p,
|
||||
@ -1756,6 +1895,17 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||
fp);
|
||||
|
||||
clean:
|
||||
if (username != username_buf) MG_FREE(username);
|
||||
if (cnonce != cnonce_buf) MG_FREE(cnonce);
|
||||
if (response != response_buf) MG_FREE(response);
|
||||
if (uri != uri_buf) MG_FREE(uri);
|
||||
if (qop != qop_buf) MG_FREE(qop);
|
||||
if (nc != nc_buf) MG_FREE(nc);
|
||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
@ -2825,8 +2975,24 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
||||
struct mg_str header;
|
||||
header.p = buf + n + cdl;
|
||||
header.len = ll - (cdl + 2);
|
||||
mg_http_parse_header(&header, "name", var_name, var_name_len);
|
||||
mg_http_parse_header(&header, "filename", file_name, file_name_len);
|
||||
{
|
||||
char *var_name2 = var_name;
|
||||
mg_http_parse_header2(&header, "name", &var_name2, var_name_len);
|
||||
/* TODO: handle reallocated buffer correctly */
|
||||
if (var_name2 != var_name) {
|
||||
MG_FREE(var_name2);
|
||||
var_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
{
|
||||
char *file_name2 = file_name;
|
||||
mg_http_parse_header2(&header, "filename", &file_name2, file_name_len);
|
||||
/* TODO: handle reallocated buffer correctly */
|
||||
if (file_name2 != file_name) {
|
||||
MG_FREE(file_name2);
|
||||
file_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,21 +31,42 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
|
||||
|
||||
/*
|
||||
* Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
|
||||
* in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero
|
||||
* otherwise.
|
||||
* in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
|
||||
* allocates a buffer of required size and writes it to `*buf`, similar to
|
||||
* asprintf(). The caller should always check whether the buffer was updated,
|
||||
* and free it if so.
|
||||
*
|
||||
* This function is supposed to parse cookies, authentication headers, etc.
|
||||
* Example (error handling omitted):
|
||||
*
|
||||
* char user[20];
|
||||
* char user_buf[20];
|
||||
* char *user = user_buf;
|
||||
* struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
|
||||
* mg_http_parse_header(hdr, "username", user, sizeof(user));
|
||||
* mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
|
||||
* // ... do something useful with user
|
||||
* if (user != user_buf) {
|
||||
* free(user);
|
||||
* }
|
||||
*
|
||||
* Returns the length of the variable's value. If buffer is not large enough,
|
||||
* or variable not found, 0 is returned.
|
||||
* Returns the length of the variable's value. If variable is not found, 0 is
|
||||
* returned.
|
||||
*/
|
||||
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||
size_t buf_size);
|
||||
|
||||
/*
|
||||
* DEPRECATED: use mg_http_parse_header2() instead.
|
||||
*
|
||||
* Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
|
||||
* `char **`), and thus it cannot allocate a new buffer if the provided one
|
||||
* is not enough, and just returns 0 in that case.
|
||||
*/
|
||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size);
|
||||
size_t buf_size)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((deprecated));
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Gets and parses the Authorization: Basic header
|
||||
|
@ -45,7 +45,9 @@ COMMON_FEATURE_FLAGS = \
|
||||
-DMG_ENABLE_SNTP -DMG_SNTP_NO_DELAY_CORRECTION \
|
||||
-DMG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||
UNIX_FEATURE_FLAGS=-DMG_ENABLE_IPV6 -DMG_ENABLE_SSL
|
||||
CFLAGS = -W -Wall -Wundef -Werror -g -O0 -Wno-multichar -D__USE_MISC \
|
||||
# TODO: remove -Wno-deprecated-declarations once deprecated
|
||||
# `mg_http_parse_header()` is removed from mongoose.
|
||||
CFLAGS = -W -Wall -Wundef -Werror -Wno-deprecated-declarations -g -O0 -Wno-multichar -D__USE_MISC \
|
||||
$(COMMON_FEATURE_FLAGS) $(UNIX_FEATURE_FLAGS) \
|
||||
$(patsubst %,-I%,$(subst :, ,$(VPATH))) \
|
||||
-include unit_test.h -pthread $(CFLAGS_EXTRA)
|
||||
|
@ -4941,12 +4941,34 @@ static const char *test_http_parse_header(void) {
|
||||
"xx=1 kl yy, ert=234 kl=123, "
|
||||
"uri=\"/?naii=x,y\";ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234");
|
||||
char buf[20];
|
||||
char *buf2;
|
||||
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, sizeof(buf)), 3);
|
||||
ASSERT_STREQ(buf, "234");
|
||||
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 2), 0);
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 3), 0);
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 4), 3);
|
||||
|
||||
buf2 = buf;
|
||||
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 2), 3);
|
||||
ASSERT(buf2 != buf);
|
||||
free(buf2);
|
||||
|
||||
buf2 = buf;
|
||||
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 3), 3);
|
||||
ASSERT(buf2 != buf);
|
||||
free(buf2);
|
||||
|
||||
buf2 = buf;
|
||||
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 4), 3);
|
||||
ASSERT(buf2 == buf);
|
||||
|
||||
buf2 = NULL;
|
||||
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 0), 3);
|
||||
ASSERT_STREQ(buf2, "234");
|
||||
free(buf2);
|
||||
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "gf", buf, sizeof(buf)), 0);
|
||||
ASSERT_EQ(mg_http_parse_header(&h, "zz", buf, sizeof(buf)), 5);
|
||||
ASSERT_STREQ(buf, "aa bb");
|
||||
|
Loading…
Reference in New Issue
Block a user