Add mg_next_query_string_entry_n() and mg_url_decode_n()

Move to mg_util.h so encode and decode are next ot each other.

Pull out mg_next_list_entry_n() for advanced use cases.

Add unit tests.
This commit is contained in:
Deomid Ryabkov 2020-10-24 22:43:51 +01:00
parent 9fe1c93c9b
commit be64f81eee
13 changed files with 424 additions and 156 deletions

View File

@ -2116,6 +2116,43 @@ int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
return len;
}
static struct mg_str mg_next_list_entry_n(struct mg_str list, char sep1,
struct mg_str *val1, char sep2,
struct mg_str *val2) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val1 = list;
if ((chr = mg_strchr(*val1, sep1)) != NULL) {
/* Comma found. Store length and shift the list ptr */
val1->len = chr - val1->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (val2 != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
val2->len = 0;
val2->p = (const char *) memchr(val1->p, sep2, val1->len);
if (val2->p != NULL) {
val2->p++; /* Skip over sep2 character */
val2->len = val1->p + val1->len - val2->p;
val1->len = (val2->p - val1->p) - 1;
}
}
}
return list;
}
const char *mg_next_comma_list_entry(const char *, struct mg_str *,
struct mg_str *) WEAK;
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
@ -2128,38 +2165,16 @@ struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val = list;
return mg_next_list_entry_n(list, ',', val, '=', eq_val);
}
if ((chr = mg_strchr(*val, ',')) != NULL) {
/* Comma found. Store length and shift the list ptr */
val->len = chr - val->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (eq_val != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
eq_val->len = 0;
eq_val->p = (const char *) memchr(val->p, '=', val->len);
if (eq_val->p != NULL) {
eq_val->p++; /* Skip over '=' character */
eq_val->len = val->p + val->len - eq_val->p;
val->len = (eq_val->p - val->p) - 1;
}
}
}
return list;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) {
return mg_next_list_entry_n(list, '&', val, '=', eq_val);
}
size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
@ -7264,34 +7279,6 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
#endif
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
return -1;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; /* Null-terminate the destination */
return i >= src_len ? j : -1;
}
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
size_t dst_len) {
const char *p, *e, *s;
@ -10662,6 +10649,56 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
struct mg_str mg_url_encode(const struct mg_str src) {
return mg_url_encode_opt(src, mg_mk_str("._-$,;~()/"), 0);
}
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
struct mg_str srcs = MG_MK_STR_N(src, (size_t) src_len);
struct mg_str dsts = MG_MK_STR_N(dst, (size_t) dst_len);
int res = mg_url_decode_n(srcs, &dsts, is_form_url_encoded);
if (res >= 0) {
if (res < dst_len) {
dst[res] = '\0';
} else {
res = -1; /* Not enough space for NUL-temrination. */
}
}
return res;
}
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
int mg_url_decode_n(struct mg_str srcs, struct mg_str *dsts,
int is_form_url_encoded) {
int i, j, a, b, src_len, dst_len;
const char *src = srcs.p;
char *dst;
if (dsts == NULL) return -1;
dst = (char *) dsts->p;
src_len = (int) srcs.len;
dst_len = (int) dsts->len;
for (i = j = 0; i < src_len && j < dst_len; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
break;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dsts->len = (size_t) j;
return i == src_len ? j : -1;
}
#undef HEXTOI
#ifdef MG_MODULE_LINES
#line 1 "src/mg_mqtt.c"
#endif

View File

@ -2323,6 +2323,14 @@ const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val);
/*
* Helper for parsing query strings.
* Parses '&' and '=' entries. Does not perform unescaping.
*/
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val);
/*
* Matches 0-terminated string (mg_match_prefix) or string with given length
* mg_match_prefix_n against a glob pattern. Glob syntax:
@ -4325,6 +4333,27 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
/* Same as `mg_url_encode_opt(src, "._-$,;~()/", 0)`. */
struct mg_str mg_url_encode(const struct mg_str src);
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
* Otherwise, the length of the decoded string is returned,
* not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
/*
* mg_str variant of mg_url_decode. Does not NUL-terminate dst.
* It is ok for src and dst to be the same.
*/
int mg_url_decode_n(struct mg_str src, struct mg_str *dst,
int is_form_url_encoded);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@ -4662,20 +4691,6 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
*Otherwise,
* a length of the decoded string is returned, not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
const size_t *msg_lens, uint8_t *digest);
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],

6
src/.clang-format Normal file
View File

