gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (3444792f -> 1e7ad301)


From: gnunet
Subject: [libmicrohttpd] branch master updated (3444792f -> 1e7ad301)
Date: Mon, 19 Dec 2022 16:17:57 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 3444792f Refactored user-poison: minimized scope of non-sanitized code
     new e4db5b26 response.c: added new internal function to avoid repetitive 
malloc()s
     new 784b8029 digestauth: avoid malloc() repeat by using the new function
     new c47bb426 parse_http_version(): cosmetics
     new 2a71dbd3 MHD_get_version_bin(): added new function
     new 38b46a2f test_parse_cookies: rewritten
     new 1e7ad301 Refactored cookies parsing.

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/include/microhttpd.h          |   14 +-
 src/microhttpd/connection.c       |  321 +++----
 src/microhttpd/daemon.c           |   15 +
 src/microhttpd/digestauth.c       |   29 +-
 src/microhttpd/response.c         |   81 +-
 src/microhttpd/response.h         |   27 +
 src/testcurl/Makefile.am          |   16 +-
 src/testcurl/test_parse_cookies.c | 1873 +++++++++++++++++++++++++++++--------
 8 files changed, 1768 insertions(+), 608 deletions(-)

diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 0eae3f9f..b66f4b3d 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097543
+#define MHD_VERSION 0x00097544
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -5935,6 +5935,18 @@ _MHD_EXTERN const char *
 MHD_get_version (void);
 
 
+/**
+ * Obtain the version of this library as a binary value.
+ *
+ * @return version binary value, e.g. "0x00090900" (#MHD_VERSION of
+ *         compiled MHD binary)
+ * @note Available since #MHD_VERSION 0x00097544
+ * @ingroup specialized
+ */
+_MHD_EXTERN uint32_t
+MHD_get_version_bin (void);
+
+
 /**
  * Types of information about MHD features,
  * used by #MHD_is_feature_supported().
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 19d1c699..16c5fb93 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -2826,146 +2826,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).
  *
@@ -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,32 +3066,74 @@ 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;
 }
 
 
 #endif /* COOKIE_SUPPORT */
 
+
 /**
- * Detect HTTP version
+ * The valid length of any HTTP version string
+ */
+#define HTTP_VER_LEN (MHD_STATICSTR_LEN_(MHD_HTTP_VERSION_1_1))
+
+/**
+ * Detect HTTP version, send error response if version is not supported
  *
  * @param connection the connection
  * @param http_string the pointer to HTTP version string
@@ -3201,13 +3150,13 @@ parse_http_version (struct MHD_Connection *connection,
   mhd_assert (NULL != http_string);
 
   /* String must start with 'HTTP/d.d', case-sensetive match.
-   * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
-  if ((len != 8) ||
-      (h[0] != 'H') || (h[1] != 'T') || (h[2] != 'T') || (h[3] != 'P') ||
-      (h[4] != '/')
-      || (h[6] != '.') ||
-      ((h[5] < '0') || (h[5] > '9')) ||
-      ((h[7] < '0') || (h[7] > '9')))
+   * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
+  if ((HTTP_VER_LEN != len) ||
+      ('H' != h[0] ) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
+      ('/' != h[4])
+      || ('.' != h[6]) ||
+      (('0' > h[5]) || ('9' < h[5])) ||
+      (('0' > h[7]) || ('9' < h[7])))
   {
     connection->rq.http_ver = MHD_HTTP_VER_INVALID;
     transmit_error_response_static (connection,
@@ -3940,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/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 53ab147f..d854de31 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -8576,6 +8576,21 @@ MHD_get_version (void)
 }
 
 
+/**
+ * Obtain the version of this library as a binary value.
+ *
+ * @return version binary value, e.g. "0x00090900" (#MHD_VERSION of
+ *         compiled MHD binary)
+ * @note Available since #MHD_VERSION 0x00097544
+ * @ingroup specialized
+ */
+_MHD_EXTERN uint32_t
+MHD_get_version_bin (void)
+{
+  return (uint32_t) MHD_VERSION;
+}
+
+
 /**
  * Get information about supported MHD features.
  * Indicate that MHD was compiled with or without support for
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 926ac5f4..5505118c 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -3507,6 +3507,7 @@ queue_auth_required_response3_inner (struct 
MHD_Connection *connection,
   size_t buf_size;
   char *buf;
   size_t p; /* The position in the buffer */
