From 26ef859aa707fda72ce530bf761d1c41a76f67b7 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 27 Oct 2018 17:21:35 +0300 Subject: [PATCH] Add evhttp_parse_query_str_flags() And a set of flags: - EVHTTP_URI_QUERY_LAST - EVHTTP_URI_QUERY_NONCONFORMANT Fixes: #15 --- http.c | 23 +++++++++++++---- include/event2/http.h | 30 ++++++++++++++++++++++- test/regress_http.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 6 deletions(-) diff --git a/http.c b/http.c index 181352b6..cd3f2f05 100644 --- a/http.c +++ b/http.c @@ -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; @@ -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) { @@ -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); } @@ -3284,12 +3292,17 @@ done: 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 * diff --git a/include/event2/http.h b/include/event2/http.h index ed9acf45..534e604e 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -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. @@ -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. diff --git a/test/regress_http.c b/test/regress_http.c index a1bd4a8a..46ecdc21 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -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); } @@ -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 }, -- 2.40.0