@ -0,0 +1,6 @@
BasedOnStyle: Google
AllowShortFunctionsOnASingleLine: false
SpaceAfterCStyleCast: true
PointerBindsToType: false
DerivePointerBinding: false
IncludeBlocks: Preserve

View File

@ -413,6 +413,43 @@ int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
return len;
}
static struct mg_str mg_next_list_entry_n(struct mg_str list, char sep1,
struct mg_str *val1, char sep2,
struct mg_str *val2) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val1 = list;
if ((chr = mg_strchr(*val1, sep1)) != NULL) {
/* Comma found. Store length and shift the list ptr */
val1->len = chr - val1->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (val2 != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
val2->len = 0;
val2->p = (const char *) memchr(val1->p, sep2, val1->len);
if (val2->p != NULL) {
val2->p++; /* Skip over sep2 character */
val2->len = val1->p + val1->len - val2->p;
val1->len = (val2->p - val1->p) - 1;
}
}
}
return list;
}
const char *mg_next_comma_list_entry(const char *, struct mg_str *,
struct mg_str *) WEAK;
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
@ -425,38 +462,16 @@ struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val = list;
return mg_next_list_entry_n(list, ',', val, '=', eq_val);
}
if ((chr = mg_strchr(*val, ',')) != NULL) {
/* Comma found. Store length and shift the list ptr */
val->len = chr - val->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (eq_val != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
eq_val->len = 0;
eq_val->p = (const char *) memchr(val->p, '=', val->len);
if (eq_val->p != NULL) {
eq_val->p++; /* Skip over '=' character */
eq_val->len = val->p + val->len - eq_val->p;
val->len = (eq_val->p - val->p) - 1;
}
}
}
return list;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) {
return mg_next_list_entry_n(list, '&', val, '=', eq_val);
}
size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;

View File

@ -148,6 +148,14 @@ const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val);
/*
* Helper for parsing query strings.
* Parses '&' and '=' entries. Does not perform unescaping.
*/
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val);
/*
* Matches 0-terminated string (mg_match_prefix) or string with given length
* mg_match_prefix_n against a glob pattern. Glob syntax:

View File

@ -1624,34 +1624,6 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
#endif
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
return -1;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; /* Null-terminate the destination */
return i >= src_len ? j : -1;
}
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
size_t dst_len) {
const char *p, *e, *s;

View File

@ -329,20 +329,6 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
*Otherwise,
* a length of the decoded string is returned, not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
const size_t *msg_lens, uint8_t *digest);
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],

View File

@ -338,3 +338,53 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
struct mg_str mg_url_encode(const struct mg_str src) {
return mg_url_encode_opt(src, mg_mk_str("._-$,;~()/"), 0);
}
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
struct mg_str srcs = MG_MK_STR_N(src, (size_t) src_len);
struct mg_str dsts = MG_MK_STR_N(dst, (size_t) dst_len);
int res = mg_url_decode_n(srcs, &dsts, is_form_url_encoded);
if (res >= 0) {
if (res < dst_len) {
dst[res] = '\0';
} else {
res = -1; /* Not enough space for NUL-temrination. */
}
}
return res;
}
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
int mg_url_decode_n(struct mg_str srcs, struct mg_str *dsts,
int is_form_url_encoded) {
int i, j, a, b, src_len, dst_len;
const char *src = srcs.p;
char *dst;
if (dsts == NULL) return -1;
dst = (char *) dsts->p;
src_len = (int) srcs.len;
dst_len = (int) dsts->len;
for (i = j = 0; i < src_len && j < dst_len; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
break;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dsts->len = (size_t) j;
return i == src_len ? j : -1;
}
#undef HEXTOI

View File

@ -205,6 +205,27 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
/* Same as `mg_url_encode_opt(src, "._-$,;~()/", 0)`. */
struct mg_str mg_url_encode(const struct mg_str src);
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
* Otherwise, the length of the decoded string is returned,
* not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
/*
* mg_str variant of mg_url_decode. Does not NUL-terminate dst.
* It is ok for src and dst to be the same.
*/
int mg_url_decode_n(struct mg_str src, struct mg_str *dst,
int is_form_url_encoded);
#ifdef __cplusplus
}
#endif /* __cplusplus */

6
test/.clang-format Normal file
View File

@ -0,0 +1,6 @@
BasedOnStyle: Google
AllowShortFunctionsOnASingleLine: false
SpaceAfterCStyleCast: true
PointerBindsToType: false
DerivePointerBinding: false
IncludeBlocks: Preserve