+  char *hdr_name;
 
   if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
   {
@@ -3836,24 +3837,34 @@ queue_auth_required_response3_inner (struct 
MHD_Connection *connection,
     buf[p++] = ' ';
   }
   mhd_assert (buf_size >= p);
-  /* The build string ends with ", ". Replace comma with zero-termination. */
+  /* The built string ends with ", ". Replace comma with zero-termination. */
   --p;
   buf[--p] = 0;
 
-  if (! MHD_add_response_entry_no_check_ (response, MHD_HEADER_KIND,
-                                          MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+  hdr_name = malloc (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 
1);
+  if (NULL != hdr_name)
+  {
+    memcpy (hdr_name, MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+            MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1);
+    if (MHD_add_response_entry_no_alloc_ (response, MHD_HEADER_KIND,
+                                          hdr_name,
                                           MHD_STATICSTR_LEN_ ( \
                                             MHD_HTTP_HEADER_WWW_AUTHENTICATE),
                                           buf, p))
-  {
+    {
+      *buf_ptr = NULL; /* The buffer will be free()ed when the response is 
destroyed */
+      return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
+    }
 #ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("Failed to add Digest auth header.\n"));
+    else
+    {
+      MHD_DLOG (connection->daemon,
+                _ ("Failed to add Digest auth header.\n"));
+    }
 #endif /* HAVE_MESSAGES */
-    return MHD_NO;
+    free (hdr_name);
   }
-
-  return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
+  return MHD_NO;
 }
 
 
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index d352a804..b29f48f3 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -144,6 +144,51 @@
   } \
 } while (0)
 
+/**
+ * Add preallocated strings a header or footer line to the response without
+ * checking.
+ *
+ * Header/footer strings are not checked and assumed to be correct.
+ *
+ * The string must not be statically allocated!
+ * The strings must be malloc()'ed and zero terminated. The strings will
+ * be free()'ed when the response is destroyed.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header string to add, must be malloc()'ed and
+ *               zero-terminated
+ * @param header_len the length of the @a header
+ * @param content the value string to add, must be malloc()'ed and
+ *                zero-terminated
+ * @param content_len the length of the @a content
+ */
+bool
+MHD_add_response_entry_no_alloc_ (struct MHD_Response *response,
+                                  enum MHD_ValueKind kind,
+                                  char *header,
+                                  size_t header_len,
+                                  char *content,
+                                  size_t content_len)
+{
+  struct MHD_HTTP_Res_Header *hdr;
+
+  mhd_assert (0 != header_len);
+  mhd_assert (0 != content_len);
+  if (NULL == (hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Res_Header))))
+    return false;
+
+  hdr->header = header;
+  hdr->header_size = header_len;
+  hdr->value = content;
+  hdr->value_size = content_len;
+  hdr->kind = kind;
+  _MHD_insert_header_last (response, hdr);
+
+  return true; /* Success exit point */
+}
+
+
 /**
  * Add a header or footer line to the response without checking.
  *
@@ -166,34 +211,32 @@ MHD_add_response_entry_no_check_ (struct MHD_Response 
*response,
                                   const char *content,
                                   size_t content_len)
 {
-  struct MHD_HTTP_Res_Header *hdr;
+  char *header_malloced;
+  char *value_malloced;
 
   mhd_assert (0 != header_len);
   mhd_assert (0 != content_len);
-  if (NULL == (hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Res_Header))))
+  header_malloced = malloc (header_len + 1);
+  if (NULL == header_malloced)
     return false;
-  hdr->header = malloc (header_len + 1);
-  if (NULL != hdr->header)
-  {
-    memcpy (hdr->header, header, header_len);
-    hdr->header[header_len] = 0;
-    hdr->header_size = header_len;
 
-    hdr->value = malloc (content_len + 1);
-    if (NULL != hdr->value)
-    {
-      memcpy (hdr->value, content, content_len);
-      hdr->value[content_len] = 0;
-      hdr->value_size = content_len;
+  memcpy (header_malloced, header, header_len);
+  header_malloced[header_len] = 0;
 
-      hdr->kind = kind;
-      _MHD_insert_header_last (response, hdr);
+  value_malloced = malloc (content_len + 1);
+  if (NULL != value_malloced)
+  {
+    memcpy (value_malloced, content, content_len);
+    value_malloced[content_len] = 0;
 
+    if (MHD_add_response_entry_no_alloc_ (response, kind,
+                                          header_malloced, header_len,
+                                          value_malloced, content_len))
       return true; /* Success exit point */
-    }
-    free (hdr->header);
+
+    free (value_malloced);
   }
-  free (hdr);
+  free (header_malloced);
 
   return false; /* Failure exit point */
 }
diff --git a/src/microhttpd/response.h b/src/microhttpd/response.h
index 4f9c32c9..ad614c1b 100644
--- a/src/microhttpd/response.h
+++ b/src/microhttpd/response.h
@@ -95,4 +95,31 @@ MHD_add_response_entry_no_check_ (struct MHD_Response 
*response,
                                   const char *content,
                                   size_t content_len);
 
+/**
+ * Add preallocated strings a header or footer line to the response without
+ * checking.
+ *
+ * Header/footer strings are not checked and assumed to be correct.
+ *
+ * The string must not be statically allocated!
+ * The strings must be malloc()'ed and zero terminated. The strings will
+ * be free()'ed when the response is destroyed.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header string to add, must be malloc()'ed and
+ *               zero-terminated
+ * @param header_len the length of the @a header
+ * @param content the value string to add, must be malloc()'ed and
+ *                zero-terminated
+ * @param content_len the length of the @a content
+ */
+bool
+MHD_add_response_entry_no_alloc_ (struct MHD_Response *response,
+                                  enum MHD_ValueKind kind,
+                                  char *header,
+                                  size_t header_len,
+                                  char *content,
+                                  size_t content_len);
+
 #endif
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index cc685d3b..0d190f9d 100644
--- 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_invalid
+  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.c mhd_has_in_name.h
+test_parse_cookies_strict_zero_SOURCES = \
+  test_parse_cookies.c mhd_has_in_name.h mhd_has_param.h
 
-test_parse_cookies_invalid_SOURCES = \
-  test_parse_cookies.c mhd_has_in_name.h
+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
index d02a2b76..308b751e 100644
--- a/src/testcurl/test_parse_cookies.c
+++ b/src/testcurl/test_parse_cookies.c
@@ -1,14 +1,14 @@
 /*
-     This file is part of libmicrohttpd
+     This file is part of GNU libmicrohttpd
      Copyright (C) 2007 Christian Grothoff
-     Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
+     Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
 
-     libmicrohttpd is free software; you can redistribute it and/or modify
+     GNU libmicrohttpd is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
+     by the Free Software Foundation; either version 2, or (at your
      option) any later version.
 
-     libmicrohttpd is distributed in the hope that it will be useful, but
+     GNU libmicrohttpd is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      General Public License for more details.
@@ -22,522 +22,1645 @@
 /**
  * @file test_parse_cookies.c
  * @brief  Testcase for HTTP cookie parsing
- * @author Christian Grothoff
  * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
  */
 
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <time.h>
-#include "mhd_has_in_name.h"
 
-#ifndef WINDOWS
+#ifndef _WIN32
+#include <sys/socket.h>
 #include <unistd.h>
 #endif
 
-static int use_invalid;
+#include "mhd_has_param.h"
+#include "mhd_has_in_name.h"
 
