libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 1e7ad3010da7d2be1afe219da0ba84dd129f9c4e
parent 38b46a2fd1cf24270e6deab2bb64c381938b95a9
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Tue,  6 Dec 2022 23:40:21 +0300

Refactored cookies parsing.

The new code is more compact as duplicated code was removed.
Added testing for various cookies parsing strictness levels.

Diffstat:
Msrc/microhttpd/connection.c | 299+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/testcurl/Makefile.am | 14+++++++++-----
Msrc/testcurl/test_parse_cookies.c | 540++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
3 files changed, 614 insertions(+), 239 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -2829,146 +2829,6 @@ enum _MHD_ParseCookie /** * Parse the cookies string (see RFC 6265). * - * Parsing may fail if the string is not formed strictly as defined by RFC 6265. - * - * @param str the string to parse, without leading whitespaces - * @param str_len the size of the @a str, not including mandatory - * zero-termination - * @param connection the connection to add parsed cookies - * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise - */ -static enum _MHD_ParseCookie -parse_cookies_string_strict (char *str, - size_t str_len, - struct MHD_Connection *connection) -{ - size_t i; - - i = 0; - while (i < str_len) - { - size_t name_start; - size_t name_len; - size_t value_start; - size_t value_len; - bool val_quoted; - /* 'i' must point to the first char of cookie-name */ - name_start = i; - /* Find the end of the cookie-name */ - do - { - const char l = str[i]; - if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) || - (';' == l) || (0 == l)) - break; - } while (str_len > ++i); - if ((str_len == i) || ('=' != str[i]) || (name_start == i)) - return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */ - name_len = i - name_start; - /* 'i' must point to the '=' char */ - mhd_assert ('=' == str[i]); - i++; - /* 'i' must point to the first char of cookie-value */ - if (str_len == i) - { - value_start = 0; - value_len = 0; -#ifdef _DEBUG - val_quoted = false; /* This assignment used in assert */ -#endif - } - else - { - bool valid_cookie; - val_quoted = ('"' == str[i]); - if (val_quoted) - i++; - value_start = i; - /* Find the end of the cookie-value */ - while (str_len > i) - { - const char l = str[i]; - if ((';' == l) || ('"' == l) || (' ' == l) || ('\t' == l) - || (',' == l) || ('\\' == l) || (0 == l)) - break; - i++; - } - value_len = i - value_start; - if (val_quoted) - { - if ((str_len == i) || ('"' != str[i])) - return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */ - i++; - } - if (str_len == i) - valid_cookie = true; - else if (';' == str[i]) - valid_cookie = true; - else if ((' ' == str[i]) || ('\t' == str[i])) - { /* Optional whitespace at the end of the string? */ - while (str_len > ++i) - { - if ((' ' != str[i]) && ('\t' != str[i])) - break; - } - if (str_len == i) - valid_cookie = true; - else - valid_cookie = false; - } - else - valid_cookie = false; - - if (! valid_cookie) - return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */ - } - mhd_assert (0 != name_len); - str[name_start + name_len] = 0; /* Zero-terminate the name */ - if (0 != value_len) - { - mhd_assert (0 == str[i] || ';' == str[i]); - mhd_assert (! val_quoted || ';' == str[i]); - str[value_start + value_len] = 0; /* Zero-terminate the value */ - if (MHD_NO == - MHD_set_connection_value_n_nocheck_ (connection, - MHD_COOKIE_KIND, - str + name_start, - name_len, - str + value_start, - value_len)) - return MHD_PARSE_COOKIE_NO_MEMORY; - } - else - { - if (MHD_NO == - MHD_set_connection_value_n_nocheck_ (connection, - MHD_COOKIE_KIND, - str + name_start, - name_len, - "", - 0)) - return MHD_PARSE_COOKIE_NO_MEMORY; - } - if (str_len > i) - { - mhd_assert (0 == str[i] || ';' == str[i]); - mhd_assert (! val_quoted || ';' == str[i]); - mhd_assert (';' != str[i] || val_quoted || 0 == value_len); - i++; - if (str_len == i) - return MHD_PARSE_COOKIE_MALFORMED; /* No cookie name after semicolon */ - if (' ' != str[i]) - return MHD_PARSE_COOKIE_MALFORMED; /* No space after semicolon */ - i++; - } - } - return MHD_PARSE_COOKIE_OK; -} - - -/** - * Parse the cookies string (see RFC 6265). - * * Try to parse the cookies string even if it is not strictly formed * as specified by RFC 6265. * @@ -2979,12 +2839,22 @@ parse_cookies_string_strict (char *str, * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise */ static enum _MHD_ParseCookie -parse_cookies_string_lenient (char *str, - size_t str_len, - struct MHD_Connection *connection) +parse_cookies_string (char *str, + const size_t str_len, + struct MHD_Connection *connection) { size_t i; bool non_strict; + /* Skip extra whitespaces and empty cookies */ + const bool allow_wsp_empty = (0 >= connection->daemon->strict_for_client); + /* Allow whitespaces around '=' character */ + const bool wsp_around_eq = (0 > connection->daemon->strict_for_client); + /* Allow whitespaces in quoted cookie value */ + const bool wsp_in_quoted = (0 >= connection->daemon->strict_for_client); + /* Allow tab as space after semicolon between cookies */ + const bool tab_as_sp = (0 >= connection->daemon->strict_for_client); + /* Allow no space after semicolon between cookies */ + const bool allow_no_space = (0 >= connection->daemon->strict_for_client); non_strict = false; i = 0; @@ -2998,6 +2868,8 @@ parse_cookies_string_lenient (char *str, /* Skip any whitespaces and empty cookies */ while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) { + if (! allow_wsp_empty) + return MHD_PARSE_COOKIE_MALFORMED; non_strict = true; i++; if (i == str_len) @@ -3017,6 +2889,8 @@ parse_cookies_string_lenient (char *str, /* Skip any whitespaces */ while (str_len > i && (' ' == str[i] || '\t' == str[i])) { + if (! wsp_around_eq) + return MHD_PARSE_COOKIE_MALFORMED; non_strict = true; i++; } @@ -3028,6 +2902,8 @@ parse_cookies_string_lenient (char *str, /* Skip any whitespaces */ while (str_len > i && (' ' == str[i] || '\t' == str[i])) { + if (! wsp_around_eq) + return MHD_PARSE_COOKIE_MALFORMED; non_strict = true; i++; } @@ -3058,6 +2934,8 @@ parse_cookies_string_lenient (char *str, { if (! val_quoted) break; + if (! wsp_in_quoted) + return MHD_PARSE_COOKIE_MALFORMED; non_strict = true; } i++; @@ -3070,10 +2948,19 @@ parse_cookies_string_lenient (char *str, i++; } /* Skip any whitespaces */ - while (str_len > i && (' ' == str[i] || '\t' == str[i])) + if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i]))) { - non_strict = true; - i++; + do + { + i++; + } while (str_len > i && (' ' == str[i] || '\t' == str[i])); + /* Whitespace at the end? */ + if (str_len > i) + { + if (! allow_wsp_empty) + return MHD_PARSE_COOKIE_MALFORMED; + non_strict = true; + } } if (str_len == i) valid_cookie = true; @@ -3118,11 +3005,29 @@ parse_cookies_string_lenient (char *str, mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); i++; if (str_len == i) - non_strict = true; /* No cookie name after semicolon */ + { /* No next cookie after semicolon */ + if (! allow_wsp_empty) + return MHD_PARSE_COOKIE_MALFORMED; + non_strict = true; + } else if (' ' != str[i]) - non_strict = true; /* No space after semicolon */ + {/* No space after semicolon */ + if (('\t' == str[i]) && tab_as_sp) + i++; + else if (! allow_no_space) + return MHD_PARSE_COOKIE_MALFORMED; + non_strict = true; + } else + { i++; + if (str_len == i) + { + if (! allow_wsp_empty) + return MHD_PARSE_COOKIE_MALFORMED; + non_strict = true; + } + } } } return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; @@ -3141,8 +3046,10 @@ parse_cookie_header (struct MHD_Connection *connection) const char *hdr; size_t hdr_len; char *cpy; - bool strict_parsing; size_t i; + enum _MHD_ParseCookie parse_res; + const struct MHD_HTTP_Req_Header *const saved_tail = + connection->rq.headers_received_tail; if (MHD_NO == MHD_lookup_connection_value_n (connection, @@ -3159,25 +3066,61 @@ parse_cookie_header (struct MHD_Connection *connection) cpy = MHD_connection_alloc_memory_ (connection, hdr_len + 1); if (NULL == cpy) - return MHD_PARSE_COOKIE_NO_MEMORY; - - memcpy (cpy, - hdr, - hdr_len); - cpy[hdr_len] = '\0'; + parse_res = MHD_PARSE_COOKIE_NO_MEMORY; + else + { + memcpy (cpy, + hdr, + hdr_len); + cpy[hdr_len] = '\0'; + + i = 0; + /* Skip all initial whitespaces */ + while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i])) + i++; - /* TODO: add individual configuration */ - strict_parsing = (0 < connection->daemon->strict_for_client); - i = 0; - /* Skip all initial whitespaces */ - while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i])) - i++; + parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection); + } - /* 'i' points to the first non-whitespace char or to the end of the string */ - if (strict_parsing) - return parse_cookies_string_strict (cpy + i, hdr_len - i, connection); + switch (parse_res) + { + case MHD_PARSE_COOKIE_OK: + break; + case MHD_PARSE_COOKIE_OK_LAX: +#ifdef HAVE_MESSAGES + if (saved_tail != connection->rq.headers_received_tail) + MHD_DLOG (connection->daemon, + _ ("The Cookie header has been parsed, but it is not fully " + "compliant with the standard.\n")); +#endif /* HAVE_MESSAGES */ + break; + case MHD_PARSE_COOKIE_MALFORMED: +#ifdef HAVE_MESSAGES + if (saved_tail != connection->rq.headers_received_tail) + MHD_DLOG (connection->daemon, + _ ("The Cookie header has been only partially parsed as it " + "contains malformed data.\n")); + else + MHD_DLOG (connection->daemon, + _ ("The Cookie header has malformed data.\n")); +#endif /* HAVE_MESSAGES */ + break; + case MHD_PARSE_COOKIE_NO_MEMORY: +#ifdef HAVE_MESSAGES + MHD_DLOG (connection->daemon, + _ ("Not enough memory in the connection pool to " + "parse client cookies!\n")); +#endif /* HAVE_MESSAGES */ + break; + default: + mhd_assert (0); + break; + } +#ifndef HAVE_MESSAGES + (void) saved_tail; /* Mute compiler warning */ +#endif /* ! HAVE_MESSAGES */ - return parse_cookies_string_lenient (cpy + i, hdr_len - i, connection); + return parse_res; } @@ -3946,37 +3889,13 @@ parse_connection_headers (struct MHD_Connection *connection) size_t val_len; #ifdef COOKIE_SUPPORT - enum _MHD_ParseCookie cookie_res; - - cookie_res = parse_cookie_header (connection); - if (MHD_PARSE_COOKIE_NO_MEMORY == cookie_res) + if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection)) { -#ifdef HAVE_MESSAGES - MHD_DLOG (connection->daemon, - _ ("Not enough memory in pool to parse cookies!\n")); -#endif transmit_error_response_static (connection, MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_TOO_BIG); return; } - else if (MHD_PARSE_COOKIE_OK_LAX == cookie_res) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (connection->daemon, - _ ("The Cookie header has been parsed, but is not fully " - "compliant with the standard.\n")); -#endif - (void) 0; /* Mute compiler warning */ - } - else if (MHD_PARSE_COOKIE_MALFORMED == cookie_res) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (connection->daemon, - _ ("The Cookie header has malformed data.\n")); -#endif - (void) 0; /* Mute compiler warning */ - } #endif /* COOKIE_SUPPORT */ if ( (1 <= connection->daemon->strict_for_client) && (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) && diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am @@ -144,8 +144,9 @@ check_PROGRAMS = \ if ENABLE_COOKIE check_PROGRAMS += \ - test_parse_cookies \ - test_parse_cookies_nonstrict + test_parse_cookies_strict_p1 \ + test_parse_cookies_strict_zero \ + test_parse_cookies_strict_n1 endif if HEAVY_TESTS @@ -467,11 +468,14 @@ test_post_SOURCES = \ test_process_headers_SOURCES = \ test_process_headers.c mhd_has_in_name.h -test_parse_cookies_SOURCES = \ +test_parse_cookies_strict_zero_SOURCES = \ test_parse_cookies.c mhd_has_in_name.h mhd_has_param.h -test_parse_cookies_nonstrict_SOURCES = \ - $(test_parse_cookies_SOURCES) +test_parse_cookies_strict_p1_SOURCES = \ + $(test_parse_cookies_strict_zero_SOURCES) + +test_parse_cookies_strict_n1_SOURCES = \ + $(test_parse_cookies_strict_zero_SOURCES) test_process_arguments_SOURCES = \ test_process_arguments.c mhd_has_in_name.h diff --git a/src/testcurl/test_parse_cookies.c b/src/testcurl/test_parse_cookies.c @@ -189,7 +189,7 @@ _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) /* Could be increased to facilitate debugging */ -#define TIMEOUTS_VAL 5 +#define TIMEOUTS_VAL 500000 #define EXPECTED_URI_BASE_PATH "/" @@ -203,6 +203,9 @@ _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) "<html><head><title>libmicrohttpd test page</title></head>" \ "<body>Success!</body></html>" +#define PAGE_ERROR \ + "<html><body>Cookies parsing error</body></html>" + #ifndef MHD_STATICSTR_LEN_ /** @@ -234,19 +237,293 @@ struct strct_test_data { unsigned int line_num; const char *header_str; - unsigned int num_cookies_non_strict; - unsigned int num_cookies_strict; + unsigned int num_cookies_strict_p2; /* Reserved */ + unsigned int num_cookies_strict_p1; + unsigned int num_cookies_strict_zero; + unsigned int num_cookies_strict_n1; struct strct_cookie cookies[5]; }; static const struct strct_test_data test_data[] = { { __LINE__, + "name1=value1", + 1, + 1, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1;", + 1, + 1, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; ", + 0, + 1, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "; name1=value1", + 0, + 0, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + ";name1=value1", + 0, + 0, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1 ", + 1, + 1, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1 ;", + 0, + 0, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1 ; ", + 0, + 0, + 1, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name2=\"value 2\"", + 0, + 0, + 1, + 1, + { + COOKIE_ ("name2", "value 2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1;\tname2=value2", + 0, + 1, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name1=value1", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2=value2", + 2, + 2, + 2, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, "name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\"; " \ "name5=var_with_=_char", - 5, 0, + 3, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -260,8 +537,10 @@ static const struct strct_test_data test_data[] = { "name1=var1;name2=var2;name3=;" \ "name4=\"var4 with spaces\";" \ "name5=var_with_=_char", - 5, 0, + 1, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -275,8 +554,10 @@ static const struct strct_test_data test_data[] = { "name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\"; " \ "name5=var_with_=_char\t \t", - 5, 0, + 1, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -290,8 +571,10 @@ static const struct strct_test_data test_data[] = { "name1=var1;;name2=var2;;name3=;;" \ "name4=\"var4 with spaces\";;" \ "name5=var_with_=_char;\t \t", - 5, 0, + 1, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -305,14 +588,16 @@ static const struct strct_test_data test_data[] = { "name3=; name1=var1; name2=var2; " \ "name5=var_with_=_char;" \ "name4=\"var4 with spaces\"", - 5, 0, + 4, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), COOKIE_ ("name3", ""), - COOKIE_ ("name4", "var4 with spaces"), - COOKIE_ ("name5", "var_with_=_char") + COOKIE_ ("name5", "var_with_=_char"), + COOKIE_ ("name4", "var4 with spaces") } }, { @@ -320,14 +605,16 @@ static const struct strct_test_data test_data[] = { "name2=var2; name1=var1; " \ "name5=var_with_=_char; name3=; " \ "name4=\"var4 with spaces\";", - 5, 0, + 4, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), COOKIE_ ("name3", ""), - COOKIE_ ("name4", "var4 with spaces"), - COOKIE_ ("name5", "var_with_=_char") + COOKIE_ ("name5", "var_with_=_char"), + COOKIE_ ("name4", "var4 with spaces") } }, { @@ -335,14 +622,16 @@ static const struct strct_test_data test_data[] = { "name2=var2; name1=var1; " \ "name5=var_with_=_char; " \ "name4=\"var4 with spaces\"; name3=", - 5, 0, + 3, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), + COOKIE_ ("name5", "var_with_=_char"), COOKIE_ ("name3", ""), - COOKIE_ ("name4", "var4 with spaces"), - COOKIE_ ("name5", "var_with_=_char") + COOKIE_ ("name4", "var4 with spaces") } }, { @@ -350,8 +639,10 @@ static const struct strct_test_data test_data[] = { "name2=var2; name1=var1; " \ "name4=\"var4 with spaces\"; " \ "name5=var_with_=_char; name3=;", - 5, 0, + 2, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -365,8 +656,10 @@ static const struct strct_test_data test_data[] = { ";;;;;;;;name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\"; " \ "name5=var_with_=_char", - 5, 0, + 0, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -380,8 +673,10 @@ static const struct strct_test_data test_data[] = { "name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\"; ; ; ; ; " \ "name5=var_with_=_char", - 5, 0, + 3, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -395,8 +690,10 @@ static const struct strct_test_data test_data[] = { "name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\"; " \ "name5=var_with_=_char;;;;;;;;", - 5, 0, + 3, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -408,16 +705,18 @@ static const struct strct_test_data test_data[] = { { __LINE__, "name1=var1; name2=var2; " \ - "name4=\"var4 with spaces\"" \ + "name4=\"var4 with spaces\";" \ "name5=var_with_=_char; ; ; ; ; name3=", - 5, 0, + 2, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), + COOKIE_ ("name5", "var_with_=_char"), COOKIE_ ("name3", ""), - COOKIE_ ("name4", "var4 with spaces"), - COOKIE_ ("name5", "var_with_=_char") + COOKIE_ ("name4", "var4 with spaces") } }, { @@ -425,8 +724,10 @@ static const struct strct_test_data test_data[] = { "name5=var_with_=_char ;" \ "name1=var1; name2=var2; name3=; " \ "name4=\"var4 with spaces\" ", - 5, 0, + 0, + 5, + 5, { COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), @@ -439,14 +740,106 @@ static const struct strct_test_data test_data[] = { __LINE__, "name5=var_with_=_char; name4=\"var4 with spaces\";" \ "name1=var1; name2=var2; name3=", - 5, 0, + 1, + 5, + 5, { + COOKIE_ ("name5", "var_with_=_char"), COOKIE_ ("name1", "var1"), COOKIE_ ("name2", "var2"), COOKIE_ ("name3", ""), - COOKIE_ ("name4", "var4 with spaces"), - COOKIE_ ("name5", "var_with_=_char") + COOKIE_ ("name4", "var4 with spaces") + } + }, + { + __LINE__, + "name1 = value1", + 0, + 0, + 0, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1\t=\tvalue1", + 0, + 0, + 0, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1\t = \tvalue1", + 0, + 0, + 0, + 1, + { + COOKIE_ ("name1", "value1"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1 = value1; name2 =\tvalue2", + 0, + 0, + 0, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1=value1; name2 =\tvalue2", + 0, + 1, + 1, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL + } + }, + { + __LINE__, + "name1 = value1; name2=value2", + 0, + 0, + 0, + 2, + { + COOKIE_ ("name1", "value1"), + COOKIE_ ("name2", "value2"), + COOKIE_NULL, + COOKIE_NULL, + COOKIE_NULL } }, { @@ -454,6 +847,8 @@ static const struct strct_test_data test_data[] = { "", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -467,6 +862,8 @@ static const struct strct_test_data test_data[] = { " ", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -480,6 +877,8 @@ static const struct strct_test_data test_data[] = { "\t", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -493,6 +892,8 @@ static const struct strct_test_data test_data[] = { "var=,", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -506,6 +907,8 @@ static const struct strct_test_data test_data[] = { "var=\"\\ \"", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -519,6 +922,8 @@ static const struct strct_test_data test_data[] = { "var=value space", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -532,6 +937,8 @@ static const struct strct_test_data test_data[] = { "var=value\ttab", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -545,6 +952,8 @@ static const struct strct_test_data test_data[] = { "=", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -558,6 +967,8 @@ static const struct strct_test_data test_data[] = { "====", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -571,6 +982,8 @@ static const struct strct_test_data test_data[] = { ";=", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -584,6 +997,8 @@ static const struct strct_test_data test_data[] = { "var", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -597,6 +1012,8 @@ static const struct strct_test_data test_data[] = { "=;", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -610,6 +1027,8 @@ static const struct strct_test_data test_data[] = { "= ;", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -623,6 +1042,8 @@ static const struct strct_test_data test_data[] = { ";= ;", 0, 0, + 0, + 0, { COOKIE_NULL, COOKIE_NULL, @@ -636,7 +1057,10 @@ static const struct strct_test_data test_data[] = { /* Global parameters */ static int verbose; static int oneone; /**< If false use HTTP/1.0 for requests*/ -static int use_non_strict; +static int use_strict_n1; +static int use_strict_zero; +static int use_strict_p1; +static int strict_level; static void test_global_init (void) @@ -700,12 +1124,19 @@ ahcCheck (void *cls, struct MHD_Response *response; enum MHD_Result ret; struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; - const unsigned int expected_num_cookies = - use_non_strict ? param->check->num_cookies_non_strict : - param->check->num_cookies_strict; + unsigned int expected_num_cookies; unsigned int i; int cookie_failed; + if (use_strict_p1) + expected_num_cookies = param->check->num_cookies_strict_p1; + else if (use_strict_zero) + expected_num_cookies = param->check->num_cookies_strict_zero; + else if (use_strict_n1) + expected_num_cookies = param->check->num_cookies_strict_n1; + else + externalErrorExit (); + if (NULL == param) mhdErrorExitDesc ("cls parameter is NULL"); @@ -813,7 +1244,17 @@ ahcCheck (void *cls, cookie_failed = 1; } if (cookie_failed) - return MHD_NO; /* Break connection */ + { + response = + MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE_ERROR), + PAGE_ERROR); + ret = MHD_queue_response (connection, + MHD_HTTP_BAD_REQUEST, + response); + MHD_destroy_response (response); + + return ret; + } if (&marker != *req_cls) { @@ -1057,7 +1498,6 @@ check_result (CURLcode curl_code, CURL *c, long expected_code, struct CBC *pcbc) { long code; - unsigned int ret; if (CURLE_OK != curl_code) { @@ -1079,15 +1519,15 @@ check_result (CURLcode curl_code, CURL *c, long expected_code, if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code)) libcurlErrorExit (); - ret = 1; if (expected_code != code) { - fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n", + fprintf (stderr, "### The response has wrong HTTP code: %ld\t" + "Expected: %ld.\n", code, expected_code); - ret = 0; + return 0; } else if (verbose) - printf ("The response has expected HTTP code: %ld\n", expected_code); + printf ("### The response has expected HTTP code: %ld\n", expected_code); if (pcbc->pos != MHD_STATICSTR_LEN_ (PAGE)) { @@ -1105,7 +1545,7 @@ check_result (CURLcode curl_code, CURL *c, long expected_code, fflush (stderr); fflush (stdout); - return ret; + return 1; } @@ -1125,13 +1565,13 @@ testExternalPolling (void) if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) port = 0; else - port = 1340 + oneone ? 0 : 1 + use_non_strict ? 0 : 2; + port = 1340 + oneone ? 0 : 6 + (uint16_t) (1 + strict_level); d = MHD_start_daemon (MHD_USE_ERROR_LOG, port, NULL, NULL, &ahcCheck, &ahc_param, MHD_OPTION_STRICT_FOR_CLIENT, - (int) (use_non_strict ? 0 : 1), + (int) (strict_level), MHD_OPTION_END); if (d == NULL) return 1; @@ -1167,13 +1607,13 @@ testExternalPolling (void) MHD_HTTP_OK, &cbc)) { if (verbose) - printf ("Got expected response for the check at line %u.\n", + printf ("### Got expected response for the check at line %u.\n", test_data[i].line_num); fflush (stdout); } else { - fprintf (stderr, "FAILED request for the check at line %u.\n", + fprintf (stderr, "### FAILED request for the check at line %u.\n", test_data[i].line_num); fflush (stderr); failed = 1; @@ -1200,7 +1640,19 @@ main (int argc, char *const *argv) has_param (argc, argv, "-s") || has_param (argc, argv, "--silent")); oneone = ! has_in_name (argv[0], "10"); - use_non_strict = has_in_name (argv[0], "_nonstrict"); + use_strict_n1 = has_in_name (argv[0], "_strict_n1"); + use_strict_zero = has_in_name (argv[0], "_strict_zero"); + use_strict_p1 = has_in_name (argv[0], "_strict_p1"); + if (1 != ((use_strict_n1 ? 1 : 0) + (use_strict_zero ? 1 : 0) + + (use_strict_p1 ? 1 : 0))) + return 99; + + if (use_strict_n1) + strict_level = -1; + else if (use_strict_zero) + strict_level = 0; + else if (use_strict_p1) + strict_level = 1; test_global_init ();