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;
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) {
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);
}
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 *
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.
@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.
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);
}
{ "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 },