-struct CBC
+#ifndef MHD_STATICSTR_LEN_
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! MHD_STATICSTR_LEN_ */
+
+#ifndef CURL_VERSION_BITS
+#define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
+#endif /* ! CURL_VERSION_BITS */
+#ifndef CURL_AT_LEAST_VERSION
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+  (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
+#endif /* ! CURL_AT_LEAST_VERSION */
+
+#ifndef _MHD_INSTRMACRO
+/* Quoted macro parameter */
+#define _MHD_INSTRMACRO(a) #a
+#endif /* ! _MHD_INSTRMACRO */
+#ifndef _MHD_STRMACRO
+/* Quoted expanded macro parameter */
+#define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
+#endif /* ! _MHD_STRMACRO */
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+  _externalErrorExit_func (NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+  _externalErrorExit_func (errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+  _libcurlErrorExit_func (NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+  _libcurlErrorExit_func (errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+  _mhdErrorExit_func (NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+  _mhdErrorExit_func (errDesc, __func__, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+  _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
+                       __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+  _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+  _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+  _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+  _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+  _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+  _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+  _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
+                       __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, 
__LINE__)
+#define externalErrorExitDesc(errDesc) \
+  _externalErrorExit_func (errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+  _libcurlErrorExit_func (errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+  _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
+                       __LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int 
lineNum)
 {
-  char *buf;
-  size_t pos;
-  size_t size;
-};
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "System or external library call failed");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
 
-static size_t
-copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+  fflush (stderr);
+  exit (99);
+}
+
+
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+_MHD_NORETURN static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
 {
-  struct CBC *cbc = ctx;
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "CURL library call failed");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
 
-  if (cbc->pos + size * nmemb > cbc->size)
-    return 0;                   /* overflow */
-  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
-  cbc->pos += size * nmemb;
-  return size * nmemb;
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+  if (0 != libcurl_errbuf[0])
+    fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
+
+  fflush (stderr);
+  exit (99);
 }
 
 
-static enum MHD_Result
-ahc_echo (void *cls,
-          struct MHD_Connection *connection,
-          const char *url,
-          const char *method,
-          const char *version,
-          const char *upload_data, size_t *upload_data_size,
-          void **req_cls)
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
 {
-  static int ptr;
-  const int *puse_invalid = cls;
-  struct MHD_Response *response;
-  enum MHD_Result ret;
-  const char *hdr;
-  (void) version; (void) upload_data; (void) upload_data_size;       /* 
Unused. Silent compiler warning. */
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "MHD unexpected error");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
 
-  if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
-    return MHD_NO;              /* unexpected method */
-  if (&ptr != *req_cls)
-  {
-    *req_cls = &ptr;
-    return MHD_YES;
-  }
-  *req_cls = NULL;
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
 
-  if (! *puse_invalid)
+  fflush (stderr);
+  exit (8);
+}
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 500000
+
+#define EXPECTED_URI_BASE_PATH  "/"
+
+#define URL_SCHEME "http:/" "/"
+
+#define URL_HOST "127.0.0.1"
+
+#define URL_SCHEME_HOST URL_SCHEME URL_HOST
+
+#define PAGE \
+  "<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_
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! MHD_STATICSTR_LEN_ */
+
+
+struct strct_str_len
+{
+  const char *str;
+  const size_t len;
+};
+
+#define STR_LEN_(str)   {str, MHD_STATICSTR_LEN_(str)}
+#define STR_NULL_       {NULL, 0}
+
+struct strct_cookie
+{
+  struct strct_str_len name;
+  struct strct_str_len value;
+};
+
+#define COOKIE_(name,value)     {STR_LEN_(name), STR_LEN_(value)}
+#define COOKIE_NULL             {STR_NULL_, STR_NULL_}
+
+struct strct_test_data
+{
+  unsigned int line_num;
+  const char *header_str;
+  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[] = {
   {
-    hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name1");
-    if ((hdr == NULL) || (0 != strcmp (hdr, "var1")))
+    __LINE__,
+    "name1=value1",
+    1,
+    1,
+    1,
+    1,
     {
-      fprintf (stderr, "'name1' cookie decoded incorrectly.\n");
-      exit (11);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name2");
-    if ((hdr == NULL) || (0 != strcmp (hdr, "var2")))
+  },
+  {
+    __LINE__,
+    "name1=value1;",
+    1,
+    1,
+    1,
+    1,
     {
-      fprintf (stderr, "'name2' cookie decoded incorrectly.\n");
-      exit (11);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name3");
-    if ((hdr == NULL) || (0 != strcmp (hdr, "")))
+  },
+  {
+    __LINE__,
+    "name1=value1; ",
+    0,
+    1,
+    1,
+    1,
     {
-      fprintf (stderr, "'name3' cookie decoded incorrectly.\n");
-      exit (11);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name4");
-    if ((hdr == NULL) || (0 != strcmp (hdr, "var4 with spaces")))
+  },
+  {
+    __LINE__,
+    "; name1=value1",
+    0,
+    0,
+    1,
+    1,
     {
-      fprintf (stderr, "'name4' cookie decoded incorrectly.\n");
-      exit (11);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name5");
-    if ((hdr == NULL) || (0 != strcmp (hdr, "var_with_=_char")))
+  },
+  {
+    __LINE__,
+    ";name1=value1",
+    0,
+    0,
+    1,
+    1,
     {
-      fprintf (stderr, "'name5' cookie decoded incorrectly.\n");
-      exit (11);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    if (5 != MHD_get_connection_values_n (connection, MHD_COOKIE_KIND,
-                                          NULL, NULL))
+  },
+  {
+    __LINE__,
+    "name1=value1 ",
+    1,
+    1,
+    1,
+    1,
     {
-      fprintf (stderr, "The total number of cookie is not five.\n");
-      exit (12);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-  }
-  else
+  },
   {
-    if (0 != MHD_get_connection_values_n (connection, MHD_COOKIE_KIND,
-                                          NULL, NULL))
+    __LINE__,
+    "name1=value1 ;",
+    0,
+    0,
+    1,
+    1,
     {
-      fprintf (stderr, "The total number of cookie is not zero.\n");
-      exit (12);
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-  }
-  response = MHD_create_response_from_buffer_copy (strlen (url),
-                                                   url);
-  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
-  MHD_destroy_response (response);
-  if (ret == MHD_NO)
-    abort ();
-  return ret;
-}
-
-
-/* Re-use the same port for all checks */
-static uint16_t port;
-
-static unsigned int
-testExternalGet (int test_number)
-{
-  struct MHD_Daemon *d;
-  CURL *c;
-  char buf[2048];
-  struct CBC cbc;
-  CURLM *multi;
-  CURLMcode mret;
-  fd_set rs;
-  fd_set ws;
-  fd_set es;
-  MHD_socket maxsock;
-#ifdef MHD_WINSOCK_SOCKETS
-  int maxposixs; /* Max socket number unused on W32 */
-#else  /* MHD_POSIX_SOCKETS */
-#define maxposixs maxsock
-#endif /* MHD_POSIX_SOCKETS */
-  int running;
-  struct CURLMsg *msg;
-  time_t start;
-  struct timeval tv;
-
-  multi = NULL;
-  cbc.buf = buf;
-  cbc.size = 2048;
-  cbc.pos = 0;
-  d = MHD_start_daemon (MHD_USE_ERROR_LOG,
-                        port, NULL, NULL, &ahc_echo, &use_invalid,
-                        MHD_OPTION_END);
-  if (d == NULL)
-    return 256;
-  if (0 == port)
+  },
   {
-    const union MHD_DaemonInfo *dinfo;
-    dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
-    if ((NULL == dinfo) || (0 == dinfo->port) )
+    __LINE__,
+    "name1=value1 ; ",
+    0,
+    0,
+    1,
+    1,
     {
-      MHD_stop_daemon (d); return 32;
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    port = dinfo->port;
-  }
-  c = curl_easy_init ();
-  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world";);
-  curl_easy_setopt (c, CURLOPT_PORT, (long) port);
-  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
-  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
-  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
-  if (! use_invalid)
+  },
   {
-    if (0 == test_number)
+    __LINE__,
+    "name2=\"value 2\"",
+    0,
+    0,
+    1,
+    1,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1; name2=var2; name3=; " \
-                        "name4=\"var4 with spaces\"; " \
-                        "name5=var_with_=_char");
+      COOKIE_ ("name2", "value 2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (1 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1;\tname2=value2",
+    0,
+    1,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1;name2=var2;name3=;" \
-                        "name4=\"var4 with spaces\";" \
-                        "name5=var_with_=_char");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (2 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name1=value1",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1;  name2=var2;  name3=;  " \
-                        "name4=\"var4 with spaces\";  " \
-                        "name5=var_with_=_char\t \t");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (3 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1;;name2=var2;;name3=;;" \
-                        "name4=\"var4 with spaces\";;" \
-                        "name5=var_with_=_char;\t \t");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (4 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1 ;name2=var2 ;name3= ;" \
-                        "name4=\"var4 with spaces\" ;" \
-                        "name5=var_with_=_char ;");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (5 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name3=; name1=var1; name2=var2; " \
-                        "name5=var_with_=_char;" \
-                        "name4=\"var4 with spaces\"");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (6 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name2=var2; name1=var1; " \
-                        "name5=var_with_=_char; name3=; " \
-                        "name4=\"var4 with spaces\";");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (7 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name2=var2; name1=var1; " \
-                        "name5=var_with_=_char; " \
-                        "name4=\"var4 with spaces\"; name3=");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (8 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name2=var2; name1=var1; " \
-                        "name4=\"var4 with spaces\"; " \
-                        "name5=var_with_=_char; name3=;");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (9 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2=value2",
+    2,
+    2,
+    2,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        ";;;;;;;;name1=var1; name2=var2; name3=; " \
-                        "name4=\"var4 with spaces\"; " \
-                        "name5=var_with_=_char");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (10 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1; name2=var2; name3=; " \
+    "name4=\"var4 with spaces\"; " \
+    "name5=var_with_=_char",
+    0,
+    3,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1; name2=var2; name3=; " \
-                        "name4=\"var4 with spaces\"; ; ; ; ; " \
-                        "name5=var_with_=_char");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (11 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1;name2=var2;name3=;" \
+    "name4=\"var4 with spaces\";" \
+    "name5=var_with_=_char",
+    0,
+    1,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1; name2=var2; name3=; " \
-                        "name4=\"var4 with spaces\"; " \
-                        "name5=var_with_=_char;;;;;;;;");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (12 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1;  name2=var2;  name3=;  " \
+    "name4=\"var4 with spaces\";  " \
+    "name5=var_with_=_char\t \t",
+    0,
+    1,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name1=var1; name2=var2; " \
-                        "name4=\"var4 with spaces\"" \
-                        "name5=var_with_=_char; ; ; ; ; name3=");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (13 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1;;name2=var2;;name3=;;" \
+    "name4=\"var4 with spaces\";;" \
+    "name5=var_with_=_char;\t \t",
+    0,
+    1,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name5=var_with_=_char ;" \
-                        "name1=var1; name2=var2; name3=; " \
-                        "name4=\"var4 with spaces\" ");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (14 == test_number)
+  },
+  {
+    __LINE__,
+    "name3=; name1=var1; name2=var2; " \
+    "name5=var_with_=_char;" \
+    "name4=\"var4 with spaces\"",
+    0,
+    4,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "name5=var_with_=_char; name4=\"var4 with spaces\";" \
-                        "name1=var1; name2=var2; name3=");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name5", "var_with_=_char"),
+      COOKIE_ ("name4", "var4 with spaces")
     }
-  }
-  else
+  },
+  {
+    __LINE__,
+    "name2=var2; name1=var1; " \
+    "name5=var_with_=_char; name3=; " \
+    "name4=\"var4 with spaces\";",
+    0,
+    4,
+    5,
+    5,
+    {
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name5", "var_with_=_char"),
+      COOKIE_ ("name4", "var4 with spaces")
+    }
+  },
   {
-    if (0 == test_number)
+    __LINE__,
+    "name2=var2; name1=var1; " \
+    "name5=var_with_=_char; " \
+    "name4=\"var4 with spaces\"; name3=",
+    0,
+    3,
+    5,
+    5,
     {
-      (void) 0; /* No cookie */
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name5", "var_with_=_char"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces")
     }
-    else if (1 == test_number)
+  },
+  {
+    __LINE__,
+    "name2=var2; name1=var1; " \
+    "name4=\"var4 with spaces\"; " \
+    "name5=var_with_=_char; name3=;",
+    0,
+    2,
+    5,
+    5,
+    {
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
+    }
+  },
+  {
+    __LINE__,
+    ";;;;;;;;name1=var1; name2=var2; name3=; " \
+    "name4=\"var4 with spaces\"; " \
+    "name5=var_with_=_char",
+    0,
+    0,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (2 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1; name2=var2; name3=; " \
+    "name4=\"var4 with spaces\"; ; ; ; ; " \
+    "name5=var_with_=_char",
+    0,
+    3,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "      ");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (3 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1; name2=var2; name3=; " \
+    "name4=\"var4 with spaces\"; " \
+    "name5=var_with_=_char;;;;;;;;",
+    0,
+    3,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "\t");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (4 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=var1; name2=var2; " \
+    "name4=\"var4 with spaces\";" \
+    "name5=var_with_=_char; ; ; ; ; name3=",
+    0,
+    2,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "var=,");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name5", "var_with_=_char"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces")
     }
-    else if (5 == test_number)
+  },
+  {
+    __LINE__,
+    "name5=var_with_=_char ;" \
+    "name1=var1; name2=var2; name3=; " \
+    "name4=\"var4 with spaces\" ",
+    0,
+    0,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "var=\"\\ \"");
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces"),
+      COOKIE_ ("name5", "var_with_=_char")
     }
-    else if (6 == test_number)
+  },
+  {
+    __LINE__,
+    "name5=var_with_=_char; name4=\"var4 with spaces\";" \
+    "name1=var1; name2=var2; name3=",
+    0,
+    1,
+    5,
+    5,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "var=value  space");
+      COOKIE_ ("name5", "var_with_=_char"),
+      COOKIE_ ("name1", "var1"),
+      COOKIE_ ("name2", "var2"),
+      COOKIE_ ("name3", ""),
+      COOKIE_ ("name4", "var4 with spaces")
     }
