]> granicus.if.org Git - libevent/commitdiff
Add evhttp_parse_query_str_flags()
authorAzat Khuzhin <a3at.mail@gmail.com>
Sat, 27 Oct 2018 14:21:35 +0000 (17:21 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Sat, 27 Oct 2018 14:59:11 +0000 (17:59 +0300)
And a set of flags:
- EVHTTP_URI_QUERY_LAST
- EVHTTP_URI_QUERY_NONCONFORMANT

Fixes: #15
http.c
include/event2/http.h
test/regress_http.c

diff --git a/http.c b/http.c
index 181352b63afe2257f39f42b54dd63523af9872e6..cd3f2f050ddf9fc4c9b14bee786402a5a67178a7 100644 (file)
--- 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 *
index ed9acf45ca5de528937bc4c813bac95791aac971..534e604e775120d98845092fdbb0fb08167409ff 100644 (file)
@@ -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.
index a1bd4a8a30b4d8e73b9a987e807d79647ebd5c15..46ecdc21a89e63949d57b2557a0a0479d3f324cb 100644 (file)
@@ -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 },