gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated (64e91ef6 -> e1693575)


From: gnunet
Subject: [libmicrohttpd] branch master updated (64e91ef6 -> e1693575)
Date: Mon, 05 Jul 2021 16:07:41 +0200

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

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 64e91ef6 Use compiler built-ins for bits rotations if available
     new d51a5ce4 mhd_str: minor code refactoring
     new 8e0fdcad mhd_str: minor optimisation
     new d06ee976 Corrected internal doxy
     new 63dd1bff MHD_del_response_header(): removed unused variable
     new ada4c2ed Added MHD_str_remove_token_caseless_() function
     new ed3daed3 Added MHD_str_remove_tokens_caseless_() function
     new e1693575 response: process "Connection" header in a special manner

The 7 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/microhttpd/.gitignore               |   3 +
 src/microhttpd/Makefile.am              |  16 +-
 src/microhttpd/internal.h               |  21 +-
 src/microhttpd/mhd_str.c                | 381 +++++++++++++++++++-
 src/microhttpd/mhd_str.h                |  68 ++++
 src/microhttpd/response.c               | 233 +++++++++++-
 src/microhttpd/test_response_entries.c  | 618 ++++++++++++++++++++++++++++++++
 src/microhttpd/test_str_token_remove.c  | 248 +++++++++++++
 src/microhttpd/test_str_tokens_remove.c | 282 +++++++++++++++
 9 files changed, 1852 insertions(+), 18 deletions(-)
 create mode 100644 src/microhttpd/test_response_entries.c
 create mode 100644 src/microhttpd/test_str_token_remove.c
 create mode 100644 src/microhttpd/test_str_tokens_remove.c

diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore
index 66b0ac54..3b0d8ce4 100644
--- a/src/microhttpd/.gitignore
+++ b/src/microhttpd/.gitignore
@@ -53,6 +53,9 @@ test_http_reasons
 /test_options
 /test_start_stop
 /test_str_token
+/test_str_token_remove
+/test_str_tokens_remove
+/test_response_entries
 test_shutdown_poll
 test_shutdown_select
 test_md5
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index abcbeba7..e610c1df 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -158,12 +158,15 @@ check_PROGRAMS = \
   test_str_compare \
   test_str_to_value \
   test_str_token \
+  test_str_token_remove \
+  test_str_tokens_remove \
   test_http_reasons \
   test_md5 \
   test_sha1 \
   test_sha256 \
   test_start_stop \
   test_daemon \
+  test_response_entries \
   test_postprocessor_md \
   test_options
 
@@ -215,13 +218,18 @@ endif
 test_start_stop_SOURCES = \
   test_start_stop.c
 test_start_stop_LDADD = \
-  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+  libmicrohttpd.la
 
 test_daemon_SOURCES = \
   test_daemon.c
 test_daemon_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la
 
+test_response_entries_SOURCES = \
+  test_response_entries.c
+test_response_entries_LDADD = \
+  libmicrohttpd.la
+
 test_upgrade_SOURCES = \
   test_upgrade.c test_helpers.h mhd_sockets.h
 test_upgrade_CPPFLAGS = \
@@ -353,6 +361,12 @@ test_str_to_value_SOURCES = \
 test_str_token_SOURCES = \
   test_str_token.c mhd_str.c mhd_str.h
 
+test_str_token_remove_SOURCES = \
+  test_str_token_remove.c mhd_str.c mhd_str.h mhd_assert.h mhd_options.h
+
+test_str_tokens_remove_SOURCES = \
+  test_str_tokens_remove.c mhd_str.c mhd_str.h mhd_assert.h mhd_options.h
+
 test_http_reasons_SOURCES = \
   test_http_reasons.c \
   reason_phrase.c mhd_str.c mhd_str.h
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 5821011b..d1f3b9ff 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -328,7 +328,7 @@ struct MHD_HTTP_Header
   char *header;
 
   /**
-   * Number of bytes in @a header.
+   * The length of the @a header, not including the final zero termination.
    */
   size_t header_size;
 
@@ -338,7 +338,7 @@ struct MHD_HTTP_Header
   char *value;
 
   /**
-   * Number of bytes in @a value.
+   * The length of the @a value, not including the final zero termination.
    */
   size_t value_size;
 
@@ -351,6 +351,18 @@ struct MHD_HTTP_Header
 };
 
 
+/**
+ * Automatically assigned flags
+ */
+enum MHD_ResponseAutoFlags
+{
+  MHD_RAF_NO_FLAGS = 0,                  /**< No auto flags */
+  MHD_RAF_HAS_CONNECTION_HDR = 1 << 0,   /**< Has "Connection" header */
+  MHD_RAF_HAS_CONNECTION_CLOSE = 1 << 1, /**< Has "Connection: close" */
+  MHD_RAF_HAS_TRANS_ENC_CHUNKED = 2 << 2 /**< Has "Transfer-Encoding: chunked 
*/
+} _MHD_FIXED_FLAGS_ENUM;
+
+
 #if defined(MHD_WINSOCK_SOCKETS)
 /**
  * Internally used I/O vector type for use with winsock.
@@ -510,6 +522,11 @@ struct MHD_Response
    */
   enum MHD_ResponseFlags flags;
 
+  /**
+   * Automatic flags set for the MHD response.
+   */
+  enum MHD_ResponseAutoFlags flags_auto;
+
   /**
    * If the @e fd is a pipe (no sendfile()).
    */
diff --git a/src/microhttpd/mhd_str.c b/src/microhttpd/mhd_str.c
index eea2500b..60c198bc 100644
--- a/src/microhttpd/mhd_str.c
+++ b/src/microhttpd/mhd_str.c
@@ -27,8 +27,10 @@
 
 #ifdef HAVE_STDBOOL_H
 #include <stdbool.h>
-#endif
+#endif /* HAVE_STDBOOL_H */
+#include <string.h>
 
+#include "mhd_assert.h"
 #include "mhd_limits.h"
 
 #ifdef MHD_FAVOR_SMALL_CODE
@@ -143,6 +145,7 @@ isasciialnum (char c)
 #endif /* Disable unused functions. */
 
 
+#if 0 /* Disable unused functions. */
 /**
  * Convert US-ASCII character to lower case.
  * If character is upper case letter in US-ASCII than it's converted to lower
@@ -159,7 +162,6 @@ toasciilower (char c)
 }
 
 
-#if 0 /* Disable unused functions. */
 /**
  * Convert US-ASCII character to upper case.
  * If character is lower case letter in US-ASCII than it's converted to upper
@@ -219,6 +221,23 @@ toxdigitvalue (char c)
 }
 
 
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c1 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+_MHD_static_inline bool
+charsequalcaseless (const char c1, const char c2)
+{
+  return ( (c1 == c2) ||
+           (isasciiupper (c1) ?
+            ((c1 - 'A' + 'a') == c2) :
+            ((c1 == (c2 - 'A' + 'a')) && isasciiupper (c2))) );
+}
+
+
 #else  /* !INLINE_FUNC */
 
 
@@ -331,6 +350,20 @@ toxdigitvalue (char c)
                             ( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \
                               (int) (((unsigned char) (c)) - 'a' + 10) : \
                               (int) (-1) )))
+
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c1 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+#define charsequalcaseless(c1, c2) \
+  ( ((c1) == (c2)) || \
+           (isasciiupper (c1) ? \
+             (((c1) - 'A' + 'a') == (c2)) : \
+             (((c1) == ((c2) - 'A' + 'a')) && isasciiupper (c2))) )
+
 #endif /* !INLINE_FUNC */
 
 
