Skip to content

Commit

Permalink
Add evhttp_parse_query_str_flags()
Browse files Browse the repository at this point in the history
And a set of flags:
- EVHTTP_URI_QUERY_LAST
- EVHTTP_URI_QUERY_NONCONFORMANT

Fixes: libevent#15
  • Loading branch information
azat committed Oct 27, 2018
1 parent d161ec3 commit 26ef859
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
23 changes: 18 additions & 5 deletions http.c
Original file line number Diff line number Diff line change
Expand Up @@ -3216,7 +3216,7 @@ evhttp_uridecode(const char *uri, int decode_plus, size_t *size_out)

static int
evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
int is_whole_uri)
int is_whole_uri, unsigned flags)
{
char *line=NULL;
char *argument;
Expand Down Expand Up @@ -3254,8 +3254,14 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,

value = argument;
key = strsep(&value, "=");
if (value == NULL || *key == '\0') {
goto error;
if (flags & EVHTTP_URI_QUERY_NONCONFORMANT) {
if (value == NULL)
value = "";
if (*key == '\0')
continue;
} else {
if (value == NULL || *key == '\0')
goto error;
}

if ((decoded_value = mm_malloc(strlen(value) + 1)) == NULL) {
Expand All @@ -3265,6 +3271,8 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
evhttp_decode_uri_internal(value, strlen(value),
decoded_value, 1 /*always_decode_plus*/);
event_debug(("Query Param: %s -> %s\n", key, decoded_value));
if (flags & EVHTTP_URI_QUERY_LAST_VAL)
evhttp_remove_header(headers, key);
evhttp_add_header_internal(headers, key, decoded_value);
mm_free(decoded_value);
}
Expand All @@ -3284,12 +3292,17 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
int
evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
{
return evhttp_parse_query_impl(uri, headers, 1);
return evhttp_parse_query_impl(uri, headers, 1, 0);
}
int
evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers)
{
return evhttp_parse_query_impl(uri, headers, 0);
return evhttp_parse_query_impl(uri, headers, 0, 0);
}
int
evhttp_parse_query_str_flags(const char *uri, struct evkeyvalq *headers, unsigned flags)
{
return evhttp_parse_query_impl(uri, headers, 0, flags);
}

static struct evhttp_cb *
Expand Down
30 changes: 29 additions & 1 deletion include/event2/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,33 @@ char *evhttp_uridecode(const char *uri, int decode_plus,
EVENT2_EXPORT_SYMBOL
int evhttp_parse_query(const char *uri, struct evkeyvalq *headers);

/** @see evhttp_parse_query_str_flags() */
EVENT2_EXPORT_SYMBOL
int evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers);

/** Tolerate queries that are not standard conformant.
*
* Here are some examples:
*
* - test=123&test2
* with with this flag test2 will be present in the output headers
*
* - test=123&&test2=1
* will parse the query with this flag
*
* - test=123&=456&test2=1
* will parse the queyr with this flag, however there won't be empty key
* present
*/
#define EVHTTP_URI_QUERY_NONCONFORMANT 0x01
/** Prefer last value over the first from query args
*
* Example: test=123&test=456
* Without: test=123
* With : test=456
*/
#define EVHTTP_URI_QUERY_LAST_VAL 0x02

/**
Helper function to parse out arguments from the query portion of an
HTTP URI.
Expand All @@ -1019,10 +1046,11 @@ int evhttp_parse_query(const char *uri, struct evkeyvalq *headers);
@param query_parse the query portion of the URI
@param headers the head of the evkeyval queue
@param flags one or more of EVHTTP_URI_QUERY_*
@return 0 on success, -1 on failure
*/
EVENT2_EXPORT_SYMBOL
int evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers);
int evhttp_parse_query_str_flags(const char *uri, struct evkeyvalq *headers, unsigned flags);

/**
* Escape HTML character entities in a string.
Expand Down
57 changes: 57 additions & 0 deletions test/regress_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,62 @@ http_parse_query_str_test(void *ptr)
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);

end:
evhttp_clear_headers(&headers);
}
static void
http_parse_query_str_flags_test(void *ptr)
{
struct evkeyvalq headers;
int r;

TAILQ_INIT(&headers);

/** ~EVHTTP_URI_QUERY_LAST_VAL */
r = evhttp_parse_query_str_flags("q=test&q=test2", &headers, 0);
tt_want(validate_header(&headers, "q", "test") == 0);
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);

/** EVHTTP_URI_QUERY_LAST_VAL */
r = evhttp_parse_query_str_flags("q=test&q=test2", &headers, EVHTTP_URI_QUERY_LAST_VAL);
tt_want(validate_header(&headers, "q", "test2") == 0);
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);

/** ~EVHTTP_URI_QUERY_NONCONFORMANT */
r = evhttp_parse_query_str_flags("q=test&q2", &headers, 0);
tt_int_op(r, ==, -1);
evhttp_clear_headers(&headers);

r = evhttp_parse_query_str_flags("q=test&&q2=test2", &headers, 0);
tt_int_op(r, ==, -1);
evhttp_clear_headers(&headers);

r = evhttp_parse_query_str_flags("q=test&=1&q2=test2", &headers, 0);
tt_int_op(r, ==, -1);
evhttp_clear_headers(&headers);

/** EVHTTP_URI_QUERY_NONCONFORMANT */
r = evhttp_parse_query_str_flags("q=test&q2", &headers, EVHTTP_URI_QUERY_NONCONFORMANT);
tt_want(validate_header(&headers, "q", "test") == 0);
tt_want(validate_header(&headers, "q2", "") == 0);
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);

r = evhttp_parse_query_str_flags("q=test&&q2=test2", &headers, EVHTTP_URI_QUERY_NONCONFORMANT);
tt_want(validate_header(&headers, "q", "test") == 0);
tt_want(validate_header(&headers, "q2", "test2") == 0);
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);

r = evhttp_parse_query_str_flags("q=test&=1&q2=test2", &headers, EVHTTP_URI_QUERY_NONCONFORMANT);
tt_want(validate_header(&headers, "q", "test") == 0);
tt_want(validate_header(&headers, "q2", "test2") == 0);
tt_int_op(r, ==, 0);
evhttp_clear_headers(&headers);


end:
evhttp_clear_headers(&headers);
}
Expand Down Expand Up @@ -4826,6 +4882,7 @@ struct testcase_t http_testcases[] = {
{ "bad_headers", http_bad_header_test, 0, NULL, NULL },
{ "parse_query", http_parse_query_test, 0, NULL, NULL },
{ "parse_query_str", http_parse_query_str_test, 0, NULL, NULL },
{ "parse_query_str_flags", http_parse_query_str_flags_test, 0, NULL, NULL },
{ "parse_uri", http_parse_uri_test, 0, NULL, NULL },
{ "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" },
{ "uriencode", http_uriencode_test, 0, NULL, NULL },
Expand Down

0 comments on commit 26ef859

Please sign in to comment.