-    else if (7 == test_number)
+  },
+  {
+    __LINE__,
+    "name1 = value1",
+    0,
+    0,
+    0,
+    1,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "var=value\ttab");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (8 == test_number)
+  },
+  {
+    __LINE__,
+    "name1\t=\tvalue1",
+    0,
+    0,
+    0,
+    1,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "=");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (9 == test_number)
+  },
+  {
+    __LINE__,
+    "name1\t = \tvalue1",
+    0,
+    0,
+    0,
+    1,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "====");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (10 == test_number)
+  },
+  {
+    __LINE__,
+    "name1 = value1; name2 =\tvalue2",
+    0,
+    0,
+    0,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        ";=");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (11 == test_number)
+  },
+  {
+    __LINE__,
+    "name1=value1; name2 =\tvalue2",
+    0,
+    1,
+    1,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "var");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (12 == test_number)
+  },
+  {
+    __LINE__,
+    "name1 = value1; name2=value2",
+    0,
+    0,
+    0,
+    2,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "=;");
+      COOKIE_ ("name1", "value1"),
+      COOKIE_ ("name2", "value2"),
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (13 == test_number)
+  },
+  {
+    __LINE__,
+    "",
+    0,
+    0,
+    0,
+    0,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        "= ;");
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
-    else if (14 == test_number)
+  },
+  {
+    __LINE__,
+    "      ",
+    0,
+    0,
+    0,
+    0,
     {
-      curl_easy_setopt (c, CURLOPT_COOKIE,
-                        ";= ;");
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "\t",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "var=,",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "var=\"\\ \"",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "var=value  space",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "var=value\ttab",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "=",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "====",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    ";=",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "var",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "=;",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    "= ;",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
+    }
+  },
+  {
+    __LINE__,
+    ";= ;",
+    0,
+    0,
+    0,
+    0,
+    {
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL,
+      COOKIE_NULL
     }
   }