@@ -392,10 +425,7 @@ MHD_str_equal_caseless_n_ (const char *const str1,
     const char c2 = str2[i];
     if (0 == c2)
       return 0 == c1;
-    if ( (c1 == c2) ||
-         (isasciiupper (c1) ?
-          ((c1 - 'A' + 'a') == c2) :
-          (isasciiupper (c2) && (c1 == (c2 - 'A' + 'a')))) )
+    if (charsequalcaseless (c1, c2))
       continue;
     else
       return 0;
@@ -424,10 +454,7 @@ MHD_str_equal_caseless_bin_n_ (const char *const str1,
   {
     const char c1 = str1[i];
     const char c2 = str2[i];
-    if ( (c1 == c2) ||
-         (isasciiupper (c1) ?
-          ((c1 - 'A' + 'a') == c2) :
-          (isasciiupper (c2) && (c1 == (c2 - 'A' + 'a')))) )
+    if (charsequalcaseless (c1, c2))
       continue;
     else
       return 0;
@@ -473,8 +500,7 @@ MHD_str_has_token_caseless_ (const char *str,
 
       if (0 == sc)
         return false;
-      if ( (sc != tc) &&
-           (toasciilower (sc) != toasciilower (tc)) )
+      if (! charsequalcaseless (sc, tc))
         break;
       if (i >= token_len)
       {
@@ -497,6 +523,337 @@ MHD_str_has_token_caseless_ (const char *str,
 }
 
 
+/**
+ * Remove case-insensitive @a token from the @a str and put result
+ * to the output @a buf.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of string) or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The output string is normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma.
+ *
+ * @param str the string to process
+ * @param str_len the length of the @a str, not including optional
+ *                terminating null-character.
+ * @param token the token to find
+ * @param token_len the length of @a token, not including optional
+ *                  terminating null-character.
+ * @param[out] buf the output buffer, not null-terminated.
+ * @param[in,out] buf_size pointer to the size variable, at input it
+ *                         is the size of allocated buffer, at output
+ *                         it is the size of the resulting string (can
+ *                         be up to 50% larger than input) or negative value
+ *                         if there is not enough space for the result
+ * @return 'true' if token has been removed,
+ *         'false' otherwise.
+ */
+bool
+MHD_str_remove_token_caseless_ (const char *str,
+                                size_t str_len,
+                                const char *const token,
+                                const size_t token_len,
+                                char *buf,
+                                ssize_t *buf_size)
+{
+  const char *s1; /**< the "input" string / character */
+  char *s2;       /**< the "output" string / character */
+  size_t t_pos;   /**< position of matched character in the token */
+  bool token_removed;
+
+  mhd_assert (NULL == memchr (token, 0, token_len));
+  mhd_assert (NULL == memchr (token, ' ', token_len));
+  mhd_assert (NULL == memchr (token, '\t', token_len));
+  mhd_assert (NULL == memchr (token, ',', token_len));
+  mhd_assert (0 <= *buf_size);
+
+  s1 = str;
+  s2 = buf;
+  token_removed = false;
+
+  while ((size_t) (s1 - str) < str_len)
+  {
+    const char *cur_token; /**< the first char of current token */
+    size_t copy_size;
+
+    /* Skip any initial whitespaces and empty tokens */
+    while ( ((size_t) (s1 - str) < str_len) &&
+            ((' ' == *s1) || ('\t' == *s1) || (',' == *s1)) )
+      s1++;
+
+    /* 's1' points to the first char of token in the input string or
+     * points just beyond the end of the input string */
+
+    if ((size_t) (s1 - str) >= str_len)
+      break; /* Nothing to copy, end of the input string */
+
+    cur_token = s1; /* the first char of input token or
+                       the char after the input buffer */
+
+    /* Check the token with case-insensetive match */
+    t_pos = 0;
+    while ( ((size_t) (s1 - str) < str_len) && (token_len > t_pos) &&
+            (charsequalcaseless (*s1, token[t_pos])) )
+    {
+      s1++;
+      t_pos++;
+    }
+    if ( (token_len == t_pos) && (0 != token_len) )
+    {
+      /* 'token' matched, check that current input token does not have
+       * any suffixes */
+      while ( ((size_t) (s1 - str) < str_len) &&
+              ((' ' == *s1) || ('\t' == *s1)) )
+        s1++;
+      /* 's1' points to the first non-whitespace char after the token matched
+       * requested token or points just beyond the end of the input string 
after
+       * the requested token */
+      if (((size_t) (s1 - str) == str_len) || (',' == *s1))
+      {/* full token match, do not copy current token to the output */
+        token_removed = true;
+        continue;
+      }
+    }
+
+    /* 's1' points to some non-whitespace char in the token in the input
+     * string, to the ',', or just beyond the end of the input string */
+    /* The current token in the input string does not match excluded token,
+     * it must be copied to the output string */
+    /* the current token size excluding leading whitespaces and current char */
+    copy_size = (size_t) (s1 - cur_token);
+    if (buf == s2)
+    { /* The first token to copy to the output */
+      if (buf + *buf_size < s2 + copy_size)
+      { /* Not enough space in the output buffer */
+        *buf_size = (ssize_t) -1;
+        return false;
+      }
+    }
+    else
+    { /* Some token was already copied to the output buffer */
+      if (buf + *buf_size < s2 + copy_size + 2)
+      { /* Not enough space in the output buffer */
+        *buf_size = (ssize_t) -1;
+        return false;
+      }
+      *(s2++) = ',';
+      *(s2++) = ' ';
+    }
+    /* Copy non-matched token to the output */
+    if (0 != copy_size)
+    {
+      memcpy (s2, cur_token, copy_size);
+      s2 += copy_size;
+    }
+
+    while ( ((size_t) (s1 - str) < str_len) && (',' != *s1))
+    {
+      /* 's1' points to some non-whitespace char in the token */
+      while ( ((size_t) (s1 - str) < str_len) &&
+              (',' != *s1) && (' ' != *s1) && ('\t' != *s1) )
+      {
+        if (buf + *buf_size <= s2) /* '<= s2' equals '< s2 + 1' */
+        { /* Not enough space in the output buffer */
+          *buf_size = (ssize_t) -1;
+          return false;
+        }
+        *(s2++) = *(s1++);
+      }
+      /* 's1' points to some whitespace char in the token in the input
+       * string, to the ',', or just beyond the end of the input string */
+      /* Skip all whitespaces */
+      while ( ((size_t) (s1 - str) < str_len) &&
+              ((' ' == *s1) || ('\t' == *s1)) )
+        s1++;
+
+      /* 's1' points to the first non-whitespace char in the input string
+       * after whitespace chars or just beyond the end of the input string */
+      if (((size_t) (s1 - str) < str_len) && (',' != *s1))
+      { /* Not the end of the current token */
+        if (buf + *buf_size <= s2) /* '<= s2' equals '< s2 + 1' */
+        { /* Not enough space in the output buffer */
+          *buf_size = (ssize_t) -1;
+          return false;
+        }
+        *(s2++) = ' ';
+      }
+    }
+  }
+  mhd_assert (((ssize_t) (s2 - buf)) <= *buf_size);
+  *buf_size = (ssize_t) (s2 - buf);
+  return token_removed;
+}
+
+
+/**
+ * Perform in-place case-insensitive removal of @a tokens from the @a str.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of string) or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The input string must be normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma. The string is updated in-place.
+ *
+ * Behavior is undefined is input string in not normalised.
+ *
+ * @param[in,out] str the string to update
+ * @param[in,out] str_len the length of the @a str, not including optional
+ *                        terminating null-character, not null-terminated
+ * @param tokens the token to find
+ * @param tokens_len the length of @a tokens, not including optional
+ *                   terminating null-character.
+ * @return 'true' if any token has been removed,
+ *         'false' otherwise.
+ */
+bool
+MHD_str_remove_tokens_caseless_ (char *str,
+                                 size_t *str_len,
+                                 const char *const tokens,
+                                 const size_t tokens_len)
+{
+  const char *t;   /**< position in the @a tokens string */
+  bool token_removed;
+
+  mhd_assert (NULL == memchr (tokens, 0, tokens_len));
+
+  token_removed = false;
+  t = tokens;
+
+  while ((size_t) (t - tokens) < tokens_len && *str_len != 0)
+  {
+    const char *tkn; /**< the current token */
+    size_t tkn_len;
+
+    /* Skip any initial whitespaces and empty tokens in 'tokens' */
+    while ( ((size_t) (t - tokens) < tokens_len) &&
+            ((' ' == *t) || ('\t' == *t) || (',' == *t)) )
+      t++;
+
+    if ((size_t) (t - tokens) >= tokens_len)
+      break; /* No more tokens, nothing to remove */
+
+    /* Found non-whitespace char which is not a comma */
+    tkn = t;
+    do
+    {
+      do
+      {
+        t++;
+      } while ((size_t) (t - tokens) < tokens_len && (' ' != *t && '\t' != *t 
&&
+                                                      ',' != *t));
+      /* Found end of token string, space, tab, or comma */
+      tkn_len = t - tkn;
+
+      /* Skip all spaces and tabs */
+      while ((size_t) (t - tokens) < tokens_len && (' ' == *t || '\t' == *t))
+        t++;
+      /* Found end of token string or non-whitespace char */
+    } while((size_t) (t - tokens) < tokens_len && ',' != *t);
+
+    /* 'tkn' is the input token with 'tkn_len' chars */
+    mhd_assert (0 != tkn_len);
+
+    if (*str_len == tkn_len)
+    {
+      if (MHD_str_equal_caseless_bin_n_ (str, tkn, tkn_len))
+      {
+        *str_len = 0;
+        token_removed = true;
+      }
+      continue;
+    }
+    if (*str_len > tkn_len + 2)
+    { /* Remove 'tkn' from the input string */
+      const char *s1;  /**< the "input" string / character */
+      char *s2;        /**< the "output" string / character */
+
+      s1 = str;
+      s2 = str;
+
+      do
+      {
+        mhd_assert (s1 >= s2);
+        mhd_assert ((str + *str_len) >= (s1 + tkn_len));
+        if ( ( ((str + *str_len) == (s1 + tkn_len)) || (',' == s1[tkn_len]) ) 
&&
+             MHD_str_equal_caseless_bin_n_ (s1, tkn, tkn_len) )
+        {
+          /* current token in the input string matches the 'tkn', skip it */
+          mhd_assert ((str + *str_len == s1 + tkn_len) || \
+                      (',' == s1[tkn_len]));
+          mhd_assert ((str + *str_len == s1 + tkn_len) || \
+                      (' ' == s1[tkn_len + 1]));
+          token_removed = true;
+          /* Advance to the next token in the input string or beyond
+           * the end of the input string. */
+          s1 += tkn_len + 2;
+        }
+        else
+        {
+          /* current token in the input string does not match the 'tkn',
+           * copy to the output */
+          if (str != s2)
+          { /* not the first output token, add ", " to separate */
+            if (s1 != s2 + 2)
+            {
+              *(s2++) = ',';
+              *(s2++) = ' ';
+            }
+            else
+              s2 += 2;
+          }
+          do
+          {
+            if (s1 != s2)
+              *s2 = *s1;
+            s1++;
+            s2++;
+          } while (s1 < str + *str_len && ',' != *s1);
+          /* Advance to the next token in the input string or beyond
+           * the end of the input string. */
+          s1 += 2;
+        }
+        /* s1 should point to the next token in the input string or beyond
+         * the end of the input string */
+        if ((str + *str_len) < (s1 + tkn_len))
+        { /* The rest of the 's1' is too small to match 'tkn' */
+          if ((str + *str_len) > s1)
+          { /* Copy the rest of the string */
+            size_t copy_size;
+            copy_size = *str_len - (size_t) (s1 - str);
+            if (str != s2)
+            { /* not the first output token, add ", " to separate */
+              if (s1 != s2 + 2)
+              {
+                *(s2++) = ',';
+                *(s2++) = ' ';
+              }
+              else
+                s2 += 2;
+            }
+            if (s1 != s2)
+              memmove (s2, s1, copy_size);
+            s2 += copy_size;
+          }
+          *str_len = s2 - str;
+          break;
+        }
+        mhd_assert ((' ' != s1[0]) && ('\t' != s1[0]));
+        mhd_assert ((s1 == str) || (' ' == *(s1 - 1)));
+        mhd_assert ((s1 == str) || (',' == *(s1 - 2)));
+      } while (1);
+    }
+  }
+
+  return token_removed;
+}
+
+
 #ifndef MHD_FAVOR_SMALL_CODE
 /* Use individual function for each case */
 
diff --git a/src/microhttpd/mhd_str.h b/src/microhttpd/mhd_str.h
index 80f3f975..33f91086 100644
--- a/src/microhttpd/mhd_str.h
+++ b/src/microhttpd/mhd_str.h
@@ -130,6 +130,74 @@ MHD_str_has_token_caseless_ (const char *str,
 #define MHD_str_has_s_token_caseless_(str,tkn) \
   MHD_str_has_token_caseless_ ((str),(tkn),MHD_STATICSTR_LEN_ (tkn))
 
+
+/**
+ * Remove case-insensitive @a token from the @a str and put result
+ * to the output @a buf.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of string) or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The output string is normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma.
+ *
+ * @param str the string to process
+ * @param str_len the length of the @a str, not including optional
+ *                terminating null-character.
+ * @param token the token to find
+ * @param token_len the length of @a token, not including optional
+ *                  terminating null-character.
+ * @param[out] buf the output buffer, not null-terminated.
+ * @param[in,out] buf_size pointer to the size variable, at input it
+ *                         is the size of allocated buffer, at output
+ *                         it is the size of the resulting string (can
+ *                         be up to 50% larger than input) or negative value
+ *                         if there is not enough space for the result
+ * @return 'true' if token has been removed,
+ *         'false' otherwise.
+ */
+bool
+MHD_str_remove_token_caseless_ (const char *str,
+                                size_t str_len,
+                                const char *const token,
+                                const size_t token_len,
+                                char *buf,
+                                ssize_t *buf_size);
+
+
+/**
+ * Perform in-place case-insensitive removal of @a tokens from the @a str.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of string) or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The input string must be normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma. The string is updated in-place.
+ *
+ * Behavior is undefined is input string in not normalised.
+ *
+ * @param[in,out] str the string to update
+ * @param[in,out] str_len the length of the @a str, not including optional
+ *                        terminating null-character, not null-terminated
+ * @param tokens the token to find
+ * @param tokens_len the length of @a tokens, not including optional
+ *                   terminating null-character.
+ * @return 'true' if any token has been removed,
+ *         'false' otherwise.
+ */
+bool
+MHD_str_remove_tokens_caseless_ (char *str,
+                                 size_t *str_len,
+                                 const char *const tokens,
+                                 const size_t tokens_len);
+
+
 #ifndef MHD_FAVOR_SMALL_CODE
 /* Use individual function for each case to improve speed */
 
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index c91125eb..674cef0d 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -193,6 +193,227 @@ add_response_entry (struct MHD_Response *response,
 }
 
 
+/**
+ * Add "Connection:" header to the response with special processing.
+ *
+ * "Connection:" header value will be combined with any existing "Connection:"
+ * header, "close" token (if any) will be de-duplicated and moved to the first
+ * position.
+ *
+ * @param response the response to add a header to
+ * @param value the value to add
+ * @return #MHD_NO on error (no memory).
+ */
+static enum MHD_Result
+add_response_header_connection (struct MHD_Response *response,
+                                const char *value)
+{
+  static const char *key = "Connection";
+  /** the length of the "Connection" key */
+  static const size_t key_len = MHD_STATICSTR_LEN_ ("Connection");
+  size_t value_len;  /**< the length of the @a value */
+  size_t old_value_len; /**< the length of the existing "Connection" value */
+  size_t buf_size;   /**< the size of the buffer */
+  ssize_t norm_len;  /**< the length of the normalised value */
+  char *buf;         /**< the temporal buffer */
+  struct MHD_HTTP_Header *hdr; /**< existing "Connection" header */
+  bool value_has_close; /**< the @a value has "close" token */
+  bool already_has_close; /**< existing "Connection" header has "close" token 
*/
+  size_t pos = 0;   /**< position of addition in the @a buf */
+
+  if ( (NULL != strchr (value, '\r')) ||
+       (NULL != strchr (value, '\n')) )
+    return MHD_NO;
+
+  if (0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
+  {
+    hdr = MHD_get_response_element_n_ (response, MHD_HEADER_KIND,
+                                       key, key_len);
+    already_has_close =
+      (0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+    mhd_assert (already_has_close == (0 == memcmp (hdr->value, "close", 5)));
+    mhd_assert (NULL != hdr);
+  }
+  else
+  {
+    hdr = NULL;
+    already_has_close = false;
+    mhd_assert (NULL == MHD_get_response_element_n_ (response,
+                                                     MHD_HEADER_KIND,
+                                                     key, key_len));
+    mhd_assert (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+  }
+  if (NULL != hdr)
+    old_value_len = hdr->value_size + 2; /* additional size for ", " */
+  else
+    old_value_len = 0;
+
+  value_len = strlen (value);
+  /* Additional space for normalisation and zero-termination*/
+  norm_len = (ssize_t) (value_len + value_len / 2 + 1);
+  buf_size = old_value_len + (size_t) norm_len;
+
+  buf = malloc (buf_size);
+  if (NULL == buf)
+    return MHD_NO;
+  /* Move "close" token (if any) to the front */
+  value_has_close = MHD_str_remove_token_caseless_ (value, value_len, "close",
+                                                    MHD_STATICSTR_LEN_ ( \
+                                                      "close"),
+                                                    buf + old_value_len,
+                                                    &norm_len);
+  mhd_assert (0 <= norm_len);
+  if (0 > norm_len)
+    norm_len = 0; /* Must never happen */
+  if (0 == norm_len)
+  { /* New value is empty after normalisation */
+    if (! value_has_close)
+    { /* The new value had no tokens */
+      free (buf);
+      return MHD_NO;
+    }
+    if (already_has_close)
+    { /* The "close" token is already present, nothing to modify */
+      free (buf);
+      return MHD_YES;
+    }
+  }
+  /* Add "close" token if required */
+  if (value_has_close && ! already_has_close)
+  {
+    /* Need to insert "close" token at the first position */
+    mhd_assert (buf_size >= old_value_len + (size_t) norm_len   \
+                + MHD_STATICSTR_LEN_ ("close, ") + 1);
+    if (0 != norm_len)
+      memmove (buf + MHD_STATICSTR_LEN_ ("close, ") + old_value_len,
+               buf + old_value_len, norm_len + 1);
+    memcpy (buf, "close", MHD_STATICSTR_LEN_ ("close"));
+    pos += MHD_STATICSTR_LEN_ ("close");
+  }
+  /* Add old value tokens (if any) */
+  if (0 != old_value_len)
+  {
+    if (0 != pos)
+    {
+      buf[pos++] = ',';
+      buf[pos++] = ' ';
+    }
+    memcpy (buf + pos, hdr->value,
+            hdr->value_size);
+    pos += hdr->value_size;
+  }
+  /* Add new value token (if any) */
+  if (0 != norm_len)
+  {
+    if (0 != pos)
+    {
+      buf[pos++] = ',';
+      buf[pos++] = ' ';
+    }
+    /* The new value tokens must be already at the correct position */
+    mhd_assert ((value_has_close && ! already_has_close) ? \
+                (MHD_STATICSTR_LEN_ ("close, ") + old_value_len == pos) : \
+                (old_value_len == pos));
+    pos += (size_t) norm_len;
+  }
+  mhd_assert (buf_size > pos);
+  buf[pos] = 0; /* Null terminate the result */
+
+  if (NULL == hdr)
+  {
+    struct MHD_HTTP_Header *new_hdr; /**< new "Connection" header */
+    /* Create new response header entry */
+    new_hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Header));
+    if (NULL != new_hdr)
+    {
+      new_hdr->header = malloc (key_len + 1);
+      if (NULL != new_hdr->header)
+      {
+        memcpy (new_hdr->header, key, key_len + 1);
+        new_hdr->header_size = key_len;
+        new_hdr->value = buf;
+        new_hdr->value_size = pos;
+        new_hdr->kind = MHD_HEADER_KIND;
+        if (value_has_close)
+          response->flags_auto = (MHD_RAF_HAS_CONNECTION_HDR
+                                  | MHD_RAF_HAS_CONNECTION_CLOSE);
+        else
+          response->flags_auto = MHD_RAF_HAS_CONNECTION_HDR;
+        _MHD_insert_header_first (response, new_hdr);
+        return MHD_YES;
+      }
+      free (new_hdr);
+    }
+    free (buf);
+    return MHD_NO;
+  }
+
+  /* Update existing header entry */
+  free (hdr->value);
+  hdr->value = buf;
+  hdr->value_size = pos;
+  if (value_has_close && ! already_has_close)
+    response->flags_auto |= MHD_RAF_HAS_CONNECTION_CLOSE;
+  return MHD_YES;
+}
+
+
+/**
+ * Remove tokens from "Connection:" header of the response.
+ *
+ * Provided tokens will be removed from "Connection:" header value.
+ *
+ * @param response the response to manipulate "Connection:" header
+ * @param value the tokens to remove
+ * @return #MHD_NO on error (no headers or tokens found).
+ */
+static enum MHD_Result
+del_response_header_connection (struct MHD_Response *response,
+                                const char *value)
+{
+  struct MHD_HTTP_Header *hdr; /**< existing "Connection" header */
+
+  hdr = MHD_get_response_element_n_ (response, MHD_HEADER_KIND, "Connection",
+                                     MHD_STATICSTR_LEN_ ("Connection"));
+  if (NULL == hdr)
+    return MHD_NO;
+
+  if (! MHD_str_remove_tokens_caseless_ (hdr->value, &hdr->value_size, value,
+                                         strlen (value)))
+    return MHD_NO;
+  if (0 == hdr->value_size)
+  {
+    _MHD_remove_header (response, hdr);
+    free (hdr->value);
+    free (hdr->header);
+    free (hdr);
+    response->flags_auto &= ~(MHD_RAF_HAS_CONNECTION_HDR
+                              | MHD_RAF_HAS_CONNECTION_CLOSE);
+  }
+  else
+  {
+    hdr->value[hdr->value_size] = 0; /* Null-terminate the result */
+    if (0 != (response->flags_auto & ~(MHD_RAF_HAS_CONNECTION_CLOSE)))
+    {
+      if (MHD_STATICSTR_LEN_ ("close") == hdr->value_size)
+      {
+        if (0 != memcmp (hdr->value, "close", MHD_STATICSTR_LEN_ ("close")))
+          response->flags_auto &= ~(MHD_RAF_HAS_CONNECTION_CLOSE);
+      }
+      else if (MHD_STATICSTR_LEN_ ("close, ") < hdr->value_size)
+      {
+        if (0 != memcmp (hdr->value, "close, ",
+                         MHD_STATICSTR_LEN_ ("close, ")))
+          response->flags_auto &= ~(MHD_RAF_HAS_CONNECTION_CLOSE);
+      }
+      else
+        response->flags_auto &= ~(MHD_RAF_HAS_CONNECTION_CLOSE);
+    }
+  }
+  return MHD_YES;
+}
+
+
 /**
  * Add a header line to the response.
  *
@@ -232,6 +453,9 @@ MHD_add_response_header (struct MHD_Response *response,
                          const char *header,
                          const char *content)
 {
+  if (MHD_str_equal_caseless_ (header, MHD_HTTP_HEADER_CONNECTION))
+    return add_response_header_connection (response, content);
+
   if (MHD_str_equal_caseless_ (header,
                                MHD_HTTP_HEADER_TRANSFER_ENCODING))
   {
@@ -301,7 +525,6 @@ MHD_del_response_header (struct MHD_Response *response,
                          const char *content)
 {
   struct MHD_HTTP_Header *pos;
-  struct MHD_HTTP_Header *prev;
   size_t header_len;
   size_t content_len;
 
@@ -309,8 +532,13 @@ MHD_del_response_header (struct MHD_Response *response,
        (NULL == content) )
     return MHD_NO;
   header_len = strlen (header);
+
+  if ((0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR)) &&
+      (MHD_STATICSTR_LEN_ ("Connection") == header_len) &&
+      MHD_str_equal_caseless_bin_n_ (header, "Connection", header_len))
+    return del_response_header_connection (response, content);
+
   content_len = strlen (content);
-  prev = NULL;
   pos = response->first_header;
   while (NULL != pos)
   {
@@ -329,7 +557,6 @@ MHD_del_response_header (struct MHD_Response *response,
       free (pos);
       return MHD_YES;
     }
-    prev = pos;
     pos = pos->next;
   }
   return MHD_NO;
diff --git a/src/microhttpd/test_response_entries.c 
b/src/microhttpd/test_response_entries.c
new file mode 100644
index 00000000..5a21b025
--- /dev/null
+++ b/src/microhttpd/test_response_entries.c
@@ -0,0 +1,618 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2021 Karlson2k (Evgeny Grin)
+
+     This test_response_entries.c file is in the public domain
+*/
+
+/**
+ * @file test_response_entries.c
+ * @brief  Test adding and removing response headers
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "mhd_options.h"
+#include "platform.h"
+#include <string.h>
+#include <microhttpd.h>
+
+
+static int
+expect_str (const char *actual, const char *expected)
+{
+  if (expected == actual)
+    return ! 0;
+  if (NULL == actual)
+  {
+    fprintf (stderr, "FAILED: result: NULL\n" \
+             "        expected: \"%s\"",
+             expected);
+    return 0;
+  }
+  if (NULL == expected)
+  {
+    fprintf (stderr, "FAILED: result: \"%s\"\n" \
+             "        expected: NULL",
+             actual);
+    return 0;
+  }
+  if (0 != strcmp (actual, expected))
+  {
+    fprintf (stderr, "FAILED: result: \"%s\"\n" \
+             "        expected: \"%s\"",
+             actual, expected);
+    return 0;
+  }
+  return ! 0;
+}
+
+
+int
+main (int argc,
+      char *const *argv)
+{
+  (void) argc;
+  (void) argv; /* Unused. Silence compiler warning. */
+  struct MHD_Response *r;
+
+  r = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT);
+  if (NULL == r)
+  {
+    fprintf (stderr, "Cannot create a response.\n");
+    return 1;
+  }
+
+  /* ** Test basic header functions ** */
+
+  /* Add first header */
+  if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a1"))
+  {
+    fprintf (stderr, "Cannot add header A1.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a1"))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Add second header with the same name */
+  if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a2"))
+  {
+    fprintf (stderr, "Cannot add header A2.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Value of the first header must be returned */
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a1"))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Remove the first header */
+  if (MHD_YES != MHD_del_response_header (r, "Header-Type-A", "value-a1"))
+  {
+    fprintf (stderr, "Cannot remove header A1.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Value of the ex-second header must be returned */
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a3"))
+  {
+    fprintf (stderr, "Cannot add header A2.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Value of the ex-second header must be returned */
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  /* Remove the last header */
+  if (MHD_YES != MHD_del_response_header (r, "Header-Type-A", "value-a3"))
+  {
+    fprintf (stderr, "Cannot add header A2.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Header-Type-B"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (MHD_NO != MHD_del_response_header (r, "Header-Type-C", "value-a3"))
+  {
+    fprintf (stderr, "Removed non-existing header.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+  if (MHD_NO != MHD_del_response_header (r, "Header-Type-A", "value-c"))
+  {
+    fprintf (stderr, "Removed non-existing header value.\n");
+    MHD_destroy_response (r);
+    return 2;
+  }
+
+  /* ** Test "Connection:" header ** */
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "a,b,c,d,e"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with simple values.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "a, b, c, d, 
e"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "e,b,c,d,a"))
+  {
+    fprintf (stderr,
+             "Cannot remove \"Connection\" header with simple values.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection",
+                                          "i,k,l,m,n,o,p,close"))
+  {
+    fprintf (stderr,
+             "Cannot add \"Connection\" header with simple values and 
\"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, i, k, l, m, n, o, p"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "i,k,l,m,n,o,p,close"))
+  {
+    fprintf (stderr,
+             "Cannot remove \"Connection\" header with simple values and 
\"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection",
+                                          "1,2,3,4,5,6,7,close"))
+  {
+    fprintf (stderr,
+             "Cannot add \"Connection\" header with simple values and 
\"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, 1, 2, 3, 4, 5, 6, 7"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "8,9,close"))
+  {
+    fprintf (stderr,
+             "Cannot add second \"Connection\" header with simple values and 
\"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, 1, 2, 3, 4, 5, 6, 7, 8, 9"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "1,3,5,7,9"))
+  {
+    fprintf (stderr,
+             "Cannot remove part of \"Connection\" header with simple 
values.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, 2, 4, 6, 8"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "10,12"))
+  {
+    fprintf (stderr,
+             "Cannot add third \"Connection\" header with simple values.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, 2, 4, 6, 8, 10, 12"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "12  ,10  ,8  ,close"))
+  {
+    fprintf (stderr,
+             "Cannot remove part of \"Connection\" header with simple values 
and \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "2, 4, 6"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\" 
only.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, 2, 4, 6"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "4  ,5,6,7  8,"))
+  {
+    fprintf (stderr,
+             "Cannot remove part of \"Connection\" header with simple values 
and non-existing tokens.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "close, 2"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\" 
only.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "close, 2"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "close, 10, 12, 22, nothing"))
+  {
+    fprintf (stderr,
+             "Cannot remove part of \"Connection\" header with \"close\" and 
non-existing tokens.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "2"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "2"))
+  {
+    fprintf (stderr,
+             "Cannot remove part of \"Connection\" header with simple values 
and non-existing tokens.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot remove \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection", 
"close,other-token"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, other-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close, new-token"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, other-token, new-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "close, new-token"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "other-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "other-token"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection",
+                                          "close, one-long-token"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, one-long-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, one-long-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "one-long-token,close"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection",
+                                          "close, additional-token"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, additional-token"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "additional-token,close"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "token-1,token-2"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "token-1, token-2"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "token-3"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "token-1, token-2, token-3"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, token-1, token-2, token-3"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, token-1, token-2, token-3"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close, token-4"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, token-1, token-2, token-3, token-4"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection", "close"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "token-1, token-2, token-3, token-4"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_add_response_header (r, "Connection", "close, token-5"))
+  {
+    fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, token-1, token-2, token-3, token-4, token-5"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_NO != MHD_del_response_header (r, "Connection",
+                                         "non-existing, token-9"))
+  {
+    fprintf (stderr,
+             "Non-existing tokens successfully removed from \"Connection\" 
header.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"),
+                    "close, token-1, token-2, token-3, token-4, token-5"))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_NO != MHD_add_response_header (r, "Connection",
+                                         ",,,,,,,,,,,,    ,\t\t\t, , , "))
+  {
+    fprintf (stderr,
+             "Empty token was added successfully to \"Connection\" header.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_YES != MHD_del_response_header (r, "Connection",
+                                          "close, token-1, token-2, token-3, 
token-4, token-5"))
+  {
+    fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (MHD_NO != MHD_add_response_header (r, "Connection",
+                                         ",,,,,,,,,,,,    ,\t\t\t, , , "))
+  {
+    fprintf (stderr,
+             "Empty token was added successfully to \"Connection\" header.\n");
+    MHD_destroy_response (r);
+    return 3;
+  }
+  if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+  {
+    MHD_destroy_response (r);
+    return 3;
+  }
+
+  MHD_destroy_response (r);
+  printf ("All tests has been successfully passed.\n");
+  return 0;
+}
diff --git a/src/microhttpd/test_str_token_remove.c 
b/src/microhttpd/test_str_token_remove.c
new file mode 100644
index 00000000..af1e7527
--- /dev/null
+++ b/src/microhttpd/test_str_token_remove.c
@@ -0,0 +1,248 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2017 Karlson2k (Evgeny Grin)
+
+  This test tool 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 2, or
+  (at your option) any later version.
+
+  This test tool 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+*/
+
+/**
+ * @file microhttpd/test_str_token.c
+ * @brief  Unit tests for some mhd_str functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "mhd_options.h"
+#include "mhd_str.h"
+#include "mhd_assert.h"
+
+
+static int
+expect_result_n (const char *str, size_t str_len,
+                 const char *token, size_t token_len,
+                 const char *expected, size_t expected_len,
+                 const bool expected_removed)
+{
+  char buf_in[1024];
+  char buf_token[256];
+  char buf_out[1024];
+  size_t buf_len;
+
+  mhd_assert (sizeof(buf_in) > str_len + 2);
+  mhd_assert (sizeof(buf_token) > token_len + 2);
+  mhd_assert (sizeof(buf_out) > expected_len + 2);
+
+  memset (buf_in, '#', sizeof(buf_in));
+  memset (buf_token, '#', sizeof(buf_token));
+  memcpy (buf_in, str, str_len); /* Copy without zero-termination */
+  memcpy (buf_token, token, token_len); /* Copy without zero-termination */
+
+  for (buf_len = 0; buf_len <= expected_len + 3; ++buf_len)
+  {
+    bool res;
+    ssize_t result_len;
+    memset (buf_out, '$', sizeof(buf_out));
+
+    result_len = buf_len;
+
+    res = MHD_str_remove_token_caseless_ (buf_in, str_len, buf_token, 
token_len,
+                                          buf_out, &result_len);
+    if (buf_len < expected_len)
+    { /* The result should not fit into the buffer */
+      if (res || (0 < result_len))
+      {
+        fprintf (stderr,
+                 "MHD_str_remove_token_caseless_() FAILED:\n"
+                 "\tMHD_str_remove_token_caseless_(\"%.*s\", %lu,"
+                 " \"%.*s\", %lu, buf, &(%ld->%ld)) returned %s\n",
+                 (int) str_len + 2, buf_in, (unsigned long) str_len,
+                 (int) token_len + 2, buf_token, (unsigned long) token_len,
+                 (long) buf_len, (long) result_len, res ? "true" : "false");
+        return 1;
+      }
+    }
+    else
+    { /* The result should fit into the buffer */
+      if ( (expected_removed != res) ||
+           (expected_len != (size_t) result_len) ||
+           ((0 != result_len) && (0 != memcmp (expected, buf_out,
+                                               result_len))) ||
+           ('$' != buf_out[result_len]))
+      {
+        fprintf (stderr,
+                 "MHD_str_remove_token_caseless_() FAILED:\n"
+                 "\tMHD_str_remove_token_caseless_(\"%.*s\", %lu,"
+                 " \"%.*s\", %lu, \"%.*s\", &(%ld->%ld)) returned %s\n",
+                 (int) str_len + 2, buf_in, (unsigned long) str_len,
+                 (int) token_len + 2, buf_token, (unsigned long) token_len,
+                 (int) expected_len + 2, buf_out,
+                 (long) buf_len, (long) result_len,
+                 res ? "true" : "false");
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+
+#define expect_result(s,t,e,found) \
+  expect_result_n ((s),MHD_STATICSTR_LEN_ (s), \
+                   (t),MHD_STATICSTR_LEN_ (t), \
+                   (e),MHD_STATICSTR_LEN_ (e), found)
+
+int
+check_result (void)
+{
+  int errcount = 0;
+  errcount += expect_result ("string", "string", "", true);
+  errcount += expect_result ("String", "string", "", true);
+  errcount += expect_result ("string", "String", "", true);
+  errcount += expect_result ("strinG", "String", "", true);
+  errcount += expect_result ("\t strinG", "String", "", true);
+  errcount += expect_result ("strinG\t ", "String", "", true);
+  errcount += expect_result (" \t tOkEn  ", "toKEN", "", true);
+  errcount += expect_result ("not token\t,  tOkEn  ", "toKEN", "not token",
+                             true);
+  errcount += expect_result ("not token,\t  tOkEn, more token", "toKEN",
+                             "not token, more token", true);
+  errcount += expect_result ("not token,\t  tOkEn\t, more token", "toKEN",
+                             "not token, more token", true);
+  errcount += expect_result (",,,,,,test,,,,", "TESt", "", true);
+  errcount += expect_result (",,,,,\t,test,,,,", "TESt", "", true);
+  errcount += expect_result (",,,,,,test, ,,,", "TESt", "", true);
+  errcount += expect_result (",,,,,, test,,,,", "TESt", "", true);
+  errcount += expect_result (",,,,,, test not,test,,", "TESt", "test not",
+                             true);
+  errcount += expect_result (",,,,,, test not,,test,,", "TESt", "test not",
+                             true);
+  errcount += expect_result (",,,,,, test not ,test,,", "TESt", "test not",
+                             true);
+  errcount += expect_result (",,,,,, test", "TESt", "", true);
+  errcount += expect_result (",,,,,, test      ", "TESt", "", true);
+  errcount += expect_result ("no test,,,,,, test      ", "TESt", "no test",
+                             true);
+  errcount += expect_result ("the-token,, the-token , the-token" \
+                             ",the-token ,the-token", "the-token", "", true);
+  errcount += expect_result (" the-token,, the-token , the-token," \
+                             "the-token ,the-token ", "the-token", "", true);
+  errcount += expect_result (" the-token ,, the-token , the-token," \
+                             "the-token , the-token ", "the-token", "", true);
+  errcount += expect_result ("the-token,a, the-token , the-token,b," \
+                             "the-token , c,the-token", "the-token", "a, b, c",
+                             true);
+  errcount += expect_result (" the-token, a, the-token , the-token, b," \
+                             "the-token ,c ,the-token ", "the-token",
+                             "a, b, c", true);
+  errcount += expect_result (" the-token , a , the-token , the-token, b ," \
+                             "the-token , c , the-token ", "the-token",
+                             "a, b, c",true);
+  errcount += expect_result ("the-token,aa, the-token , the-token,bb," \
+                             "the-token , cc,the-token", "the-token",
+                             "aa, bb, cc", true);
+  errcount += expect_result (" the-token, aa, the-token , the-token, bb," \
+                             "the-token ,cc ,the-token ", "the-token",
+                             "aa, bb, cc", true);
+  errcount += expect_result (" the-token , aa , the-token , the-token, bb ," \
+                             "the-token , cc , the-token ", "the-token",
+                             "aa, bb, cc", true);
+
+  errcount += expect_result ("strin", "string", "strin", false);
+  errcount += expect_result ("Stringer", "string", "Stringer", false);
+  errcount += expect_result ("sstring", "String", "sstring", false);
+  errcount += expect_result ("string", "Strin", "string", false);
+  errcount += expect_result ("\t( strinG", "String", "( strinG", false);
+  errcount += expect_result (")strinG\t ", "String", ")strinG", false);
+  errcount += expect_result (" \t tOkEn t ", "toKEN", "tOkEn t", false);
+  errcount += expect_result ("not token\t,  tOkEner  ", "toKEN",
+                             "not token, tOkEner", false);
+  errcount += expect_result ("not token,\t  tOkEns, more token", "toKEN",
+                             "not token, tOkEns, more token", false);
+  errcount += expect_result ("not token,\t  tOkEns\t, more token", "toKEN",
+                             "not token, tOkEns, more token", false);
+  errcount += expect_result (",,,,,,testing,,,,", "TESt", "testing", false);
+  errcount += expect_result (",,,,,\t,test,,,,", "TESting", "test", false);
+  errcount += expect_result ("tests,,,,,,quest, ,,,", "TESt", "tests, quest",
+                             false);
+  errcount += expect_result (",,,,,, testы,,,,", "TESt", "testы", false);
+  errcount += expect_result (",,,,,, test not,хtest,,", "TESt",
+                             "test not, хtest", false);
+  errcount += expect_result ("testing,,,,,, test not,,test2,,", "TESt",
+                             "testing, test not, test2", false);
+  errcount += expect_result (",testi,,,,, test not ,test,,", "TESting",
+                             "testi, test not, test", false);
+  errcount += expect_result (",,,,,,2 test", "TESt", "2 test", false);
+  errcount += expect_result (",,,,,,test test      ", "test", "test test",
+                             false);
+  errcount += expect_result ("no test,,,,,,test test", "test",
+                             "no test, test test", false);
+  errcount += expect_result (",,,,,,,,,,,,,,,,,,,", "the-token", "", false);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,", "the-token",
+                             "a, b, c, d, e, f, g", false);
+  errcount += expect_result (",,,,,,,,,,,,,,,,,,,", "", "", false);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,", "",
+                             "a, b, c, d, e, f, g", false);
+  errcount += expect_result ("a,b,c,d,e,f,g", "", "a, b, c, d, e, f, g",
+                             false);
+  errcount += expect_result ("a1,b1,c1,d1,e1,f1,g1", "",
+                             "a1, b1, c1, d1, e1, f1, g1", false);
+
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token",
+                             "the-token", "a, b, c, d, e, f, g", true);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token,",
+                             "the-token", "a, b, c, d, e, f, g", true);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token,x",
+                             "the-token", "a, b, c, d, e, f, g, x", true);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x",
+                             "the-token", "a, b, c, d, e, f, g, the-token x",
+                             false);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x,",
+                             "the-token", "a, b, c, d, e, f, g, the-token x",
+                             false);
+  errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x,x",
+                             "the-token", "a, b, c, d, e, f, g," \
+                             " the-token x, x", false);
+  errcount += expect_result ("the-token,a,b,c,d,e,f,g,,,,,,,,,,,,the-token",
+                             "the-token", "a, b, c, d, e, f, g", true);
+  errcount += expect_result ("the-token ,a,b,c,d,e,f,g,,,,,,,,,,,,the-token,",
+                             "the-token", "a, b, c, d, e, f, g", true);
+  errcount += expect_result ("the-token,a,b,c,d,e,f,g,,,,,,,,,,,,the-token,x",
+                             "the-token", "a, b, c, d, e, f, g, x", true);
+  errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+                             "the-token x", "the-token",
+                             "the-token x, a, b, c, d, e, f, g, the-token x",
+                             false);
+  errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+                             "the-token x,", "the-token",
+                             "the-token x, a, b, c, d, e, f, g, the-token x",
+                             false);
+  errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+                             "the-token x,x", "the-token",
+                             "the-token x, a, b, c, d, e, f, g, " \
+                             "the-token x, x", false);
+
+  return errcount;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int errcount = 0;
+  (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+  errcount += check_result ();
+  return errcount == 0 ? 0 : 1;
+}
diff --git a/src/microhttpd/test_str_tokens_remove.c 
b/src/microhttpd/test_str_tokens_remove.c
new file mode 100644
index 00000000..bc38e2cd
--- /dev/null
+++ b/src/microhttpd/test_str_tokens_remove.c
@@ -0,0 +1,282 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2017 Karlson2k (Evgeny Grin)
+
+  This test tool 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 2, or
+  (at your option) any later version.
+
+  This test tool 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+*/
+
+/**
+ * @file microhttpd/test_str_token.c
+ * @brief  Unit tests for some mhd_str functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "mhd_options.h"
+#include "mhd_str.h"
+#include "mhd_assert.h"
+
+
+static int
+expect_result_n (const char *str, size_t str_len,
+                 const char *tokens, size_t tokens_len,
+                 const char *expected, size_t expected_len,
+                 const bool expected_removed)
+{
+  char buf_in[1024];
+  char buf_tokens[256];
+  bool res;
+  size_t result_len;
+
+  mhd_assert (sizeof(buf_in) > str_len + 2);
+  mhd_assert (sizeof(buf_tokens) > tokens_len + 2);
+
+  memset (buf_tokens, '#', sizeof(buf_tokens));
+  memcpy (buf_tokens, tokens, tokens_len); /* Copy without zero-termination */
+  memset (buf_in, '$', sizeof(buf_in));
+  memcpy (buf_in, str, str_len); /* Copy without zero-termination */
+
+  result_len = str_len;
+
+  res = MHD_str_remove_tokens_caseless_ (buf_in, &result_len,
+                                         buf_tokens, tokens_len);
+
+  if ( (expected_removed != res) ||
+       (expected_len != result_len) ||
+       ((0 != result_len) && (0 != memcmp (expected, buf_in, result_len))) ||
+       ('$' != buf_in[str_len]))
+  {
+    fprintf (stderr,
+             "MHD_str_remove_tokens_caseless_() FAILED:\n"
+             "\tRESULT: "
+             "\tMHD_str_remove_token_caseless_(\"%s\"->\"%.*s\", &(%lu->%lu),"
+             " \"%.*s\", %lu) returned %s\n",
+             str,
+             (int) result_len, buf_in,
+             (unsigned long) str_len, (unsigned long) result_len,
+             (int) tokens_len, buf_tokens, (unsigned long) tokens_len,
+             res ? "true" : "false");
+    fprintf (stderr,
+             "\tEXPECTED: "
+             "\tMHD_str_remove_token_caseless_(\"%s\"->\"%s\", &(%lu->%lu),"
+             " \"%.*s\", %lu) returned %s\n",
+             str,
+             expected,
+             (unsigned long) str_len, (unsigned long) expected_len,
+             (int) tokens_len, buf_tokens, (unsigned long) tokens_len,
+             expected_removed ? "true" : "false");
+    return 1;
+  }
+  return 0;
+}
+
+
+#define expect_result(s,t,e,found) \
+  expect_result_n ((s),MHD_STATICSTR_LEN_ (s), \
+                   (t),MHD_STATICSTR_LEN_ (t), \
+                   (e),MHD_STATICSTR_LEN_ (e), found)
+
+int
+check_result (void)
+{
+  int errcount = 0;
+  errcount += expect_result ("string", "string", "", true);
+  errcount += expect_result ("String", "string", "", true);
+  errcount += expect_result ("string", "String", "", true);
+  errcount += expect_result ("strinG", "String", "", true);
+  errcount += expect_result ("strinG", "String\t", "", true);
+  errcount += expect_result ("strinG", "\tString", "", true);
+  errcount += expect_result ("tOkEn", " \t toKEN  ", "", true);
+  errcount += expect_result ("not-token, tOkEn", "token", "not-token",
+                             true);
+  errcount += expect_result ("not-token, tOkEn, toke", "token",
+                             "not-token, toke",
+                             true);
+  errcount += expect_result ("toke, tOkEn", "token", "toke",
+                             true);
+  errcount += expect_result ("not-token, tOkEn", " \t toKEN", "not-token",
+                             true);
+  errcount += expect_result ("not-token, tOkEn, more-token", "toKEN\t",
+                             "not-token, more-token", true);
+  errcount += expect_result ("not-token, tOkEn, more-token", "\t  toKEN,,,,,",
+                             "not-token, more-token", true);
+  errcount += expect_result ("a, b, c, d", ",,,,,a", "b, c, d", true);
+  errcount += expect_result ("a, b, c, d", "a,,,,,,", "b, c, d", true);
+  errcount += expect_result ("a, b, c, d", ",,,,a,,,,,,", "b, c, d", true);
+  errcount += expect_result ("a, b, c, d", "\t \t,,,,a,,   ,   ,,,\t",
+                             "b, c, d", true);
+  errcount += expect_result ("a, b, c, d", "b, c, d", "a", true);
+  errcount += expect_result ("a, b, c, d", "a, b, c, d", "", true);
+  errcount += expect_result ("a, b, c, d", "d, c, b, a", "", true);
+  errcount += expect_result ("a, b, c, d", "b, d, a, c", "", true);
+  errcount += expect_result ("a, b, c, d, e", "b, d, a, c", "e", true);
+  errcount += expect_result ("e, a, b, c, d", "b, d, a, c", "e", true);
+  errcount += expect_result ("e, a, b, c, d, e", "b, d, a, c", "e, e", true);
+  errcount += expect_result ("a, b, c, d", "b,c,d", "a", true);
+  errcount += expect_result ("a, b, c, d", "a,b,c,d", "", true);
+  errcount += expect_result ("a, b, c, d", "d,c,b,a", "", true);
+  errcount += expect_result ("a, b, c, d", "b,d,a,c", "", true);
+  errcount += expect_result ("a, b, c, d, e", "b,d,a,c", "e", true);
+  errcount += expect_result ("e, a, b, c, d", "b,d,a,c", "e", true);
+  errcount += expect_result ("e, a, b, c, d, e", "b,d,a,c", "e, e", true);
+  errcount += expect_result ("a, b, c, d", "d,,,,,,,,,c,b,a", "", true);
+  errcount += expect_result ("a, b, c, d", "b,d,a,c,,,,,,,,,,", "", true);
+  errcount += expect_result ("a, b, c, d, e", ",,,,\t,,,,b,d,a,c,\t", "e",
+                             true);
+  errcount += expect_result ("e, a, b, c, d", "b,d,a,c", "e", true);
+  errcount += expect_result ("token, a, b, c, d", "token", "a, b, c, d", true);
+  errcount += expect_result ("token1, a, b, c, d", "token1", "a, b, c, d",
+                             true);
+  errcount += expect_result ("token12, a, b, c, d", "token12", "a, b, c, d",
+                             true);
+  errcount += expect_result ("token123, a, b, c, d", "token123", "a, b, c, d",
+                             true);
+  errcount += expect_result ("token1234, a, b, c, d", "token1234", "a, b, c, 
d",
+                             true);
+  errcount += expect_result ("token12345, a, b, c, d", "token12345",
+                             "a, b, c, d", true);
+  errcount += expect_result ("token123456, a, b, c, d", "token123456",
+                             "a, b, c, d", true);
+  errcount += expect_result ("token1234567, a, b, c, d", "token1234567",
+                             "a, b, c, d", true);
+  errcount += expect_result ("token12345678, a, b, c, d", "token12345678",
+                             "a, b, c, d", true);
+
+  errcount += expect_result ("", "a", "", false);
+  errcount += expect_result ("", "", "", false);
+  errcount += expect_result ("a, b, c, d", "bb, dd, aa, cc", "a, b, c, d",
+                             false);
+  errcount += expect_result ("a, b, c, d, e", "bb, dd, aa, cc", "a, b, c, d, 
e",
+                             false);
+  errcount += expect_result ("e, a, b, c, d", "bb, dd, aa, cc", "e, a, b, c, 
d",
+                             false);
+  errcount += expect_result ("e, a, b, c, d, e", "bb, dd, aa, cc",
+                             "e, a, b, c, d, e", false);
+  errcount += expect_result ("aa, bb, cc, dd", "b, d, a, c", "aa, bb, cc, dd",
+                             false);
+  errcount += expect_result ("aa, bb, cc, dd, ee", "b, d, a, c",
+                             "aa, bb, cc, dd, ee", false);
+  errcount += expect_result ("ee, aa, bb, cc, dd", "b, d, a, c",
+                             "ee, aa, bb, cc, dd", false);
+  errcount += expect_result ("ee, aa, bb, cc, dd, ee", "b, d, a, c",
+                             "ee, aa, bb, cc, dd, ee", false);
+
+  errcount += expect_result ("TESt", ",,,,,,test,,,,", "", true);
+  errcount += expect_result ("TESt", ",,,,,\t,test,,,,", "", true);
+  errcount += expect_result ("TESt", ",,,,,,test, ,,,", "", true);
+  errcount += expect_result ("TESt", ",,,,,, test,,,,", "", true);
+  errcount += expect_result ("TESt", ",,,,,, test-not,test,,", "",
+                             true);
+  errcount += expect_result ("TESt", ",,,,,, test-not,,test,,", "",
+                             true);
+  errcount += expect_result ("TESt", ",,,,,, test-not ,test,,", "",
+                             true);
+  errcount += expect_result ("TESt", ",,,,,, test", "", true);
+  errcount += expect_result ("TESt", ",,,,,, test      ", "", true);
+  errcount += expect_result ("TESt", "no-test,,,,,, test      ", "",
+                             true);
+
+  errcount += expect_result ("the-token, a, the-token, b, the-token, " \
+                             "the-token, c, the-token", "the-token", "a, b, c",
+                             true);
+  errcount += expect_result ("aa, the-token, bb, the-token, cc, the-token, " \
+                             "the-token, dd, the-token", "the-token",
+                             "aa, bb, cc, dd", true);
+  errcount += expect_result ("the-token, a, the-token, b, the-token, " \
+                             "the-token, c, the-token, e", "the-token",
+                             "a, b, c, e", true);
+  errcount += expect_result ("aa, the-token, bb, the-token, cc, the-token, " \
+                             "the-token, dd, the-token, ee", "the-token",
+                             "aa, bb, cc, dd, ee", true);
+  errcount += expect_result ("the-token, the-token, the-token, " \
+                             "the-token, the-token", "the-token", "", true);
+  errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+                             "the-token, c, the-token, a", "c,a,b",
+                             "the-token, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+                             "the-token, yy, the-token, ww", "ww,zz,yy",
+                             "the-token, xx, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+                             "the-token, c, the-token, a", " c,\t a,b,,,",
+                             "the-token, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+                             "the-token, yy, the-token, ww",
+                             ",,,,ww,\t zz,  yy",
+                             "the-token, xx, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+                             "the-token, c, the-token, a", ",,,,c,\t a,b",
+                             "the-token, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+                             "the-token, yy, the-token, ww", " ww,\t 
zz,yy,,,,",
+                             "the-token, xx, the-token, the-token, the-token, 
the-token",
+                             true);
+  errcount += expect_result ("close, 2", "close",
+                             "2", true);
+  errcount += expect_result ("close, 22", "close",
+                             "22", true);
+  errcount += expect_result ("close, nothing", "close",
+                             "nothing", true);
+  errcount += expect_result ("close, 2", "2",
+                             "close", true);
+  errcount += expect_result ("close", "close",
+                             "", true);
+  errcount += expect_result ("close, nothing", "close, token",
+                             "nothing", true);
+  errcount += expect_result ("close, nothing", "nothing, token",
+                             "close", true);
+  errcount += expect_result ("close, 2", "close, 10, 12, 22, nothing",
+                             "2", true);
+
+  errcount += expect_result ("strin", "string", "strin", false);
+  errcount += expect_result ("Stringer", "string", "Stringer", false);
+  errcount += expect_result ("sstring", "String", "sstring", false);
+  errcount += expect_result ("string", "Strin", "string", false);
+  errcount += expect_result ("String", "\t(-strinG", "String", false);
+  errcount += expect_result ("String", ")strinG\t ", "String", false);
+  errcount += expect_result ("not-token, tOkEner", "toKEN",
+                             "not-token, tOkEner", false);
+  errcount += expect_result ("not-token, tOkEns, more-token", "toKEN",
+                             "not-token, tOkEns, more-token", false);
+  errcount += expect_result ("tests, quest", "TESt", "tests, quest",
+                             false);
+  errcount += expect_result ("testы", "TESt", "testы", false);
+  errcount += expect_result ("test-not, хtest", "TESt",
+                             "test-not, хtest", false);
+  errcount += expect_result ("testing, test not, test2", "TESt",
+                             "testing, test not, test2", false);
+  errcount += expect_result ("", ",,,,,,,,,,,,,,,,,,,the-token", "", false);
+  errcount += expect_result ("a1, b1, c1, d1, e1, f1, g1", "",
+                             "a1, b1, c1, d1, e1, f1, g1", false);
+
+  return errcount;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int errcount = 0;
+  (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+  errcount += check_result ();
+  if (0 == errcount)
+    printf ("All tests were passed without errors.\n");
+  return errcount == 0 ? 0 : 1;
+}

-- 
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]