Commit b9b20c64 authored by Dmitry Frank's avatar Dmitry Frank Committed by Cesanta Bot

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
parent a8a7d2cf
......@@ -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.
---
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",
......
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
......@@ -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 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.
*
* 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.
*/
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");
......
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