+};
+
+/* Global parameters */
+static int verbose;
+static int oneone;                  /**< If false use HTTP/1.0 for requests*/
+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)
+{
+  libcurl_errbuf[0] = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    externalErrorExit ();
+}
+
+
+static void
+test_global_cleanup (void)
+{
+  curl_global_cleanup ();
+}
+
 
-  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
-  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
-  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
-  /* NOTE: use of CONNECTTIMEOUT without also
-     setting NOSIGNAL results in really weird
-     crashes on my system! */
-  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
 
 
-  multi = curl_multi_init ();
-  if (multi == NULL)
+static size_t
+copyBuffer (void *ptr,
+            size_t size,
+            size_t nmemb,
+            void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+struct ahc_cls_type
+{
+  const char *rq_method;
+  const char *rq_url;
+  const struct strct_test_data *check;
+};
+
+
+static enum MHD_Result
+ahcCheck (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **req_cls)
+{
+  static int marker;
+  struct MHD_Response *response;
+  enum MHD_Result ret;
+  struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
+  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");
+
+  if (oneone)
   {
-    curl_easy_cleanup (c);
-    MHD_stop_daemon (d);
-    return 512;
+    if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
+      mhdErrorExitDesc ("Unexpected HTTP version");
   }
-  mret = curl_multi_add_handle (multi, c);
-  if (mret != CURLM_OK)
+  else
   {
-    curl_multi_cleanup (multi);
-    curl_easy_cleanup (c);
-    MHD_stop_daemon (d);
-    return 1024;
+    if (0 != strcmp (version, MHD_HTTP_VERSION_1_0))
+      mhdErrorExitDesc ("Unexpected HTTP version");
   }
-  start = time (NULL);
-  while ((time (NULL) - start < 5) && (multi != NULL))
+
+  if (0 != strcmp (url, param->rq_url))
+    mhdErrorExitDesc ("Unexpected URI");
+
+  if (NULL != upload_data)
+    mhdErrorExitDesc ("'upload_data' is not NULL");
+
+  if (NULL == upload_data_size)
+    mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
+
+  if (0 != *upload_data_size)
+    mhdErrorExitDesc ("'*upload_data_size' value is not zero");
+
+  if (0 != strcmp (param->rq_method, method))
+    mhdErrorExitDesc ("Unexpected request method");
+
+  cookie_failed = 0;
+  for (i = 0; i < expected_num_cookies; ++i)
   {
-    maxsock = MHD_INVALID_SOCKET;
-    maxposixs = -1;
-    FD_ZERO (&rs);
-    FD_ZERO (&ws);
-    FD_ZERO (&es);
-    curl_multi_perform (multi, &running);
-    mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
-    if (mret != CURLM_OK)
+    const char *cookie_val;
+    size_t cookie_val_len;
+    const struct strct_cookie *const cookie_data = param->check->cookies + i;
+    if (NULL == cookie_data->name.str)
+      externalErrorExitDesc ("Broken test data");
+    if (NULL == cookie_data->value.str)
+      externalErrorExitDesc ("Broken test data");
+
+    cookie_val =
+      MHD_lookup_connection_value (connection,
+                                   MHD_COOKIE_KIND,
+                                   cookie_data->name.str);
+    if (cookie_val == NULL)
     {
-      curl_multi_remove_handle (multi, c);
-      curl_multi_cleanup (multi);
-      curl_easy_cleanup (c);
-      MHD_stop_daemon (d);
-      return 2048;
+      fprintf (stderr, "'%s' cookie not found.\n",
+               cookie_data->name.str);
+      cookie_failed = 1;
     }
-    if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
+    else if (0 != strcmp (cookie_val,
+                          cookie_data->value.str))
     {
-      curl_multi_remove_handle (multi, c);
-      curl_multi_cleanup (multi);
-      curl_easy_cleanup (c);
-      MHD_stop_daemon (d);
-      return 4096;
+      fprintf (stderr, "'%s' cookie decoded incorrectly.\n"
+               "Expected: %s\nGot: %s\n",
+               cookie_data->name.str,
+               cookie_data->value.str,
+               cookie_val);
+      cookie_failed = 1;
     }
-    tv.tv_sec = 0;
-    tv.tv_usec = 1000;
-    if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
+    else if (MHD_YES !=
+             MHD_lookup_connection_value_n (connection,
+                                            MHD_COOKIE_KIND,
+                                            cookie_data->name.str,
+                                            cookie_data->name.len,
+                                            &cookie_val, &cookie_val_len))
     {
-#ifdef MHD_POSIX_SOCKETS
-      if (EINTR != errno)
+      fprintf (stderr, "'%s' (length %lu) cookie not found.\n",
+               cookie_data->name.str,
+               (unsigned long) cookie_data->name.len);
+      cookie_failed = 1;
+    }
+    else
+    {
+      if (cookie_data->value.len != cookie_val_len)
       {
-        fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
-                 (int) errno, __LINE__);
-        fflush (stderr);
-        exit (99);
+        fprintf (stderr, "'%s' (length %lu) cookie has wrong value length.\n"
+                 "Expected: %lu\nGot: %lu\n",
+                 cookie_data->name.str,
+                 (unsigned long) cookie_data->name.len,
+                 (unsigned long) cookie_data->value.len,
+                 (unsigned long) cookie_val_len);
+        cookie_failed = 1;
       }
-#else
-      if ((WSAEINVAL != WSAGetLastError ()) ||
-          (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+      else if (0 != memcmp (cookie_val, cookie_data->value.str, 
cookie_val_len))
       {
-        fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
-                 (int) WSAGetLastError (), __LINE__);
-        fflush (stderr);
-        exit (99);
+        fprintf (stderr, "'%s' (length %lu) cookie has wrong value.\n"
+                 "Expected: %.*s\nGot: %.*s\n",
+                 cookie_data->name.str,
+                 (unsigned long) cookie_data->name.len,
+                 (int) cookie_data->value.len, cookie_data->value.str,
+                 (int) cookie_val_len, cookie_val);
+        cookie_failed = 1;
       }
-      Sleep (1);
-#endif
     }
-    curl_multi_perform (multi, &running);
-    if (0 == running)
+  }
+  if (((int) expected_num_cookies) !=
+      MHD_get_connection_values_n (connection, MHD_COOKIE_KIND, NULL, NULL))
+  {
+    fprintf (stderr, "Wrong total number of cookies.\n"
+             "Expected: %u\nGot: %d\n",
+             expected_num_cookies,
+             MHD_get_connection_values_n (connection, MHD_COOKIE_KIND, NULL,
+                                          NULL));
+    cookie_failed = 1;
+  }
+  if (cookie_failed)
+  {
+    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)
+  {
+    *req_cls = &marker;
+    return MHD_YES;
+  }
+  *req_cls = NULL;
+
+  response =
+    MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
+                                            PAGE);
+  if (NULL == response)
+    mhdErrorExitDesc ("Failed to create response");
+
+  ret = MHD_queue_response (connection,
+                            MHD_HTTP_OK,
+                            response);
+  MHD_destroy_response (response);
+  if (MHD_YES != ret)
+    mhdErrorExitDesc ("Failed to queue response");
+
+  return ret;
+}
+
+
+static int
+libcurl_debug_cb (CURL *handle,
+                  curl_infotype type,
+                  char *data,
+                  size_t size,
+                  void *userptr)
+{
+  static const char excess_mark[] = "Excess found";
+  static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
+
+  (void) handle;
+  (void) userptr;
+
+#ifdef _DEBUG
+  switch (type)
+  {
+  case CURLINFO_TEXT:
+    fprintf (stderr, "* %.*s", (int) size, data);
+    break;
+  case CURLINFO_HEADER_IN:
+    fprintf (stderr, "< %.*s", (int) size, data);
+    break;
+  case CURLINFO_HEADER_OUT:
+    fprintf (stderr, "> %.*s", (int) size, data);
+    break;
+  case CURLINFO_DATA_IN:
+#if 0
+    fprintf (stderr, "<| %.*s\n", (int) size, data);
+#endif
+    break;
+  case CURLINFO_DATA_OUT:
+  case CURLINFO_SSL_DATA_IN:
+  case CURLINFO_SSL_DATA_OUT:
+  case CURLINFO_END:
+  default:
+    break;
+  }
+#endif /* _DEBUG */
+  if (CURLINFO_TEXT == type)
+  {
+    if ((size >= excess_mark_len) &&
+        (0 == memcmp (data, excess_mark, excess_mark_len)))
+      mhdErrorExitDesc ("Extra data has been detected in MHD reply");
+  }
+  return 0;
+}
+
+
+static CURL *
+setupCURL (void *cbc, uint16_t port)
+{
+  CURL *c;
+
+  c = curl_easy_init ();
+  if (NULL == c)
+    libcurlErrorExitDesc ("curl_easy_init() failed");
+
+  if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+                                     &copyBuffer)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+                                     ((long) TIMEOUTS_VAL))) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+                                     (oneone) ?
+                                     CURL_HTTP_VERSION_1_1 :
+                                     CURL_HTTP_VERSION_1_0)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+                                     ((long) TIMEOUTS_VAL))) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
+                                     libcurl_errbuf)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
+#ifdef _DEBUG
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
+#endif /* _DEBUG */
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
+                                     &libcurl_debug_cb)) ||
+#if CURL_AT_LEAST_VERSION (7, 19, 4)
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
+#endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
+#if CURL_AT_LEAST_VERSION (7, 45, 0)
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
+#endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
+    libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+  if (CURLE_OK !=
+      curl_easy_setopt (c, CURLOPT_URL,
+                        URL_SCHEME_HOST EXPECTED_URI_BASE_PATH))
+    libcurlErrorExitDesc ("Cannot set request URL");
+
+  return c;
+}
+
+
+static CURLcode
+performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
+{
+  CURLM *multi;
+  time_t start;
+  struct timeval tv;
+  CURLcode ret;
+
+  ret = CURLE_FAILED_INIT; /* will be replaced with real result */
+  if (NULL != *multi_reuse)
+    multi = *multi_reuse;
+  else
+  {
+    multi = curl_multi_init ();
+    if (multi == NULL)
+      libcurlErrorExitDesc ("curl_multi_init() failed");
+    *multi_reuse = multi;
+  }
+  if (CURLM_OK != curl_multi_add_handle (multi, c))
+    libcurlErrorExitDesc ("curl_multi_add_handle() failed");
+
+  start = time (NULL);
+  while (time (NULL) - start <= TIMEOUTS_VAL)
+  {
+    fd_set rs;
+    fd_set ws;
+    fd_set es;
+    MHD_socket maxMhdSk;
+    int maxCurlSk;
+    int running;
+
+    maxMhdSk = MHD_INVALID_SOCKET;
+    maxCurlSk = -1;
+    FD_ZERO (&rs);
+    FD_ZERO (&ws);
+    FD_ZERO (&es);
+    if (NULL != multi)
     {
-      int pending;
-      int curl_fine = 0;
-      while (NULL != (msg = curl_multi_info_read (multi, &pending)))
+      curl_multi_perform (multi, &running);
+      if (0 == running)
       {
-        if (msg->msg == CURLMSG_DONE)
+        struct CURLMsg *msg;
+        int msgLeft;
+        int totalMsgs = 0;
+        do
+        {
+          msg = curl_multi_info_read (multi, &msgLeft);
+          if (NULL == msg)
+            libcurlErrorExitDesc ("curl_multi_info_read() failed");
+          totalMsgs++;
+          if (CURLMSG_DONE == msg->msg)
+            ret = msg->data.result;
+        } while (msgLeft > 0);
+        if (1 != totalMsgs)
         {
-          if (msg->data.result == CURLE_OK)
-            curl_fine = 1;
-          else
-          {
-            fprintf (stderr,
-                     "%s failed at %s:%d: `%s'\n",
-                     "curl_multi_perform",
-                     __FILE__,
-                     __LINE__, curl_easy_strerror (msg->data.result));
-            abort ();
-          }
+          fprintf (stderr,
+                   "curl_multi_info_read returned wrong "
+                   "number of results (%d).\n",
+                   totalMsgs);
+          externalErrorExit ();
         }
+        curl_multi_remove_handle (multi, c);
+        multi = NULL;
       }
-      if (! curl_fine)
+      else
       {
-        fprintf (stderr, "libcurl haven't returned OK code\n");
-        abort ();
+        if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
+          libcurlErrorExitDesc ("curl_multi_fdset() failed");
       }
-      curl_multi_remove_handle (multi, c);
-      curl_multi_cleanup (multi);
-      curl_easy_cleanup (c);
-      c = NULL;
-      multi = NULL;
     }
-    MHD_run (d);
+    if (NULL == multi)
+    { /* libcurl has finished, check whether MHD still needs to perform 
cleanup */
+      if (0 != MHD_get_timeout64s (d))
+        break; /* MHD finished as well */
+    }
+    if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+      mhdErrorExitDesc ("MHD_get_fdset() failed");
+    tv.tv_sec = 0;
+    tv.tv_usec = 200000;
+    if (0 == MHD_get_timeout64s (d))
+      tv.tv_usec = 0;
+    else
+    {
+      long curl_to = -1;
+      curl_multi_timeout (multi, &curl_to);
+      if (0 == curl_to)
+        tv.tv_usec = 0;
+    }
+#ifdef MHD_POSIX_SOCKETS
+    if (maxMhdSk > maxCurlSk)
+      maxCurlSk = maxMhdSk;
+#endif /* MHD_POSIX_SOCKETS */
+    if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
+    {
+#ifdef MHD_POSIX_SOCKETS
+      if (EINTR != errno)
+        externalErrorExitDesc ("Unexpected select() error");
+#else
+      if ((WSAEINVAL != WSAGetLastError ()) ||
+          (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+        externalErrorExitDesc ("Unexpected select() error");
+      Sleep ((unsigned long) tv.tv_usec / 1000);
+#endif
+    }
+    if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+      mhdErrorExitDesc ("MHD_run_from_select() failed");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Check request result
+ * @param curl_code the CURL easy return code
+ * @param pcbc the pointer struct CBC
+ * @return non-zero if success, zero if failed
+ */
+static unsigned int
+check_result (CURLcode curl_code, CURL *c, long expected_code,
+              struct CBC *pcbc)
+{
+  long code;
+
+  if (CURLE_OK != curl_code)
+  {
+    fflush (stdout);
+    if (0 != libcurl_errbuf[0])
+      fprintf (stderr, "Request failed. "
+               "libcurl error: '%s'.\n"
+               "libcurl error description: '%s'.\n",
+               curl_easy_strerror (curl_code),
+               libcurl_errbuf);
+    else
+      fprintf (stderr, "Request failed. "
+               "libcurl error: '%s'.\n",
+               curl_easy_strerror (curl_code));
+    fflush (stderr);
+    return 0;
+  }
+
+  if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
+    libcurlErrorExit ();
+
+  if (expected_code != code)
+  {
+    fprintf (stderr, "### The response has wrong HTTP code: %ld\t"
+             "Expected: %ld.\n",
+             code, expected_code);
+    return 0;
   }
-  if (multi != NULL)
+  else if (verbose)
+    printf ("### The response has expected HTTP code: %ld\n", expected_code);
+
+  if (pcbc->pos != MHD_STATICSTR_LEN_ (PAGE))
   {
-    curl_multi_remove_handle (multi, c);
-    curl_easy_cleanup (c);
-    curl_multi_cleanup (multi);
+    fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+             (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
+             (unsigned) MHD_STATICSTR_LEN_ (PAGE));
+    mhdErrorExitDesc ("Wrong returned data length");
   }
-  MHD_stop_daemon (d);
-  if (cbc.pos != strlen ("/hello_world"))
-    return 8192;
-  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
-    return 16384;
-  return 0;
+  if (0 != memcmp (PAGE, pcbc->buf, pcbc->pos))
+  {
+    fprintf (stderr, "Got invalid response '%.*s'. ",
+             (int) pcbc->pos, pcbc->buf);
+    mhdErrorExitDesc ("Wrong returned data");
+  }
+  fflush (stderr);
+  fflush (stdout);
+
+  return 1;
 }
 
 
-int
-main (int argc, char *const *argv)
+static unsigned int
+testExternalPolling (void)
 {
-  unsigned int errorCount = 0;
-  (void) argc;   /* Unused. Silent compiler warning. */
+  struct MHD_Daemon *d;
+  uint16_t port;
+  struct CBC cbc;
+  struct ahc_cls_type ahc_param;
+  char buf[2048];
+  CURL *c;
+  CURLM *multi_reuse;
+  size_t i;
+  int failed = 0;
 
-  if ((NULL == argv) || (0 == argv[0]))
-    return 99;
-  use_invalid = has_in_name (argv[0], "_invalid");
-  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
-    return 2;
   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
     port = 0;
   else
+    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) (strict_level),
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  if (0 == port)
+  {
+    const union MHD_DaemonInfo *dinfo;
+
+    dinfo = MHD_get_daemon_info (d,
+                                 MHD_DAEMON_INFO_BIND_PORT);
+    if ( (NULL == dinfo) ||
+         (0 == dinfo->port) )
+      mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+    port = dinfo->port;
+  }
+
+  ahc_param.rq_method = MHD_HTTP_METHOD_GET;
+  ahc_param.rq_url = EXPECTED_URI_BASE_PATH;
+  cbc.buf = buf;
+  cbc.size = sizeof (buf);
+  memset (cbc.buf, 0, cbc.size);
+  c = setupCURL (&cbc, port);
+  multi_reuse = NULL;
+  for (i = 0; i < sizeof(test_data) / sizeof(test_data[0]); ++i)
   {
-    port = 1340;
-    if (use_invalid)
-      port += 5;
+    cbc.pos = 0;
+    ahc_param.check = test_data + i;
+    if (CURLE_OK !=
+        curl_easy_setopt (c, CURLOPT_COOKIE,
+                          ahc_param.check->header_str))
+      libcurlErrorExitDesc ("Cannot set request cookies");
+
+    if (check_result (performQueryExternal (d, c, &multi_reuse), c,
+                      MHD_HTTP_OK, &cbc))
+    {
+      if (verbose)
+        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",
+               test_data[i].line_num);
+      fflush (stderr);
+      failed = 1;
+    }
   }
-  errorCount += testExternalGet (0);
-  errorCount += testExternalGet (1);
-  errorCount += testExternalGet (2);
-  errorCount += testExternalGet (3);
-  errorCount += testExternalGet (4);
-  errorCount += testExternalGet (5);
-  errorCount += testExternalGet (6);
-  errorCount += testExternalGet (7);
-  errorCount += testExternalGet (8);
-  errorCount += testExternalGet (9);
-  errorCount += testExternalGet (10);
-  errorCount += testExternalGet (11);
+
+  curl_easy_cleanup (c);
+  if (NULL != multi_reuse)
+    curl_multi_cleanup (multi_reuse);
+
+  MHD_stop_daemon (d);
+  return failed ? 1 : 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  /* Test type and test parameters */
+  verbose = ! (has_param (argc, argv, "-q") ||
+               has_param (argc, argv, "--quiet") ||
+               has_param (argc, argv, "-s") ||
+               has_param (argc, argv, "--silent"));
+  oneone = ! has_in_name (argv[0], "10");
+  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 ();
+
+  errorCount += testExternalPolling ();
   if (errorCount != 0)
     fprintf (stderr, "Error (code: %u)\n", errorCount);
-  curl_global_cleanup ();
+
+  test_global_cleanup ();
+
   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]