View File

@ -112,3 +112,7 @@ fuzz:
# docker run -v $(CURDIR)/../..:/cesanta -t -i --entrypoint=/bin/bash cesanta/mongoose_test
docker:
docker run --rm -v $(CURDIR)/../..:/cesanta cesanta/mongoose_test
amalgam:
cd .. && tools/amalgam.py --prefix=MG --public-header=mongoose.h --license=LICENSE `cat src/mongoose.c.manifest` > mongoose.c
cd .. && tools/amalgam.py --prefix=MG --license=LICENSE `cat src/mongoose.h.manifest` > mongoose.h

View File

@ -19,8 +19,8 @@
#include "mongoose.h"
#include "common/cs_md5.h"
#include "../src/mg_internal.h"
#include "common/cs_md5.h"
#include "test_main.h"
#include "test_util.h"
@ -856,6 +856,106 @@ static const char *test_mg_normalize_uri_path(void) {
return NULL;
}
static const char *test_mg_next_list_entry(void) {
struct mg_str val1, val2;
{
struct mg_str l = MG_NULL_STR;
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a=1");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "1");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a=");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a=,");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a=123,b=456");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_MG_STREQ(l, "b=456");
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "123");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "b");
ASSERT_MG_STREQ(val2, "456");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a,b=456");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_MG_STREQ(l, "b=456");
ASSERT_MG_STREQ(val1, "a");
ASSERT_MG_STREQ(val2, "");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "b");
ASSERT_MG_STREQ(val2, "456");
l = mg_next_comma_list_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
{
struct mg_str l = MG_MK_STR("a,b&c=4+5%206");
l = mg_next_query_string_entry_n(l, &val1, &val2);
ASSERT_MG_STREQ(l, "c=4+5%206");
ASSERT_MG_STREQ(val1, "a,b");
ASSERT_MG_STREQ(val2, "");
l = mg_next_query_string_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p != NULL);
ASSERT_EQ(l.len, 0);
ASSERT_MG_STREQ(val1, "c");
ASSERT_MG_STREQ(val2, "4+5%206");
l = mg_next_query_string_entry_n(l, &val1, &val2);
ASSERT_TRUE(l.p == NULL);
ASSERT_EQ(l.len, 0);
}
return NULL;
}
#define CHECK_U2LP(u, exp_rv, exp_lp, exp_rem) \
do { \
int rv; \
@ -931,21 +1031,25 @@ static const char *test_mg_uri_to_local_path(void) {
return NULL;
}
static const char *test_mg_url_encode(void) {
const struct mg_str encode_me =
MG_MK_STR("I'm a.little_tea-pot,here's$my;spout~oink(oink)oink/!");
static const char *test_mg_url_encode_decode(void) {
#define ENCODE_ME "I'm a.little_tea-po+,here's$my;spout~oink(oink)oink/!"
struct mg_str encode_me = MG_MK_STR(ENCODE_ME "XXX");
encode_me.len -= 3; /* Not nul-terminated on purpose */
{
struct mg_str encoded = mg_url_encode(encode_me);
ASSERT_MG_STREQ(
encoded,
"I%27m%20a.little_tea-pot,here%27s$my;spout~oink(oink)oink/%21");
"I%27m%20a.little_tea-po%2b,here%27s$my;spout~oink(oink)oink/%21");
ASSERT_EQ(mg_url_decode_n(encoded, &encoded, 1), encode_me.len);
ASSERT_MG_STREQ(encoded, ENCODE_ME);
free((void *) encoded.p);
}
{
struct mg_str encoded = mg_url_encode_opt(encode_me, mg_mk_str(NULL), 0);
ASSERT_MG_STREQ(encoded,
"I%27m%20a%2elittle%5ftea%2dpot%2chere%27s%24my%3bspout%"
"I%27m%20a%2elittle%5ftea%2dpo%2b%2chere%27s%24my%3bspout%"
"7eoink%28oink%29oink%2f%21");
ASSERT_EQ(mg_url_decode_n(encoded, &encoded, 0), encode_me.len);
free((void *) encoded.p);
}
{
@ -953,8 +1057,10 @@ static const char *test_mg_url_encode(void) {
MG_URL_ENCODE_F_UPPERCASE_HEX);
ASSERT_MG_STREQ(encoded,
"I%27m "
"a%2Elittle%5Ftea%2Dpot%2Chere%27s%24my%3Bspout%7Eoink%"
"a%2Elittle%5Ftea%2Dpo%2B%2Chere%27s%24my%3Bspout%7Eoink%"
"28oink%29oink/!");
ASSERT_EQ(mg_url_decode_n(encoded, &encoded, 0), encode_me.len);
ASSERT_MG_STREQ(encoded, ENCODE_ME);
free((void *) encoded.p);
}
{
@ -962,10 +1068,51 @@ static const char *test_mg_url_encode(void) {
encode_me, mg_mk_str("/!"),
MG_URL_ENCODE_F_SPACE_AS_PLUS | MG_URL_ENCODE_F_UPPERCASE_HEX);
ASSERT_MG_STREQ(encoded,
"I%27m+a%2Elittle%5Ftea%2Dpot%2Chere%27s%24my%3Bspout%"
"I%27m+a%2Elittle%5Ftea%2Dpo%2B%2Chere%27s%24my%3Bspout%"
"7Eoink%28oink%29oink/!");
ASSERT_EQ(mg_url_decode_n(encoded, &encoded, 1), encode_me.len);
ASSERT_MG_STREQ(encoded, ENCODE_ME);
free((void *) encoded.p);
}
{
struct mg_str in = MG_MK_STR("a%20b%20c");
char outbuf[6] = {'X', 'X', 'X', 'X', 'X', 'X'};
struct mg_str out = MG_MK_STR_N(outbuf, sizeof(outbuf)), out1 = out;
ASSERT_EQ(mg_url_decode_n(in, &out, 0), 5);
ASSERT_MG_STREQ(out, "a b c");
ASSERT_MG_STREQ(out1, "a b cX");
}
{
struct mg_str in = MG_MK_STR("a%20b%20c");
char outbuf[6] = {'X', 'X', 'X', 'X', 'X', 'X'};
ASSERT_EQ(mg_url_decode(in.p, (int) in.len, outbuf, sizeof(outbuf), 0), 5);
/* NUL-terminated for her pleasure. */
ASSERT_MG_STREQ(mg_mk_str(outbuf), "a b c");
}
{
struct mg_str in = MG_MK_STR("a%20b%20c");
char outbuf[6] = {'X', 'X', 'X', 'X', 'X', 'X'};
struct mg_str out = MG_MK_STR_N(outbuf, 4);
ASSERT_EQ(mg_url_decode_n(in, &out, 0), -1);
ASSERT_MG_STREQ(out, "a b ");
ASSERT_MG_STREQ(mg_mk_str_n(outbuf, sizeof(outbuf)), "a b XX");
}
{
struct mg_str in = MG_MK_STR("a%20b%20c");
char outbuf[6] = {'X', 'X', 'X', 'X', 'X', 'X'};
ASSERT_EQ(mg_url_decode(in.p, (int) in.len, outbuf, 5, 0), -1);
/* Not enough space to NUL-terminate. */
struct mg_str out1 = MG_MK_STR_N(outbuf, sizeof(outbuf));
ASSERT_MG_STREQ(out1, "a b cX");
}
{
struct mg_str in = MG_MK_STR("a%20b%YYc");
char outbuf[6] = {'X', 'X', 'X', 'X', 'X', 'X'};
struct mg_str out = MG_MK_STR_N(outbuf, sizeof(outbuf)), out1 = out;
ASSERT_EQ(mg_url_decode_n(in, &out, 0), -1);
ASSERT_MG_STREQ(out, "a b");
ASSERT_MG_STREQ(out1, "a bXXX");
}
return NULL;
}
@ -5788,8 +5935,9 @@ const char *tests_run(const char *filter) {
RUN_TEST(test_assemble_uri);
RUN_TEST(test_parse_address);
RUN_TEST(test_mg_normalize_uri_path);
RUN_TEST(test_mg_next_list_entry);
RUN_TEST(test_mg_uri_to_local_path);
RUN_TEST(test_mg_url_encode);
RUN_TEST(test_mg_url_encode_decode);
RUN_TEST(test_check_ip_acl);
RUN_TEST(test_connect_opts);
RUN_TEST(test_connect_opts_error_string);

View File

@ -21,4 +21,4 @@ $ tools/amalgam.py --prefix=MG --public-header=mongoose.h $(cat mongoose.c.manif
The same applies to `mongoose.h`, except `--public-header` should be omitted during amalgamation.
`tools/amalgam.sh` can be used to assemble `mongoose.c` and `mongoose.h`.
`tools/amalgam.py` can be used to assemble `mongoose.c` and `mongoose.h`.