libmicrohttpd

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

commit e1693575ed0edfd2c0f09abcfd1df3021c1f793e
parent ed3daed30580e6c4ae62d4114c58e38b6f0f9f85
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Mon,  5 Jul 2021 12:57:34 +0300

response: process "Connection" header in a special manner

Diffstat:
Msrc/microhttpd/.gitignore | 1+
Msrc/microhttpd/Makefile.am | 8+++++++-
Msrc/microhttpd/internal.h | 17+++++++++++++++++
Msrc/microhttpd/response.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd/test_response_entries.c | 618+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 873 insertions(+), 1 deletion(-)

diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore @@ -55,6 +55,7 @@ test_http_reasons /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 @@ -166,6 +166,7 @@ check_PROGRAMS = \ test_sha256 \ test_start_stop \ test_daemon \ + test_response_entries \ test_postprocessor_md \ test_options @@ -217,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 = \ diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -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. @@ -511,6 +523,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()). */ bool is_pipe; diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -194,6 +194,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. * * When reply is generated with queued response, some headers are generated @@ -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)) { @@ -308,6 +532,12 @@ 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); pos = response->first_header; while (NULL != pos) diff --git a/src/microhttpd/test_response_entries.c 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; +}