gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated: verbatim import of David Gausmann


From: gnunet
Subject: [libmicrohttpd] branch master updated: verbatim import of David Gausmann's websocket extension; tests do not yet pass, only enabled with --enable-experimental
Date: Mon, 26 Apr 2021 14:14:49 +0200

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

grothoff pushed a commit to branch master
in repository libmicrohttpd.

The following commit(s) were added to refs/heads/master by this push:
     new c488cadb verbatim import of David Gausmann's websocket extension; 
tests do not yet pass, only enabled with --enable-experimental
c488cadb is described below

commit c488cadbc17253178c418f52ba9a321816f0080b
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Apr 26 14:12:23 2021 +0200

    verbatim import of David Gausmann's websocket extension; tests do not yet 
pass, only enabled with --enable-experimental
---
 .gitignore                         |    1 +
 ChangeLog                          |    3 +
 configure.ac                       |    5 +-
 src/Makefile.am                    |    4 +-
 src/include/Makefile.am            |    6 +-
 src/include/microhttpd_ws.h        |  979 ++++
 src/microhttpd/Makefile.am         |    4 +
 src/microhttpd_ws/Makefile.am      |   39 +
 src/microhttpd_ws/mhd_websocket.c  | 2072 +++++++++
 src/microhttpd_ws/sha1.c           |  420 ++
 src/microhttpd_ws/sha1.h           |  145 +
 src/microhttpd_ws/test_websocket.c | 8983 ++++++++++++++++++++++++++++++++++++
 12 files changed, 12656 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index 14a1a2b3..be7d81f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,3 +62,4 @@ src/examples/suspend_resume_epoll
 uncrustify.cfg
 **.dvi
 **.t2d
+src/microhttpd_ws/test_websocket
diff --git a/ChangeLog b/ChangeLog
index 13364d82..020711bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+Mon 26 Apr 2021 02:09:46 PM CEST
+    Importing experimental Websocket support by David Gausmann. -CG
+
 Sun 25 Apr 2021 14:00:00 MSK
     Releasing GNU libmicrohttpd 0.9.73. -EG
 
diff --git a/configure.ac b/configure.ac
index ea49349b..cfe3e554 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 # This file is part of libmicrohttpd.
-# (C) 2006-2020 Christian Grothoff (and other contributing authors)
+# (C) 2006-2021 Christian Grothoff (and other contributing authors)
 #
 # libmicrohttpd is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published
@@ -935,7 +935,7 @@ test "x$enable_examples" = "xno" || enable_examples=yes
 AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"])
 
 AC_ARG_ENABLE([[heavy-tests]],
-  [AS_HELP_STRING([[--enable-heavy-tests]], [use heavy tests in test-suite. 
WARNING:] 
+  [AS_HELP_STRING([[--enable-heavy-tests]], [use heavy tests in test-suite. 
WARNING:]
   [a dedicated host with minimal number of background processes and no network]
   [activity is recommended to enable.])], [],
     [enable_heavy_tests=no])
@@ -2413,6 +2413,7 @@ src/Makefile
 src/include/Makefile
 src/lib/Makefile
 src/microhttpd/Makefile
+src/microhttpd_ws/Makefile
 src/examples/Makefile
 src/testcurl/Makefile
 src/testcurl/https/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 1f43e73d..c6e52abc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,11 +16,11 @@ endif
 
 # Finally (last!) also build experimental lib...
 if HAVE_EXPERIMENTAL
-SUBDIRS += lib
+SUBDIRS += microhttpd_ws lib
 endif
 
 EXTRA_DIST = \
  datadir/cert-and-key.pem \
- datadir/cert-and-key-for-wireshark.pem 
+ datadir/cert-and-key-for-wireshark.pem
 
 .NOTPARALLEL:
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 29c91c98..3d73c598 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -1,7 +1,11 @@
 # This Makefile.am is in the public domain
 SUBDIRS = .
 
-include_HEADERS = microhttpd.h 
+include_HEADERS = microhttpd.h
 noinst_HEADERS = microhttpd2.h microhttpd_tls.h
 
+if HAVE_EXPERIMENTAL
+include_HEADERS += microhttpd_ws.h
+endif
+
 EXTRA_DIST = platform.h autoinit_funcs.h mhd_options.h
diff --git a/src/include/microhttpd_ws.h b/src/include/microhttpd_ws.h
new file mode 100644
index 00000000..915ca339
--- /dev/null
+++ b/src/include/microhttpd_ws.h
@@ -0,0 +1,979 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2021 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library 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_ws.h
+ * @brief interface for experimental web socket extension to libmicrohttpd
+ * @author David Gausmann
+ */
+#ifndef MHD_MICROHTTPD_WS_H
+#define MHD_MICROHTTPD_WS_H
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * @brief Handle for the encoding/decoding of websocket data
+ *        (one stream is used per websocket)
+ * @ingroup websocket
+ */
+struct MHD_WebSocketStream;
+
+/**
+ * @brief Flags for the initialization of a websocket stream
+ *        `struct MHD_WebSocketStream` used by
+ *        #MHD_websocket_stream_init() or
+ *        #MHD_websocket_stream_init2().
+ * @ingroup websocket
+ */
+enum MHD_WEBSOCKET_FLAG
+{
+  /**
+   * The websocket is used by the server (default).
+   * Thus all outgoing payload will not be "masked".
+   * All incoming payload must be masked.
+   * This cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT
+   */
+  MHD_WEBSOCKET_FLAG_SERVER = 0,
+  /**
+   * The websocket is used by the client
+   * (not used if you provide the server).
+   * Thus all outgoing payload will be "masked" (XOR-ed with random values).
+   * All incoming payload must be unmasked.
+   * Please note that this implementation doesn't use a strong random
+   * number generator for the mask as suggested in RFC6455 10.3, because
+   * the main intention of this implemention is the use as server
+   * with MHD, which doesn't need masking.
+   * Instead a weak random number generator is used (`rand()`).
+   * You can set the seed for the random number generator
+   * by calling #MHD_websocket_srand().
+   * This cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER
+   */
+  MHD_WEBSOCKET_FLAG_CLIENT = 1,
+  /**
+   * You don't want to get fragmented data while decoding.
+   * Fragmented frames will be internally put together until
+   * they are complete.
+   * Whether or not data is fragmented is decided
+   * by the sender of the data during encoding.
+   * This cannot be used together with #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+   */
+  MHD_WEBSOCKET_FLAG_NO_FRAGMENTS = 0,
+  /**
+   * You want fragmented data, if it appears while decoding.
+   * You will receive the content of the fragmented frame,
+   * but if you are decoding text, you will never get an unfinished
+   * UTF-8 sequences (if the sequence appears between two fragments).
+   * Instead the text will end before the unfinished UTF-8 sequence.
+   * With the next fragment, which finishes the UTF-8 sequence,
+   * you will get the complete UTF-8 sequence.
+   * This cannot be used together with #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+   */
+  MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS = 2,
+  /**
+   * If the websocket stream becomes invalid during decoding due to
+   * protocol errors, a matching close frame will automatically
+   * be generated.
+   * The close frame will be returned via the parameters
+   * result and result_len of #MHD_websocket_decode() and
+   * the return value is negative
+   * (a value of `enum MHD_WEBSOCKET_STATUS`).
+   * The generated close frame must be freed by the caller
+   * with #MHD_websocket_free().
+   */
+  MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR = 4
+};
+
+/**
+ * @brief Enum to specify the fragmenting behavior
+ *        while encoding with #MHD_websocket_encode_text() or
+ *        #MHD_websocket_encode_binary().
+ * @ingroup websocket
+ */
+enum MHD_WEBSOCKET_FRAGMENTATION
+{
+  /**
+   * You don't want to use fragmentation.
+   * The encoded frame consists of only one frame.
+   */
+  MHD_WEBSOCKET_FRAGMENTATION_NONE = 0,
+  /**
+   * You want to use fragmentation.
+   * The encoded frame is the first frame of
+   * a series of data frames of the same type
+   * (text or binary).
+   * You may send control frames (ping, pong or close)
+   * between these data frames.
+   */
+  MHD_WEBSOCKET_FRAGMENTATION_FIRST = 1,
+  /**
+   * You want to use fragmentation.
+   * The encoded frame is not the first frame of
+   * the series of data frames, but also not the last one.
+   * You may send control frames (ping, pong or close)
+   * between these data frames.
+   */
+  MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING = 2,
+  /**
+   * You want to use fragmentation.
+   * The encoded frame is the last frame of
+   * the series of data frames, but also not the first one.
+   * After this frame, you may send all type of frames again.
+   */
+  MHD_WEBSOCKET_FRAGMENTATION_LAST = 3
+};
+
+/**
+ * @brief Enum of the return value for almost every MHD_websocket function.
+ *        Errors are negative and values equal to or above zero mean a success.
+ *        Positive values are only used by #MHD_websocket_decode().
+ * @ingroup websocket
+ */
+enum MHD_WEBSOCKET_STATUS
+{
+  /**
+   * The call succeeded.
+   * For #MHD_websocket_decode() this means that no error occurred,
+   * but also no frame has been completed yet.
+   */
+  MHD_WEBSOCKET_STATUS_OK = 0,
+  /**
+   * #MHD_websocket_decode() has decoded a text frame.
+   * The parameters result and result_len are filled with the decoded text
+   * (if any).
+   */
+  MHD_WEBSOCKET_STATUS_TEXT_FRAME = 0x1,
+  /**
+   * #MHD_websocket_decode() has decoded a binary frame.
+   * The parameters result and result_len are filled with the decoded
+   * binary data (if any).
+   */
+  MHD_WEBSOCKET_STATUS_BINARY_FRAME = 0x2,
+  /**
+   * #MHD_websocket_decode() has decoded a close frame.
+   * This means you must close the socket using #MHD_upgrade_action()
+   * with #MHD_UPGRADE_ACTION_CLOSE.
+   * You may respond with a close frame before closing.
+   * The parameters result and result_len are filled with
+   * the close reason (if any).
+   * The close reason starts with a two byte sequence of close code
+   * in network byte order (see `enum MHD_WEBSOCKET_CLOSEREASON`).
+   * After these two bytes a UTF-8 encoded close reason may follow.
+   * Compare with result_len to decide whether there is any close reason.
+   */
+  MHD_WEBSOCKET_STATUS_CLOSE_FRAME = 0x8,
+  /**
+   * #MHD_websocket_decode() has decoded a ping frame.
+   * You should respond to this with a pong frame.
+   * The pong frame must contain the same binary data as
+   * the corresponding ping frame (if it had any).
+   * The parameters result and result_len are filled with
+   * the binary ping data (if any).
+   */
+  MHD_WEBSOCKET_STATUS_PING_FRAME = 0x9,
+  /**
+   * #MHD_websocket_decode() has decoded a pong frame.
+   * You should usually only receive pong frames if you sent
+   * a ping frame before.
+   * The binary data should be equal to your ping frame and can be
+   * used to distinguish the response if you sent multiple ping frames.
+   * The parameters result and result_len are filled with
+   * the binary pong data (if any).
+   */
+  MHD_WEBSOCKET_STATUS_PONG_FRAME = 0xA,
+  /**
+   * #MHD_websocket_decode() has decoded a text frame fragment.
+   * The parameters result and result_len are filled with the decoded text
+   * (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAME, but it can only
+   * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during
+   * the call of #MHD_websocket_stream_init() or
+   * #MHD_websocket_stream_init2().
+   */
+  MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT = 0x11,
+  /**
+   * #MHD_websocket_decode() has decoded a binary frame fragment.
+   * The parameters result and result_len are filled with the decoded
+   * binary data (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAME, but it can only
+   * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during
+   * the call of #MHD_websocket_stream_init() or
+   * #MHD_websocket_stream_init2().
+   */
+  MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT = 0x12,
+  /**
+  * #MHD_websocket_decode() has decoded the last text frame fragment.
+  * The parameters result and result_len are filled with the decoded text
+  * (if any).
+  * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, but it appears
+  * only for the last fragment of a series of fragments.
+  * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+  * during the call of #MHD_websocket_stream_init() or
+  * #MHD_websocket_stream_init2().
+  */
+  MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT = 0x21,
+  /**
+  * #MHD_websocket_decode() has decoded the last binary frame fragment.
+  * The parameters result and result_len are filled with the decoded
+  * binary data (if any).
+  * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, but it appears
+  * only for the last fragment of a series of fragments.
+  * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+  * during the call of #MHD_websocket_stream_init() or
+  * #MHD_websocket_stream_init2().
+  */
+  MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT = 0x22,
+  /**
+   * The call failed and the stream is invalid now for decoding.
+   * You must close the websocket now using #MHD_upgrade_action()
+   * with #MHD_UPGRADE_ACTION_CLOSE.
+   * You can send a close frame before closing.
+   * This is only used by #MHD_websocket_decode() and happens
+   * if the stream contains errors (i. e. invalid byte data).
+   */
+  MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR = -1,
+  /**
+   * You tried to decode something, but the stream has already
+   * been marked invalid.
+   * You must close the websocket now using #MHD_upgrade_action()
+   * with #MHD_UPGRADE_ACTION_CLOSE.
+   * You can send a close frame before closing.
+   * This is only used by #MHD_websocket_decode() and happens
+   * if you call #MDM_websocket_decode() again after is
+   * has been invalidated.
+   */
+  MHD_WEBSOCKET_STATUS_STREAM_BROKEN = -2,
+  /**
+   * A memory allocation failed. The stream remains valid.
+   * If this occurred while decoding, the decoding could be
+   * possible later if enough memory is available.
+   * This could happen while decoding if you received a too big data frame.
+   * You could try to specify max_payload_size during the call of
+   * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2() then to
+   * avoid this and close the frame instead.
+   */
+  MHD_WEBSOCKET_STATUS_MEMORY_ERROR = -3,
+  /**
+   * You passed invalid parameters during the function call
+   * (i. e. a NULL pointer for a required parameter).
+   * The stream remains valid.
+   */
+  MHD_WEBSOCKET_STATUS_PARAMETER_ERROR = -4,
+  /**
+   * The maximum payload size has been exceeded.
+   * If you got this return code from #MHD_websocket_decode() then
+   * the stream becomes invalid and the websocket must be closed
+   * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE.
+   * You can send a close frame before closing.
+   * The maximum payload size is specified during the call of
+   * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2().
+   * This can also appear if you specified 0 as maximum payload size
+   * when the message is greater than the maximum allocatable memory size
+   * (i. e. more than 4 GB on 32 bit systems).
+   * If you got this return code from #MHD_websocket_encode_close(),
+   * #MHD_websocket_encode_ping() or #MHD_websocket_encode_pong() then
+   * you passed to much payload data. The stream remains valid then.
+   */
+  MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED = -5,
+  /**
+   * An UTF-8 text is invalid.
+   * If you got this return code from #MHD_websocket_decode() then
+   * the stream becomes invalid and you must close the websocket
+   * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE.
+   * You can send a close frame before closing.
+   * If you got this from #MHD_websocket_encode_text() or
+   * #MHD_websocket_encode_close() then you passed invalid UTF-8 text.
+   * The stream remains valid then.
+   */
+  MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR = -6
+};
+
+/**
+ * @brief Enumeration of possible close reasons for close frames.
+ *
+ * The possible values are specified in RFC 6455 7.4.1
+ * These close reasons here are the default set specified by RFC 6455,
+ * but also other close reasons could be used.
+ *
+ * The definition is for short:
+ * 0-999 are never used (if you pass 0 in
+ *   #MHD_websocket_encode_close() then no close reason is used).
+ * 1000-2999 are specified by RFC 6455.
+ * 3000-3999 are specified by libraries, etc. but must be registered by IANA.
+ * 4000-4999 are reserved for private use.
+ *
+ * @ingroup websocket
+ */
+enum MHD_WEBSOCKET_CLOSEREASON
+{
+  /**
+   * This value is used as placeholder for #MHD_websocket_encode_close()
+   * to tell that you don't want to specify any reason.
+   * If you use this value then no reason text may be used.
+   * This value cannot a result of decoding, because this value
+   * is not a valid close reason for the WebSocket protocol.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_NO_REASON = 0,
+  /**
+   * You close the websocket fulfilled its purpose and shall
+   * now be closed in a normal, planned way.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_REGULAR = 1000,
+  /**
+   * You close the websocket because are shutting down the server or
+   * something similar.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001,
+  /**
+   * You close the websocket because you a protocol error occurred
+   * during decoding (i. e. invalid byte data).
+   */
+  MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR = 1002,
+  /**
+   * You close the websocket because you received data which you don't accept.
+   * For example if you received a binary frame,
+   * but your application only expects text frames.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE = 1003,
+  /**
+   * You close the websocket because it contains malformed UTF-8.
+   * The UTF-8 validity is automatically checked by #MHD_websocket_decode(),
+   * so you don't need to check it on your own.
+   * UTF-8 is specified in RFC 3629.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8 = 1007,
+  /**
+   * You close the websocket because of any reason.
+   * Usually this close reason is used if no other close reason
+   * is more specific or if you don't want to use any other close reason.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED = 1008,
+  /**
+   * You close the websocket because you received a frame which is too big to 
process.
+   * You can specify the maximum allowed payload size during the call of
+   * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2().
+   */
+  MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED = 1009,
+  /**
+   * This status code can be sent by the client if it
+   * expected a specific extension, but this extension hasn't been negotiated.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION = 1010,
+  /**
+   * The server closes the websocket because it encountered
+   * an unexpected condition that prevented it from fulfilling the request.
+   */
+  MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION = 1011
+};
+
+/**
+ * @brief Enumeration of possible UTF-8 check steps
+ *
+ * These values are used during the encoding of fragmented text frames
+ * or for error analysis while encoding text frames.
+ * Its values specify the next step of the UTF-8 check.
+ * UTF-8 sequences consist of one to four bytes.
+ * This enumeration just says how long the current UTF-8 sequence is
+ * and what is the next expected byte.
+ *
+ * @ingroup websocket
+ */
+enum MHD_WEBSOCKET_UTF8STEP
+{
+  /**
+   * There is no open UTF-8 sequence.
+   * The next byte must be 0x00-0x7F or 0xC2-0xF4.
+   */
+  MHD_WEBSOCKET_UTF8STEP_NORMAL   = 0,
+  /**
+   * The second byte of a two byte UTF-8 sequence.
+   * The first byte was 0xC2-0xDF.
+   * The next byte must be 0x80-0xBF.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 = 1,
+  /**
+   * The second byte of a three byte UTF-8 sequence.
+   * The first byte was 0xE0.
+   * The next byte must be 0xA0-0xBF.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2 = 2,
+  /**
+  * The second byte of a three byte UTF-8 sequence.
+  * The first byte was 0xED.
+  * The next byte must by 0x80-0x9F.
+  */
+  MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2 = 3,
+  /**
+  * The second byte of a three byte UTF-8 sequence.
+  * The first byte was 0xE1-0xEC or 0xEE-0xEF.
+  * The next byte must be 0x80-0xBF.
+  */
+  MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2 = 4,
+  /**
+  * The third byte of a three byte UTF-8 sequence.
+  * The next byte must be 0x80-0xBF.
+  */
+  MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2 = 5,
+  /**
+   * The second byte of a four byte UTF-8 sequence.
+   * The first byte was 0xF0.
+   * The next byte must be 0x90-0xBF.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3 = 6,
+  /**
+   * The second byte of a four byte UTF-8 sequence.
+   * The first byte was 0xF4.
+   * The next byte must be 0x80-0x8F.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3 = 7,
+  /**
+   * The second byte of a four byte UTF-8 sequence.
+   * The first byte was 0xF1-0xF3.
+   * The next byte must be 0x80-0xBF.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3 = 8,
+  /**
+   * The third byte of a four byte UTF-8 sequence.
+   * The next byte must be 0x80-0xBF.
+   */
+  MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3 = 9,
+  /**
+  * The fourth byte of a four byte UTF-8 sequence.
+  * The next byte must be 0x80-0xBF.
+  */
+  MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3 = 10
+};
+
+/**
+* @brief Enumeration of validity values
+*
+* These values are used for #MHD_websocket_stream_is_valid()
+* and specifiy the validity status.
+*
+* @ingroup websocket
+*/
+enum MHD_WEBSOCKET_VALIDITY
+{
+  /**
+  * The stream is invalid.
+  * It cannot be used for decoding anymore.
+  */
+  MHD_WEBSOCKET_VALIDITY_INVALID = 0,
+  /**
+   * The stream is valid.
+   * Decoding works as expected.
+   */
+  MHD_WEBSOCKET_VALIDITY_VALID   = 1,
+  /**
+   * The stream has received a close frame and
+   * is partly invalid.
+   * You can still use the stream for decoding,
+   * but if a data frame is received an error will be reported.
+   * After a close frame has been sent, no data frames
+   * may follow from the sender of the close frame.
+   */
+  MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES = 2
+};
+/**
+ * This method is called by many websocket
+ * functions for allocating data.
+ * By default 'malloc' is used.
+ * This can be used for operating systems like Windows
+ * where malloc, realloc and free are compiler dependent.
+ *
+ * @param len new size
+ * @return allocated memory
+ * @ingroup websocket
+ */
+typedef void*
+(*MHD_WebSocketMallocCallback) (size_t len);
+/**
+ * This method is called by many websocket
+ * functions for reallocating data.
+ * By default 'realloc' is used.
+ * This can be used for operating systems like Windows
+ * where malloc, realloc and free are compiler dependent.
+ *
+ * @param cls closure
+ * @param len new size
+ * @return reallocated memory
+ * @ingroup websocket
+ */
+typedef void*
+(*MHD_WebSocketReallocCallback) (void *cls, size_t len);
+/**
+ * This method is called by many websocket
+ * functions for freeing data.
+ * By default 'free' is used.
+ * This can be used for operating systems like Windows
+ * where malloc, realloc and free are compiler dependent.
+ *
+ * @param cls closure
+ * @ingroup websocket
+ */
+typedef void
+(*MHD_WebSocketFreeCallback) (void *cls);
+
+/**
+ * Creates the response value for the incoming 'Sec-WebSocket-Key' header.
+ * The generated value must be sent to the client as 'Sec-WebSocket-Accept' 
response header.
+ *
+ * @param sec_websocket_key The value of the 'Sec-WebSocket-Key' request header
+ * @param[out] sec_websocket_accept The response buffer, which will receive
+ *                                  the generated 'Sec-WebSocket-Accept' 
header.
+ *                                  This buffer must be at least 29 bytes long 
and
+ *                                  will contain a terminating NUL character.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_create_accept (const char*sec_websocket_key,
+                             char*sec_websocket_accept);
+
+/**
+ * Creates a new websocket stream, used for decoding/encoding.
+ *
+ * @param[out] ws The websocket stream
+ * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values
+ *              to modify the behavior of the websocket stream.
+ * @param max_message_size The maximum size for incoming payload
+ *                         data in bytes. Use 0 to allow each size.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
+                           int flags,
+                           size_t max_payload_size);
+
+/**
+ * Creates a new websocket stream, used for decoding/encoding,
+ * but with custom memory functions for malloc, realloc and free.
+ *
+ * @param[out] ws The websocket stream
+ * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values
+ *              to modify the behavior of the websocket stream.
+ * @param max_message_size The maximum size for incoming payload
+ *                         data in bytes. Use 0 to allow each size.
+ * @param callback_malloc  The callback function for 'malloc'.
+ * @param callback_realloc The callback function for 'realloc'.
+ * @param callback_free    The callback function for 'free'.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
+                            int flags,
+                            size_t max_payload_size,
+                            MHD_WebSocketMallocCallback callback_malloc,
+                            MHD_WebSocketReallocCallback callback_realloc,
+                            MHD_WebSocketFreeCallback callback_free);
+
+/**
+ * Frees a websocket stream
+ *
+ * @param ws The websocket stream. This value may be NULL.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_free (struct MHD_WebSocketStream*ws);
+
+/**
+ * Invalidates a websocket stream.
+ * After invalidation a websocket stream cannot be used for decoding anymore.
+ * Encoding is still possible.
+ *
+ * @param ws The websocket stream.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws);
+
+/**
+ * Queries whether a websocket stream is valid.
+ * Invalidated websocket streams cannot be used for decoding anymore.
+ * Encoding is still possible.
+ *
+ * @param ws The websocket stream.
+ * @return A value of `enum MHD_WEBSOCKET_VALIDITY`.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
+
+/**
+ * Decodes a byte sequence via this websocket stream.
+ * Decoding is done until either a frame is complete or
+ * the end of the byte sequence is reached.
+ *
+ * @param ws The websocket stream.
+ * @param streambuf The byte sequence for decoding.
+ *                  Typically that what you received via `recv()`.
+ * @param streambuf_len The length of the byte sequence @a streambuf
+ * @param[out] streambuf_read_len The number of bytes which has been processed
+ *                                by this call. This value may be less
+ *                                than @a streambuf_len when a frame is decoded
+ *                                before the end of the buffer is reached.
+ *                                The remaining bytes of @a buf must be passed
+ *                                in the following decoding.
+ * @param[out] payload This variable receives a buffer with the decoded
+ *                     payload data.
+ *                     If no decoded data is available this is NULL.
+ *                     When this variable is not NULL then
+ *                     the buffer contains always @a payload_len bytes plus
+ *                     one terminating NUL character.
+ *                     The caller must free this buffer
+ *                     using #MHD_websocket_free().
+ *                     If you passed the flag
+ *                     #MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
+ *                     upon creation of this websocket stream and
+ *                     a decoding error occurred
+ *                     (return value less than 0), then this
+ *                     buffer contains a generated close frame
+ *                     which must be sent via the socket to the recipient.
+ *                     If you passed the flag 
#MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+ *                     upon creation of this websocket stream then
+ *                     this payload may only be a part of the complete message.
+ *                     Only complete UTF-8 sequences are returned
+ *                     for fragmented text frames.
+ *                     If necessary the UTF-8 sequence will be completed
+ *                     with the next text fragment.
+ * @param[out] payload_len The length of the result payload buffer in bytes.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is greater than 0 if a frame has is complete, equal to 0 if 
more data
+ *         is needed an less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_decode (struct MHD_WebSocketStream*ws,
+                      const char*streambuf,
+                      size_t streambuf_len,
+                      size_t*streambuf_read_len,
+                      char**payload,
+                      size_t*payload_len);
+
+/**
+ * Splits the payload of of a decoded close frame.
+ *
+ * @param payload The payload of the close frame.
+ *                This parameter may be NULL if @a payload_len is 0.
+ * @param payload_len The length of @a payload.
+ * @param[out] reason_code The numeric close reason.
+ *                         If there was no close reason, this is
+ *                         #MHD_WEBSOCKET_CLOSEREASON_NO_REASON.
+ *                         Compare with `enum MHD_WEBSOCKET_CLOSEREASON`.
+ *                         This parameter is optional and can be NULL.
+ * @param[out] reason_utf8 The literal close reason.
+ *                         If there was no literal close reason, this is NULL.
+ *                         This parameter is optional and can be NULL.
+ *                         Please note that no memory is allocated
+ *                         in this function.
+ *                         If not NULL the returned value of this parameter
+ *                         points to a position in the specified @a payload.
+ * @param[out] reason_utf8_len The length of the literal close reason.
+ *                             If there was no literal close reason, this is 0.
+ *                             This parameter is optional and can be NULL.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_split_close_reason (const char*payload,
+                                  size_t payload_len,
+                                  unsigned short*reason_code,
+                                  const char**reason_utf8,
+                                  size_t*reason_utf8_len);
+
+/**
+ * Encodes an UTF-8 encoded text into websocket text frame.
+ *
+ * @param ws The websocket stream.
+ * @param payload_utf8 The UTF-8 encoded text to send.
+ *                     This can be NULL if payload_utf8_len is 0.
+ * @param payload_utf8_len The length of the UTF-8 encoded text in bytes.
+ * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION`
+ *                      to specifiy the fragmentation behavior.
+ *                      Specify MHD_WEBSOCKET_FRAGMENTATION_NONE
+ *                      if you don't want to use fragmentation.
+ * @param[out] frame This variable receives a buffer with the encoded frame.
+ *                   This is what you typically send via `send()` to the 
recipient.
+ *                   If no encoded data is available this is NULL.
+ *                   When this variable is not NULL then the buffer contains 
always
+ *                   @a frame_len bytes plus one terminating NUL character.
+ *                   The caller must free this buffer using 
#MHD_websocket_free().
+ * @param[out] frame_len The length of the encoded frame in bytes.
+ * @param[out] utf8_step This parameter is required for fragmentation and
+ *                       can be NULL if no fragmentation is used.
+ *                       It contains information about the last encoded
+ *                       UTF-8 sequence and is required to continue a previous
+ *                       UTF-8 sequence when fragmentation is used.
+ *                       The `enum MHD_WEBSOCKET_UTF8STEP` is for this.
+ *                       If you start a new fragment using
+ *                       MHD_WEBSOCKET_FRAGMENTATION_NONE or
+ *                       MHD_WEBSOCKET_FRAGMENTATION_FIRST the value
+ *                       of this variable will be initialized
+ *                       to MHD_WEBSOCKET_UTF8STEP_NORMAL.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
+                           const char*payload_utf8,
+                           size_t payload_utf8_len,
+                           int fragmentation,
+                           char**frame,
+                           size_t*frame_len,
+                           int*utf8_step);
+
+/**
+ * Encodes a binary data into websocket binary frame.
+ *
+ * @param ws The websocket stream.
+ * @param payload The binary data to send.
+ * @param payload_len The length of the binary data in bytes.
+ * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION`
+ *                      to specifiy the fragmentation behavior.
+ *                      Specify MHD_WEBSOCKET_FRAGMENTATION_NONE
+ *                      if you don't want to use fragmentation.
+ * @param[out] frame This variable receives a buffer with
+ *                   the encoded binary frame.
+ *                   This is what you typically send via `send()`
+ *                   to the recipient.
+ *                   If no encoded frame is available this is NULL.
+ *                   When this variable is not NULL then the allocated buffer
+ *                   contains always @a frame_len bytes plus one terminating
+ *                   NUL character.
+ *                   The caller must free this buffer using 
#MHD_websocket_free().
+ * @param[out] frame_len The length of the result frame buffer in bytes.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
+                             const char*payload,
+                             size_t payload_len,
+                             int fragmentation,
+                             char**frame,
+                             size_t*frame_len);
+
+/**
+ * Encodes a websocket ping frame
+ *
+ * @param ws The websocket stream.
+ * @param payload The binary ping payload data to send.
+ *                This may be NULL if @a payload_len is 0.
+ * @param payload_len The length of the payload data in bytes.
+ *                    This may not exceed 125 bytes.
+ * @param[out] frame This variable receives a buffer with the encoded ping 
frame data.
+ *                   This is what you typically send via `send()` to the 
recipient.
+ *                   If no encoded frame is available this is NULL.
+ *                   When this variable is not NULL then the buffer contains 
always
+ *                   @a frame_len bytes plus one terminating NUL character.
+ *                   The caller must free this buffer using 
#MHD_websocket_free().
+ * @param[out] frame_len The length of the result frame buffer in bytes.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           char**frame,
+                           size_t*frame_len);
+
+/**
+ * Encodes a websocket pong frame
+ *
+ * @param ws The websocket stream.
+ * @param payload The binary pong payload data, which is typically
+ *                the decoded payload from the received ping frame.
+ *                This may be NULL if @a payload_len is 0.
+ * @param payload_len The length of the payload data in bytes.
+ *                    This may not exceed 125 bytes.
+ * @param[out] frame This variable receives a buffer with
+ *                   the encoded pong frame data.
+ *                   This is what you typically send via `send()`
+ *                   to the recipient.
+ *                   If no encoded frame is available this is NULL.
+ *                   When this variable is not NULL then the buffer
+ *                   contains always @a frame_len bytes plus one
+ *                   terminating NUL character.
+ *                   The caller must free this buffer
+ *                   using #MHD_websocket_free().
+ * @param[out] frame_len The length of the result frame buffer in bytes.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           char**frame,
+                           size_t*frame_len);
+
+/**
+ * Encodes a websocket close frame
+ *
+ * @param ws The websocket stream.
+ * @param reason_code The reason for close.
+ *                    You can use `enum MHD_WEBSOCKET_CLOSEREASON`
+ *                    for typical reasons,
+ *                    but you are not limited to these values.
+ *                    The allowed values are specified in RFC 6455 7.4.
+ *                    If you don't want to enter a reason, you can specify
+ *                    #MHD_WEBSOCKET_CLOSEREASON_NO_REASON then
+ *                    no reason is encoded.
+ * @param reason_utf8 An UTF-8 encoded text reason why the connection is 
closed.
+ *                    This may be NULL if @a reason_utf8_len is 0.
+ *                    This must be NULL if @a reason_code is
+ *                    #MHD_WEBSOCKET_CLOSEREASON_NO_REASON (= 0).
+ * @param reason_utf8_len The length of the UTF-8 encoded text reason in bytes.
+ *                        This may not exceed 123 bytes.
+ * @param[out] frame This variable receives a buffer with
+ *                   the encoded close frame.
+ *                   This is what you typically send via `send()`
+ *                   to the recipient.
+ *                   If no encoded frame is available this is NULL.
+ *                   When this variable is not NULL then the buffer
+ *                   contains always @a frame_len bytes plus
+ *                   one terminating NUL character.
+ *                   The caller must free this buffer
+ *                   using #MHD_websocket_free().
+ * @param[out] frame_len The length of the result frame buffer in bytes.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
+                            unsigned short reason_code,
+                            const char*reason_utf8,
+                            size_t reason_utf8_len,
+                            char**frame,
+                            size_t*frame_len);
+
+/**
+ * Sets the seed for the random number generated used for
+ * the generation of masked frames (this is only used for client websockets).
+ * This seed is used for all websocket streams.
+ * Internally `srand()` is called.
+ * Please note that on some situations
+ * (where `rand()` and `srand()` are shared between your program
+ * and this library) this could cause unwanted results in your program if
+ * your program relies on a specific seed.
+ *
+ * @param seed The seed used for the initialization of
+ *             the pseudo random number generator.
+ *             Typically `time(NULL)` is used here to
+ *             generate a seed.
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_srand (unsigned long seed);
+
+/**
+ * Allocates memory with the associated 'malloc' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param len The length of the memory to allocate in bytes
+ *
+ * @return The allocated memory on success or NULL on failure.
+ * @ingroup websocket
+ */
+_MHD_EXTERN void*
+MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
+                      size_t len);
+
+/**
+ * Reallocates memory with the associated 'realloc' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param cls The previously allocated memory or NULL
+ * @param len The new length of the memory in bytes
+ *
+ * @return The allocated memory on success or NULL on failure.
+ *         If NULL is returned the previously allocated buffer
+ *         remains valid.
+ * @ingroup websocket
+ */
+_MHD_EXTERN void*
+MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
+                       void*cls,
+                       size_t len);
+
+/**
+ * Frees memory with the associated 'free' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param cls The previously allocated memory or NULL
+ *
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
+ *         or a value less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN int
+MHD_websocket_free (struct MHD_WebSocketStream*ws,
+                    void*cls);
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index fa83200a..1e252d5a 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -80,6 +80,7 @@ libmicrohttpd_la_SOURCES += \
   mhd_locks.h
 endif
 
+
 libmicrohttpd_la_CPPFLAGS = \
   $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \
   -DBUILDING_MHD_LIB=1
@@ -92,6 +93,9 @@ libmicrohttpd_la_LDFLAGS = \
 libmicrohttpd_la_LIBADD = \
   $(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS)
 
+
+
+
 if HAVE_W32
 MHD_DLL_RES_SRC = microhttpd_dll_res.rc
 MHD_DLL_RES_LO = libmicrohttpd_la-$(MHD_DLL_RES_SRC:.rc=.lo)
diff --git a/src/microhttpd_ws/Makefile.am b/src/microhttpd_ws/Makefile.am
new file mode 100644
index 00000000..16612506
--- /dev/null
+++ b/src/microhttpd_ws/Makefile.am
@@ -0,0 +1,39 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microhttpd
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
+
+noinst_DATA =
+MOSTLYCLEANFILES =
+
+SUBDIRS = .
+
+lib_LTLIBRARIES = \
+  libmicrohttpd_ws.la
+libmicrohttpd_ws_la_SOURCES = \
+  sha1.c sha1.h \
+  mhd_websocket.c
+libmicrohttpd_ws_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+libmicrohttpd_ws_la_CFLAGS = \
+  $(AM_CFLAGS) $(MHD_LIB_CFLAGS)
+libmicrohttpd_ws_la_LDFLAGS = \
+  $(MHD_LIB_LDFLAGS) \
+  $(W32_MHD_LIB_LDFLAGS) \
+  -version-info 0:0:0
+libmicrohttpd_ws_la_LIBADD = \
+  $(MHD_LIBDEPS)
+
+TESTS = $(check_PROGRAMS)
+
+check_PROGRAMS = \
+  test_websocket
+
+test_websocket_SOURCES = \
+  test_websocket.c
+test_websocket_LDADD = \
+  $(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
diff --git a/src/microhttpd_ws/mhd_websocket.c 
b/src/microhttpd_ws/mhd_websocket.c
new file mode 100644
index 00000000..0e977973
--- /dev/null
+++ b/src/microhttpd_ws/mhd_websocket.c
@@ -0,0 +1,2072 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2021 David Gausmann
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library 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_ws/mhd_websocket.c
+ * @brief  Support for the websocket protocol
+ * @author David Gausmann
+ */
+#include "platform.h"
+#include "microhttpd.h"
+#include "microhttpd_ws.h"
+#include "sha1.h"
+
+struct MHD_WebSocketStream
+{
+  /* The function pointer to malloc for payload (can be used to use different 
memory managment) */
+  MHD_WebSocketMallocCallback malloc;
+  /* The function pointer to realloc for payload (can be used to use different 
memory managment) */
+  MHD_WebSocketReallocCallback realloc;
+  /* The function pointer to free for payload (can be used to use different 
memory managment) */
+  MHD_WebSocketFreeCallback free;
+  /* The flags specified upon initialization. It may alter the behavior of 
decoding/encoding */
+  int flags;
+  /* The current step for the decoder. 0 means start of a frame. */
+  char decode_step;
+  /* Specifies whether the stream is valid (1) or not (0),
+     if a close frame has been received this is (-1) to indicate that no data 
frames are allowed anymore  */
+  char validity;
+  /* The current step of the UTF-8 encoding check in the data payload */
+  char data_utf8_step;
+  /* The current step of the UTF-8 encoding check in the control payload */
+  char control_utf8_step;
+  /* if != 0 means that we expect a CONTINUATION frame */
+  char data_type;
+  /* The start of the current frame (may differ from data_payload for 
CONTINUATION frames) */
+  char*data_payload_start;
+  /* The buffer for the data frame */
+  char*data_payload;
+  /* The buffer for the control frame */
+  char*control_payload;
+  /* Configuration for the maximum allowed buffer size for payload data */
+  size_t max_payload_size;
+  /* The current frame header size */
+  size_t frame_header_size;
+  /* The current data payload size (can be greater than payload_size for 
fragmented frames) */
+  size_t data_payload_size;
+  /* The size of the payload of the current frame (control or data) */
+  size_t payload_size;
+  /* The processing offset to the start of the payload of the current frame 
(control or data) */
+  size_t payload_index;
+  /* The frame header of the current frame (control or data) */
+  char frame_header[32];
+  /* The mask key of the current frame (control or data); this is 0 if no 
masking used */
+  char mask_key[4];
+};
+
+#define MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT          MHD_WEBSOCKET_FLAG_CLIENT
+#define MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION         \
+  MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+#define MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES \
+  MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
+#define MHD_WEBSOCKET_FLAG_MASK_ALL                   \
+  (MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT                                        
               \
+   | MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION   \
+   | MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES)
+
+enum MHD_WebSocket_Opcode
+{
+  MHD_WebSocket_Opcode_Continuation = 0x0,
+  MHD_WebSocket_Opcode_Text         = 0x1,
+  MHD_WebSocket_Opcode_Binary       = 0x2,
+  MHD_WebSocket_Opcode_Close        = 0x8,
+  MHD_WebSocket_Opcode_Ping         = 0x9,
+  MHD_WebSocket_Opcode_Pong         = 0xA
+};
+
+enum MHD_WebSocket_DecodeStep
+{
+  MHD_WebSocket_DecodeStep_Start                 =  0,
+  MHD_WebSocket_DecodeStep_Length1ofX            =  1,
+  MHD_WebSocket_DecodeStep_Length1of2            =  2,
+  MHD_WebSocket_DecodeStep_Length2of2            =  3,
+  MHD_WebSocket_DecodeStep_Length1of8            =  4,
+  MHD_WebSocket_DecodeStep_Length2of8            =  5,
+  MHD_WebSocket_DecodeStep_Length3of8            =  6,
+  MHD_WebSocket_DecodeStep_Length4of8            =  7,
+  MHD_WebSocket_DecodeStep_Length5of8            =  8,
+  MHD_WebSocket_DecodeStep_Length6of8            =  9,
+  MHD_WebSocket_DecodeStep_Length7of8            = 10,
+  MHD_WebSocket_DecodeStep_Length8of8            = 11,
+  MHD_WebSocket_DecodeStep_Mask1Of4              = 12,
+  MHD_WebSocket_DecodeStep_Mask2Of4              = 13,
+  MHD_WebSocket_DecodeStep_Mask3Of4              = 14,
+  MHD_WebSocket_DecodeStep_Mask4Of4              = 15,
+  MHD_WebSocket_DecodeStep_HeaderCompleted       = 16,
+  MHD_WebSocket_DecodeStep_PayloadOfDataFrame    = 17,
+  MHD_WebSocket_DecodeStep_PayloadOfControlFrame = 18,
+  MHD_WebSocket_DecodeStep_BrokenStream          = 99
+};
+
+enum MHD_WebSocket_UTF8Result
+{
+  MHD_WebSocket_UTF8Result_Invalid    = 0,
+  MHD_WebSocket_UTF8Result_Valid      = 1,
+  MHD_WebSocket_UTF8Result_Incomplete = 2
+};
+
+#define htonll(x) \
+  ((1 == htonl (1)) ? (x) : ((uint64_t) htonl ((x) & 0xFFFFFFFF) << 32) \
+   | htonl ((x) >> 32))
+#define ntohll(x) \
+  ((1 == ntohl (1)) ? (x) : ((uint64_t) ntohl ((x) & 0xFFFFFFFF) << 32) \
+   | ntohl ((x) >> 32))
+
+static void
+MHD_websocket_copy_payload (char*dst,
+                            const char*src,
+                            size_t len,
+                            unsigned long mask,
+                            unsigned long mask_offset);
+
+static int
+MHD_websocket_check_utf8 (const char*buf,
+                          size_t buf_len,
+                          int*utf8_step,
+                          size_t*buf_offset);
+
+static int
+MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
+                                      char**payload,
+                                      size_t*payload_len);
+
+static int
+MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
+                                       char**payload,
+                                       size_t*payload_len);
+
+static char
+MHD_websocket_encode_is_masked (struct MHD_WebSocketStream *ws);
+static char
+MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
+                                    size_t payload_len);
+
+static int
+MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           int fragmentation,
+                           char**frame,
+                           size_t*frame_len,
+                           char opcode);
+
+static int
+MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
+                                const char*payload,
+                                size_t payload_len,
+                                char**frame,
+                                size_t*frame_len,
+                                char opcode);
+
+static unsigned long
+MHD_websocket_generate_mask ();
+
+/**
+ * Creates the response for the Sec-WebSocket-Accept header
+ */
+_MHD_EXTERN int
+MHD_websocket_create_accept (const char*sec_websocket_key,
+                             char*sec_websocket_accept)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != sec_websocket_accept)
+    *sec_websocket_accept = 0;
+
+  /* validate parameters */
+  if ((NULL == sec_websocket_key) ||
+      (NULL == sec_websocket_accept) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* build SHA1 hash of the given key and the UUID appended */
+  char sha1[20];
+  const char*suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+  int length = (int) strlen (sec_websocket_key);
+  struct sha1_ctx ctx;
+  sha1_init_ctx (&ctx);
+  sha1_process_bytes (sec_websocket_key, length, &ctx);
+  sha1_process_bytes (suffix, 36, &ctx);
+  sha1_finish_ctx (&ctx, sha1);
+
+  /* base64 encode that SHA1 hash */
+  /* (simple algorithm here; SHA1 has always 20 bytes, */
+  /* which will always result in a 28 bytes base64 hash) */
+  const char*base64_encoding_table =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  for (int i = 0, j = 0; i < 20;)
+  {
+    uint32_t octet_a = i < 20 ? (unsigned char) sha1[i++] : 0;
+    uint32_t octet_b = i < 20 ? (unsigned char) sha1[i++] : 0;
+    uint32_t octet_c = i < 20 ? (unsigned char) sha1[i++] : 0;
+    uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
+
+    sec_websocket_accept[j++] = base64_encoding_table[(triple >> 3 * 6) & 
0x3F];
+    sec_websocket_accept[j++] = base64_encoding_table[(triple >> 2 * 6) & 
0x3F];
+    sec_websocket_accept[j++] = base64_encoding_table[(triple >> 1 * 6) & 
0x3F];
+    sec_websocket_accept[j++] = base64_encoding_table[(triple >> 0 * 6) & 
0x3F];
+
+  }
+  sec_websocket_accept[27] = '=';
+  sec_websocket_accept[28] = 0;
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Initializes a new websocket stream
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
+                           int flags,
+                           size_t max_payload_size)
+{
+  return MHD_websocket_stream_init2 (ws,
+                                     flags,
+                                     max_payload_size,
+                                     malloc,
+                                     realloc,
+                                     free);
+}
+
+
+/**
+ * Initializes a new websocket stream with
+ * additional parameters for allocation functions
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
+                            int flags,
+                            size_t max_payload_size,
+                            MHD_WebSocketMallocCallback callback_malloc,
+                            MHD_WebSocketReallocCallback callback_realloc,
+                            MHD_WebSocketFreeCallback callback_free)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != ws)
+    *ws = NULL;
+
+  /* validate parameters */
+  if ((NULL == ws) ||
+      (0 != (flags & ~MHD_WEBSOCKET_FLAG_MASK_ALL)) ||
+      ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) ||
+      (NULL == callback_malloc) ||
+      (NULL == callback_realloc) ||
+      (NULL == callback_free) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* allocate stream */
+  struct MHD_WebSocketStream*ws_ = (struct MHD_WebSocketStream*) malloc (
+    sizeof (struct MHD_WebSocketStream));
+  if (NULL == ws_)
+    return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+
+  /* initialize stream */
+  memset (ws_, 0, sizeof (struct MHD_WebSocketStream));
+  ws_->flags   = flags;
+  ws_->max_payload_size = max_payload_size;
+  ws_->malloc   = callback_malloc;
+  ws_->realloc  = callback_realloc;
+  ws_->free     = callback_free;
+  ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID;
+
+  /* return stream */
+  *ws = ws_;
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Frees a previously allocated websocket stream
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
+{
+  /* validate parameters */
+  if (NULL == ws)
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+
+  /* free allocated payload data */
+  if (ws->data_payload)
+    ws->free (ws->data_payload);
+  if (ws->control_payload)
+    ws->free (ws->control_payload);
+
+  /* free the stream */
+  free (ws);
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Invalidates a websocket stream (no more decoding possible)
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
+{
+  /* validate parameters */
+  if (NULL == ws)
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+
+  /* invalidate stream */
+  ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Returns whether a websocket stream is valid
+ */
+_MHD_EXTERN int
+MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
+{
+  /* validate parameters */
+  if (NULL == ws)
+    return MHD_WEBSOCKET_VALIDITY_INVALID;
+
+  return ws->validity;
+}
+
+
+/**
+ * Decodes incoming data to a websocket frame
+ */
+_MHD_EXTERN int
+MHD_websocket_decode (struct MHD_WebSocketStream*ws,
+                      const char*streambuf,
+                      size_t streambuf_len,
+                      size_t*streambuf_read_len,
+                      char**payload,
+                      size_t*payload_len)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != streambuf_read_len)
+    *streambuf_read_len = 0;
+  if (NULL != payload)
+    *payload = NULL;
+  if (NULL != payload_len)
+    *payload_len = 0;
+
+  /* validate parameters */
+  if ((NULL == ws) ||
+      (NULL == streambuf) && (0 != streambuf_len) ||
+      (NULL == streambuf_read_len) ||
+      (NULL == payload) ||
+      (NULL == payload_len) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* validate stream validity */
+  if (MHD_WEBSOCKET_VALIDITY_INVALID == ws->validity)
+    return MHD_WEBSOCKET_STATUS_STREAM_BROKEN;
+
+  /* decode loop */
+  size_t current = 0;
+  while (current < streambuf_len)
+  {
+    switch (ws->decode_step)
+    {
+    /* start of frame */
+    case MHD_WebSocket_DecodeStep_Start:
+      {
+        /* The first byte contains the opcode, the fin flag and three reserved 
bits */
+        if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity)
+        {
+          char opcode = streambuf [current];
+          if (0 != (opcode & 0x70))
+          {
+            /* RFC 6455 5.2 RSV1-3: If a reserved flag is set */
+            /* (while it isn't specified by an extension) the communcation 
must fail. */
+            ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+            if (0 != (ws->flags
+                      & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+            {
+              MHD_websocket_encode_close (ws,
+                                          
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                          0,
+                                          0,
+                                          payload,
+                                          payload_len);
+            }
+            *streambuf_read_len = current;
+            return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+          }
+          switch (opcode & 0x0F)
+          {
+          case MHD_WebSocket_Opcode_Continuation:
+            if (0 == ws->data_type)
+            {
+              /* RFC 6455 5.4: Continuation frame without previous data frame 
*/
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+            if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES ==
+                ws->validity)
+            {
+              /* RFC 6455 5.5.1: After a close frame has been sent, */
+              /* no data frames may be sent (so we don't accept data frames */
+              /* for decoding anymore) */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+            break;
+
+          case MHD_WebSocket_Opcode_Text:
+          case MHD_WebSocket_Opcode_Binary:
+            if (0 != ws->data_type)
+            {
+              /* RFC 6455 5.4: Continuation expected, but new data frame */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+            if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES ==
+                ws->validity)
+            {
+              /* RFC 6455 5.5.1: After a close frame has been sent, */
+              /* no data frames may be sent (so we don't accept data frames */
+              /* for decoding anymore) */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+            break;
+
+          case MHD_WebSocket_Opcode_Close:
+          case MHD_WebSocket_Opcode_Ping:
+          case MHD_WebSocket_Opcode_Pong:
+            if ((opcode & 0x80) == 0)
+            {
+              /* RFC 6455 5.4: Control frames may not be fragmented */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+            if (MHD_WebSocket_Opcode_Close == (opcode & 0x0F))
+            {
+              /* RFC 6455 5.5.1: After a close frame has been sent, */
+              /* no data frames may be sent (so we don't accept data frames */
+              /* for decoding anymore) */
+              ws->validity =
+                MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES;
+            }
+            break;
+
+          default:
+            /* RFC 6455 5.2 OPCODE: Only six opcodes are specified. */
+            /* All other are invalid in version 13 of the protocol. */
+            ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+            if (0 != (ws->flags
+                      & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+            {
+              MHD_websocket_encode_close (ws,
+                                          
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                          0,
+                                          0,
+                                          payload,
+                                          payload_len);
+            }
+            *streambuf_read_len = current;
+            return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+          }
+        }
+        ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+        ws->decode_step = MHD_WebSocket_DecodeStep_Length1ofX;
+      }
+      break;
+
+    case MHD_WebSocket_DecodeStep_Length1ofX:
+      {
+        /* The second byte specifies whether the data is masked and the size */
+        /* (the client MUST mask the payload, the server MUST NOT mask the 
payload) */
+        char frame_len = streambuf [current];
+        char is_masked = (frame_len & 0x80);
+        frame_len &= 0x7f;
+        if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity)
+        {
+          if (0 != is_masked)
+          {
+            if (MHD_WEBSOCKET_FLAG_CLIENT == (ws->flags
+                                              & MHD_WEBSOCKET_FLAG_CLIENT))
+            {
+              /* RFC 6455 5.1: All frames from the server must be unmasked */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+          }
+          else
+          {
+            if (MHD_WEBSOCKET_FLAG_SERVER == (ws->flags
+                                              & MHD_WEBSOCKET_FLAG_CLIENT))
+            {
+              /* RFC 6455 5.1: All frames from the client must be masked */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+          }
+          if (126 <= frame_len)
+          {
+            if (0 != (ws->frame_header [0] & 0x08))
+            {
+              /* RFC 6455 5.5: Control frames may not have more payload than 
125 bytes */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+          }
+          if (1 == frame_len)
+          {
+            if (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0F))
+            {
+              /* RFC 6455 5.5.1: The close frame must have at least */
+              /* two bytes of payload if payload is used */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current;
+              return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+            }
+          }
+        }
+        ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+
+        if (126 == frame_len)
+        {
+          ws->decode_step = MHD_WebSocket_DecodeStep_Length1of2;
+        }
+        else if (127 == frame_len)
+        {
+          ws->decode_step = MHD_WebSocket_DecodeStep_Length1of8;
+        }
+        else
+        {
+          size_t size = (size_t) frame_len;
+          if ((SIZE_MAX < size) || ws->max_payload_size &&
+              (ws->max_payload_size < size) )
+          {
+            /* RFC 6455 7.4.1 1009: If the message is too big to process, we 
may close the connection */
+            ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+            if (0 != (ws->flags
+                      & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+            {
+              MHD_websocket_encode_close (ws,
+                                          
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
+                                          0,
+                                          0,
+                                          payload,
+                                          payload_len);
+            }
+            *streambuf_read_len = current;
+            return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+          }
+          ws->payload_size = size;
+          if (0 != is_masked)
+          {
+            /* with mask */
+            ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
+          }
+          else
+          {
+            /* without mask */
+            *((unsigned long *) ws->mask_key) = 0;
+            ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
+          }
+        }
+      }
+      break;
+
+    /* Payload size first byte of 2 bytes */
+    case MHD_WebSocket_DecodeStep_Length1of2:
+    /* Payload size first 7 bytes of 8 bytes */
+    case MHD_WebSocket_DecodeStep_Length1of8:
+    case MHD_WebSocket_DecodeStep_Length2of8:
+    case MHD_WebSocket_DecodeStep_Length3of8:
+    case MHD_WebSocket_DecodeStep_Length4of8:
+    case MHD_WebSocket_DecodeStep_Length5of8:
+    case MHD_WebSocket_DecodeStep_Length6of8:
+    case MHD_WebSocket_DecodeStep_Length7of8:
+    /* Mask first 3 bytes of 4 bytes */
+    case MHD_WebSocket_DecodeStep_Mask1Of4:
+    case MHD_WebSocket_DecodeStep_Mask2Of4:
+    case MHD_WebSocket_DecodeStep_Mask3Of4:
+      ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+      ++ws->decode_step;
+      break;
+
+    /* 2 byte length finished */
+    case MHD_WebSocket_DecodeStep_Length2of2:
+      {
+        ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+        size_t size = (size_t) htons (*((unsigned
+                                         short*) &ws->frame_header [2]));
+        if (125 >= size)
+        {
+          /* RFC 6455 5.2 Payload length: The minimal number of bytes */
+          /* must be used for the length */
+          ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+          if (0 != (ws->flags
+                    & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+          {
+            MHD_websocket_encode_close (ws,
+                                        
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                        0,
+                                        0,
+                                        payload,
+                                        payload_len);
+          }
+          *streambuf_read_len = current;
+          return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+        }
+        if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size 
<
+                                                          size) )
+        {
+          /* RFC 6455 7.4.1 1009: If the message is too big to process, */
+          /* we may close the connection */
+          ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+          if (0 != (ws->flags
+                    & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+          {
+            MHD_websocket_encode_close (ws,
+                                        
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
+                                        0,
+                                        0,
+                                        payload,
+                                        payload_len);
+          }
+          *streambuf_read_len = current;
+          return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+        }
+        ws->payload_size = size;
+        if (0 != (ws->frame_header [1] & 0x80))
+        {
+          /* with mask */
+          ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
+        }
+        else
+        {
+          /* without mask */
+          *((unsigned long *) ws->mask_key) = 0;
+          ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
+        }
+      }
+      break;
+
+    /* 8 byte length finished */
+    case MHD_WebSocket_DecodeStep_Length8of8:
+      {
+        ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+        uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2]));
+        if (0x7fffffffffffffff < size)
+        {
+          /* RFC 6455 5.2 frame-payload-length-63: The length may */
+          /* not exceed 0x7fffffffffffffff */
+          ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream;
+          ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+          if (0 != (ws->flags
+                    & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+          {
+            MHD_websocket_encode_close (ws,
+                                        
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                        0,
+                                        0,
+                                        payload,
+                                        payload_len);
+          }
+          *streambuf_read_len = current;
+          return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+        }
+        if (65535 >= size)
+        {
+          /* RFC 6455 5.2 Payload length: The minimal number of bytes */
+          /* must be used for the length */
+          ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+          if (0 != (ws->flags
+                    & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+          {
+            MHD_websocket_encode_close (ws,
+                                        
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
+                                        0,
+                                        0,
+                                        payload,
+                                        payload_len);
+          }
+          *streambuf_read_len = current;
+          return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+        }
+        if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size 
<
+                                                          size) )
+        {
+          /* RFC 6455 7.4.1 1009: If the message is too big to process, */
+          /* we may close the connection */
+          ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+          if (0 != (ws->flags
+                    & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+          {
+            MHD_websocket_encode_close (ws,
+                                        
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
+                                        0,
+                                        0,
+                                        payload,
+                                        payload_len);
+          }
+          *streambuf_read_len = current;
+          return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+        }
+        ws->payload_size = (size_t) size;
+
+        if (0 != (ws->frame_header [1] & 0x80))
+        {
+          /* with mask */
+          ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
+        }
+        else
+        {
+          /* without mask */
+          *((unsigned long *) ws->mask_key) = 0;
+          ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
+        }
+      }
+      break;
+
+    /* mask finished */
+    case MHD_WebSocket_DecodeStep_Mask4Of4:
+      ws->frame_header [ws->frame_header_size++] = streambuf [current++];
+      *((unsigned long *) ws->mask_key) = *((unsigned
+                                             long *) &ws->frame_header [ws->
+                                                                        
frame_header_size
+                                                                        - 4]);
+      ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
+      break;
+
+    /* header finished */
+    case MHD_WebSocket_DecodeStep_HeaderCompleted:
+      /* return or assign either to data or control */
+      {
+        int ret = MHD_websocket_decode_header_complete (ws,
+                                                        payload,
+                                                        payload_len);
+        if (MHD_WEBSOCKET_STATUS_OK != ret)
+        {
+          *streambuf_read_len = current;
+          return ret;
+        }
+      }
+      break;
+
+    /* payload data */
+    case MHD_WebSocket_DecodeStep_PayloadOfDataFrame:
+    case MHD_WebSocket_DecodeStep_PayloadOfControlFrame:
+      {
+        size_t bytes_needed    = ws->payload_size - ws->payload_index;
+        size_t bytes_remaining = streambuf_len - current;
+        size_t bytes_to_take   = bytes_needed < bytes_remaining ? bytes_needed 
:
+                                 bytes_remaining;
+        if (0 != bytes_to_take)
+        {
+          size_t utf8_start     = ws->payload_index;
+          char *decode_payload = ws->decode_step ==
+                                 MHD_WebSocket_DecodeStep_PayloadOfDataFrame ?
+                                 ws->data_payload_start :
+                                 ws->control_payload;
+
+          /* copy the new payload data (with unmasking if necessary */
+          MHD_websocket_copy_payload (decode_payload + ws->payload_index,
+                                      &streambuf [current],
+                                      bytes_to_take,
+                                      *((unsigned long *) ws->mask_key),
+                                      (unsigned long) (ws->payload_index
+                                                       & 0x03));
+          current += bytes_to_take;
+          ws->payload_index += bytes_to_take;
+          if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
+               ws->decode_step) &&
+              (MHD_WebSocket_Opcode_Text == ws->data_type) ||
+              (MHD_WebSocket_DecodeStep_PayloadOfControlFrame ==
+               ws->decode_step) &&
+              (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) &&
+              (2 < ws->payload_index) )
+          {
+            /* RFC 6455 8.1: We need to check the UTF-8 validity */
+            int utf8_step;
+            char*decode_payload_utf8;
+            size_t bytes_to_check;
+            size_t utf8_error_offset = 0;
+            if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step)
+            {
+              utf8_step           = ws->data_utf8_step;
+              decode_payload_utf8 = decode_payload + utf8_start;
+              bytes_to_check      = bytes_to_take;
+            }
+            else
+            {
+              utf8_step = ws->control_utf8_step;
+              if ((MHD_WebSocket_Opcode_Close == (ws->frame_header [0]
+                                                  & 0x0f)) &&
+                  (2 > utf8_start) )
+              {
+                /* The first two bytes of the close frame are binary content 
and */
+                /* must be skipped in the UTF-8 check */
+                utf8_start = 2;
+                utf8_error_offset = 2;
+              }
+              decode_payload_utf8 = decode_payload + utf8_start;
+              bytes_to_check      = bytes_to_take - utf8_start;
+            }
+            size_t utf8_check_offset = 0;
+            int utf8_result = MHD_websocket_check_utf8 (decode_payload_utf8,
+                                                        bytes_to_check,
+                                                        &utf8_step,
+                                                        &utf8_check_offset);
+            if (MHD_WebSocket_UTF8Result_Invalid != utf8_result)
+            {
+              /* memorize current validity check step to continue later */
+              ws->data_utf8_step = utf8_step;
+            }
+            else
+            {
+              /* RFC 6455 8.1: We must fail on broken UTF-8 sequence */
+              ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+              if (0 != (ws->flags
+                        & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+              {
+                MHD_websocket_encode_close (ws,
+                                            
MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8,
+                                            0,
+                                            0,
+                                            payload,
+                                            payload_len);
+              }
+              *streambuf_read_len = current - bytes_to_take
+                                    + utf8_check_offset + utf8_error_offset;
+              return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
+            }
+          }
+        }
+      }
+
+      if (ws->payload_size == ws->payload_index)
+      {
+        /* all payload data of the current frame has been received */
+        int ret = MHD_websocket_decode_payload_complete (ws,
+                                                         payload,
+                                                         payload_len);
+        if (MHD_WEBSOCKET_STATUS_OK != ret)
+        {
+          *streambuf_read_len = current;
+          return ret;
+        }
+      }
+      break;
+
+    case MHD_WebSocket_DecodeStep_BrokenStream:
+      *streambuf_read_len = current;
+      return MHD_WEBSOCKET_STATUS_STREAM_BROKEN;
+    }
+  }
+
+  /* Special treatment for zero payload length messages */
+  if (MHD_WebSocket_DecodeStep_HeaderCompleted == ws->decode_step)
+  {
+    int ret = MHD_websocket_decode_header_complete (ws,
+                                                    payload,
+                                                    payload_len);
+    if (MHD_WEBSOCKET_STATUS_OK != ret)
+    {
+      *streambuf_read_len = current;
+      return ret;
+    }
+  }
+  switch (ws->decode_step)
+  {
+  case MHD_WebSocket_DecodeStep_PayloadOfDataFrame:
+  case MHD_WebSocket_DecodeStep_PayloadOfControlFrame:
+    if (ws->payload_size == ws->payload_index)
+    {
+      /* all payload data of the current frame has been received */
+      int ret = MHD_websocket_decode_payload_complete (ws,
+                                                       payload,
+                                                       payload_len);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        *streambuf_read_len = current;
+        return ret;
+      }
+    }
+    break;
+  }
+  *streambuf_read_len = current;
+
+  /* more data needed */
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+static int
+MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
+                                      char**payload,
+                                      size_t*payload_len)
+{
+  /* assign either to data or control */
+  char opcode = ws->frame_header [0] & 0x0f;
+  switch (opcode)
+  {
+  case MHD_WebSocket_Opcode_Continuation:
+    {
+      /* validate payload size */
+      size_t new_size_total = ws->payload_size + ws->data_payload_size;
+      if ((0 != ws->max_payload_size) && (ws->max_payload_size <
+                                          new_size_total) )
+      {
+        /* RFC 6455 7.4.1 1009: If the message is too big to process, */
+        /* we may close the connection */
+        ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream;
+        ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
+        if (0 != (ws->flags
+                  & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
+        {
+          MHD_websocket_encode_close (ws,
+                                      
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
+                                      0,
+                                      0,
+                                      payload,
+                                      payload_len);
+        }
+        return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+      }
+      /* allocate buffer for continued data frame */
+      char*new_buf = NULL;
+      if (0 != new_size_total)
+      {
+        new_buf = ws->realloc (ws->data_payload, new_size_total + 1);
+        if (NULL == new_buf)
+        {
+          return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+        }
+        new_buf [new_size_total] = 0;
+        ws->data_payload_start = &new_buf[ws->data_payload_size];
+      }
+      else
+      {
+        ws->data_payload_start = new_buf;
+      }
+      ws->data_payload       = new_buf;
+      ws->data_payload_size  = new_size_total;
+    }
+    ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame;
+    break;
+
+  case MHD_WebSocket_Opcode_Text:
+  case MHD_WebSocket_Opcode_Binary:
+    /* allocate buffer for data frame */
+    {
+      size_t new_size_total = ws->payload_size;
+      char*new_buf = NULL;
+      if (0 != new_size_total)
+      {
+        new_buf = ws->malloc (new_size_total + 1);
+        if (NULL == new_buf)
+        {
+          return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+        }
+        new_buf [new_size_total] = 0;
+      }
+      ws->data_payload        = new_buf;
+      ws->data_payload_start  = new_buf;
+      ws->data_payload_size   = new_size_total;
+      ws->data_type           = opcode;
+    }
+    ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame;
+    break;
+
+  case MHD_WebSocket_Opcode_Close:
+  case MHD_WebSocket_Opcode_Ping:
+  case MHD_WebSocket_Opcode_Pong:
+    /* allocate buffer for control frame */
+    {
+      size_t new_size_total = ws->payload_size;
+      char*new_buf = NULL;
+      if (0 != new_size_total)
+      {
+        new_buf = ws->malloc (new_size_total + 1);
+        if (NULL == new_buf)
+        {
+          return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+        }
+        new_buf[new_size_total] = 0;
+      }
+      ws->control_payload = new_buf;
+    }
+    ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfControlFrame;
+    break;
+  }
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+static int
+MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
+                                       char**payload,
+                                       size_t*payload_len)
+{
+  /* all payload data of the current frame has been received */
+  char is_fin = ws->frame_header [0] & 0x80;
+  if (0 != is_fin)
+  {
+    /* the frame is complete */
+    if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step)
+    {
+      /* data frame */
+      char data_type = ws->data_type;
+      if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) &&
+          (MHD_WebSocket_Opcode_Continuation == (ws->frame_header [0] & 0x0F)))
+      {
+        data_type |= 0x20;   /* mark as last fragment */
+      }
+      *payload     = ws->data_payload;
+      *payload_len = ws->data_payload_size;
+      ws->data_payload       = 0;
+      ws->data_payload_start = 0;
+      ws->data_payload_size  = 0;
+      ws->decode_step        = MHD_WebSocket_DecodeStep_Start;
+      ws->payload_index      = 0;
+      ws->data_type          = 0;
+      ws->frame_header_size  = 0;
+      return data_type;
+    }
+    else
+    {
+      /* control frame */
+      *payload     = ws->control_payload;
+      *payload_len = ws->payload_size;
+      ws->control_payload   = 0;
+      ws->decode_step       = MHD_WebSocket_DecodeStep_Start;
+      ws->payload_index     = 0;
+      ws->frame_header_size = 0;
+      return (ws->frame_header [0] & 0x0f);
+    }
+  }
+  else if (0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS))
+  {
+    /* RFC 6455 5.4: To allow streaming, the user can choose */
+    /* to return fragments */
+    if ((MHD_WebSocket_Opcode_Text == ws->data_type) &&
+        (MHD_WEBSOCKET_UTF8STEP_NORMAL != ws->data_utf8_step) )
+    {
+      /* the last UTF-8 sequence is incomplete, so we keep the start of
+      that and only return the part before */
+      size_t given_utf8;
+      switch (ws->data_utf8_step)
+      {
+      /* one byte given */
+      case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1:
+      case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2:
+      case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2:
+      case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2:
+      case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3:
+      case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3:
+      case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3:
+        given_utf8 = 1;
+        break;
+      /* two bytes given */
+      case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2:
+      case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3:
+        given_utf8 = 2;
+        break;
+      /* three bytes given */
+      case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3:
+        given_utf8 = 3;
+        break;
+      }
+      size_t new_len = ws->data_payload_size - given_utf8;
+      if (0 != new_len)
+      {
+        char *next_payload = ws->malloc (given_utf8 + 1);
+        if (NULL == next_payload)
+        {
+          return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+        }
+        memcpy (next_payload,
+                ws->data_payload_start + ws->payload_index - given_utf8,
+                given_utf8);
+        next_payload[given_utf8] = 0;
+
+        ws->data_payload[new_len] = 0;
+        *payload     = ws->data_payload;
+        *payload_len = new_len;
+        ws->data_payload      = next_payload;
+        ws->data_payload_size = given_utf8;
+      }
+      else
+      {
+        *payload     = NULL;
+        *payload_len = 0;
+      }
+      ws->decode_step       = MHD_WebSocket_DecodeStep_Start;
+      ws->payload_index     = 0;
+      ws->frame_header_size = 0;
+      return ws->data_type | 0x10;    /* mark as fragment */
+    }
+    else
+    {
+      /* we simply pass the entire data frame */
+      *payload     = ws->data_payload;
+      *payload_len = ws->data_payload_size;
+      ws->data_payload       = 0;
+      ws->data_payload_start = 0;
+      ws->data_payload_size  = 0;
+      ws->decode_step        = MHD_WebSocket_DecodeStep_Start;
+      ws->payload_index      = 0;
+      ws->frame_header_size  = 0;
+      return ws->data_type | 0x10;    /* mark as fragment */
+    }
+  }
+  else
+  {
+    /* RFC 6455 5.4: We must await a continuation frame to get */
+    /* the remainder of this data frame */
+    ws->decode_step       = MHD_WebSocket_DecodeStep_Start;
+    ws->frame_header_size = 0;
+    ws->payload_index     = 0;
+    return MHD_WEBSOCKET_STATUS_OK;
+  }
+}
+
+
+/**
+ * Splits the received close reason
+ */
+_MHD_EXTERN int
+MHD_websocket_split_close_reason (const char*payload,
+                                  size_t payload_len,
+                                  unsigned short*reason_code,
+                                  const char**reason_utf8,
+                                  size_t*reason_utf8_len)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != reason_code)
+    *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON;
+  if (NULL != reason_utf8)
+    *reason_utf8 = NULL;
+  if (NULL != reason_utf8_len)
+    *reason_utf8_len = 0;
+
+  /* validate parameters */
+  if ((NULL == payload) && (0 != payload_len))
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  if (1 == payload_len)
+    return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
+  if (125 < payload_len)
+    return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+
+  /* decode reason code */
+  if (2 > payload_len)
+  {
+    if (NULL != reason_code)
+      *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON;
+  }
+  else
+  {
+    if (NULL != reason_code)
+      *reason_code = htons (*((unsigned short*) payload));
+  }
+
+  /* decode reason text */
+  if (2 >= payload_len)
+  {
+    if (NULL != reason_utf8)
+      *reason_utf8 = NULL;
+    if (NULL != reason_utf8_len)
+      *reason_utf8_len = 0;
+  }
+  else
+  {
+    if (NULL != reason_utf8)
+      *reason_utf8 = payload + 2;
+    if (NULL != reason_utf8_len)
+      *reason_utf8_len = payload_len - 2;
+  }
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Encodes a text into a websocket text frame
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
+                           const char*payload_utf8,
+                           size_t payload_utf8_len,
+                           int fragmentation,
+                           char**frame,
+                           size_t*frame_len,
+                           int*utf8_step)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != frame)
+    *frame = NULL;
+  if (NULL != frame_len)
+    *frame_len = 0;
+  if ((NULL != utf8_step) &&
+      ((MHD_WEBSOCKET_FRAGMENTATION_FIRST == fragmentation) ||
+       (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) ))
+  {
+    /* the old UTF-8 step will be ignored for new fragments */
+    *utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL;
+  }
+
+  /* validate parameters */
+  if ((NULL == ws) ||
+      (0 != payload_utf8_len) && (NULL == payload_utf8) ||
+      (NULL == frame) ||
+      (NULL == frame_len) ||
+      (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
+      (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) ||
+      (MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) && (NULL ==
+                                                              utf8_step) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* check max length */
+  if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_utf8_len)
+  {
+    return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+  }
+
+  /* check UTF-8 */
+  int utf8_result = MHD_websocket_check_utf8 (payload_utf8,
+                                              payload_utf8_len,
+                                              utf8_step,
+                                              NULL);
+  if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) ||
+      (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
+      (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) )
+  {
+    return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
+  }
+
+  /* encode data */
+  return MHD_websocket_encode_data (ws,
+                                    payload_utf8,
+                                    payload_utf8_len,
+                                    fragmentation,
+                                    frame,
+                                    frame_len,
+                                    MHD_WebSocket_Opcode_Text);
+}
+
+
+/**
+ * Encodes binary data into a websocket binary frame
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
+                             const char*payload,
+                             size_t payload_len,
+                             int fragmentation,
+                             char**frame,
+                             size_t*frame_len)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != frame)
+    *frame = NULL;
+  if (NULL != frame_len)
+    *frame_len = 0;
+
+  /* validate parameters */
+  if ((NULL == ws) ||
+      (0 != payload_len) && (NULL == payload) ||
+      (NULL == frame) ||
+      (NULL == frame_len) ||
+      (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
+      (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* check max length */
+  if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_len)
+  {
+    return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+  }
+
+  return MHD_websocket_encode_data (ws,
+                                    payload,
+                                    payload_len,
+                                    fragmentation,
+                                    frame,
+                                    frame_len,
+                                    MHD_WebSocket_Opcode_Binary);
+}
+
+
+/**
+ * Internal function for encoding text/binary data into a websocket frame
+ */
+static int
+MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           int fragmentation,
+                           char**frame,
+                           size_t*frame_len,
+                           char opcode)
+{
+  /* calculate length and masking */
+  char is_masked      = MHD_websocket_encode_is_masked (ws);
+  size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
+  size_t total_len    = overhead_len + payload_len;
+  unsigned long mask  = 0 != is_masked ? MHD_websocket_generate_mask () : 0;
+
+  /* allocate memory */
+  char*result = ws->malloc (total_len + 1);
+  if (NULL == result)
+    return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+  result [total_len] = 0;
+  *frame     = result;
+  *frame_len = total_len;
+
+  /* add the opcode */
+  switch (fragmentation)
+  {
+  case MHD_WEBSOCKET_FRAGMENTATION_NONE:
+    *(result++) = 0x80 | opcode;
+    break;
+  case MHD_WEBSOCKET_FRAGMENTATION_FIRST:
+    *(result++) = opcode;
+    break;
+  case MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING:
+    *(result++) = MHD_WebSocket_Opcode_Continuation;
+    break;
+  case MHD_WEBSOCKET_FRAGMENTATION_LAST:
+    *(result++) = 0x80 | MHD_WebSocket_Opcode_Continuation;
+    break;
+  }
+
+  /* add the length */
+  if (126 > payload_len)
+  {
+    *(result++) = is_masked | (char) payload_len;
+  }
+  else if (65536 > payload_len)
+  {
+    *(result++) = is_masked | 126;
+    *((unsigned short *) result) = htons ((unsigned short) payload_len);
+    result += 2;
+  }
+  else
+  {
+    *(result++) = is_masked | 127;
+    *((uint64_t *) result) = htonll ((uint64_t) payload_len);
+    result += 8;
+
+  }
+
+  /* add the mask */
+  if (0 != is_masked)
+  {
+    *(result++) = ((char *) &mask)[0];
+    *(result++) = ((char *) &mask)[1];
+    *(result++) = ((char *) &mask)[2];
+    *(result++) = ((char *) &mask)[3];
+  }
+
+  /* add the payload */
+  if (0 != payload_len)
+  {
+    MHD_websocket_copy_payload (result,
+                                payload,
+                                payload_len,
+                                mask,
+                                0);
+  }
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Encodes a websocket ping frame
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           char**frame,
+                           size_t*frame_len)
+{
+  /* encode the ping frame */
+  return MHD_websocket_encode_ping_pong (ws,
+                                         payload,
+                                         payload_len,
+                                         frame,
+                                         frame_len,
+                                         MHD_WebSocket_Opcode_Ping);
+}
+
+
+/**
+ * Encodes a websocket pong frame
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
+                           const char*payload,
+                           size_t payload_len,
+                           char**frame,
+                           size_t*frame_len)
+{
+  /* encode the pong frame */
+  return MHD_websocket_encode_ping_pong (ws,
+                                         payload,
+                                         payload_len,
+                                         frame,
+                                         frame_len,
+                                         MHD_WebSocket_Opcode_Pong);
+}
+
+
+/**
+ * Internal function for encoding ping/pong frames
+ */
+static int
+MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
+                                const char*payload,
+                                size_t payload_len,
+                                char**frame,
+                                size_t*frame_len,
+                                char opcode)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != frame)
+    *frame = NULL;
+  if (NULL != frame_len)
+    *frame_len = 0;
+
+  /* validate the parameters */
+  if ((NULL == ws) ||
+      (0 != payload_len) && (NULL == payload) ||
+      (NULL == frame) ||
+      (NULL == frame_len) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload 
data */
+  if (125 < payload_len)
+    return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+
+  /* calculate length and masking */
+  char is_masked      = MHD_websocket_encode_is_masked (ws);
+  size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
+  size_t total_len    = overhead_len + payload_len;
+  unsigned long mask  = is_masked != 0 ? MHD_websocket_generate_mask () : 0;
+
+  /* allocate memory */
+  char*result = ws->malloc (total_len + 1);
+  if (NULL == result)
+    return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+  result [total_len] = 0;
+  *frame     = result;
+  *frame_len = total_len;
+
+  /* add the opcode */
+  *(result++) = 0x80 | opcode;
+
+  /* add the length */
+  *(result++) = is_masked | (char) payload_len;
+
+  /* add the mask */
+  if (0 != is_masked)
+  {
+    *(result++) = ((char *) &mask)[0];
+    *(result++) = ((char *) &mask)[1];
+    *(result++) = ((char *) &mask)[2];
+    *(result++) = ((char *) &mask)[3];
+  }
+
+  /* add the payload */
+  if (0 != payload_len)
+  {
+    MHD_websocket_copy_payload (result,
+                                payload,
+                                payload_len,
+                                mask,
+                                0);
+  }
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Encodes a websocket close frame
+ */
+_MHD_EXTERN int
+MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
+                            unsigned short reason_code,
+                            const char*reason_utf8,
+                            size_t reason_utf8_len,
+                            char**frame,
+                            size_t*frame_len)
+{
+  /* initialize output variables for errors cases */
+  if (NULL != frame)
+    *frame = NULL;
+  if (NULL != frame_len)
+    *frame_len = 0;
+
+  /* validate the parameters */
+  if ((NULL == ws) ||
+      (0 != reason_utf8_len) && (NULL == reason_utf8) ||
+      (NULL == frame) ||
+      (NULL == frame_len) ||
+      (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) && (1000 >
+                                                               reason_code) ||
+      (0 != reason_utf8_len) && (MHD_WEBSOCKET_CLOSEREASON_NO_REASON ==
+                                 reason_code) )
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload 
data, */
+  /* but in this case only 123 bytes, because 2 bytes are reserved */
+  /* for the close reason code. */
+  if (123 < reason_utf8_len)
+    return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
+
+  /* RFC 6455 5.5.1: If close payload data is given, it must be valid UTF-8 */
+  if (0 != reason_utf8_len)
+  {
+    int utf8_result = MHD_websocket_check_utf8 (reason_utf8,
+                                                reason_utf8_len,
+                                                NULL,
+                                                NULL);
+    if (MHD_WebSocket_UTF8Result_Valid != utf8_result)
+      return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
+  }
+
+  /* calculate length and masking */
+  char is_masked      = MHD_websocket_encode_is_masked (ws);
+  size_t payload_len  = (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code ?
+                         2 + reason_utf8_len : 0);
+  size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
+  size_t total_len    = overhead_len + payload_len;
+  unsigned long mask  = is_masked != 0 ? MHD_websocket_generate_mask () : 0;
+
+  /* allocate memory */
+  char*result = ws->malloc (total_len + 1);
+  if (NULL == result)
+    return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
+  result [total_len] = 0;
+  *frame     = result;
+  *frame_len = total_len;
+
+  /* add the opcode */
+  *(result++) = 0x88;
+
+  /* add the length */
+  *(result++) = is_masked | (char) payload_len;
+
+  /* add the mask */
+  if (0 != is_masked)
+  {
+    *(result++) = ((char *) &mask)[0];
+    *(result++) = ((char *) &mask)[1];
+    *(result++) = ((char *) &mask)[2];
+    *(result++) = ((char *) &mask)[3];
+  }
+
+  /* add the payload */
+  if (0 != reason_code)
+  {
+    /* close reason code */
+    unsigned short reason_code_nb = htons (reason_code);
+    MHD_websocket_copy_payload (result,
+                                (const char*) &reason_code_nb,
+                                2,
+                                mask,
+                                0);
+    result += 2;
+
+    /* custom reason payload */
+    if (0 != reason_utf8_len)
+    {
+      MHD_websocket_copy_payload (result,
+                                  reason_utf8,
+                                  reason_utf8_len,
+                                  mask,
+                                  2);
+    }
+  }
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Returns the 0x80 prefix for masked data, 0x00 otherwise
+ */
+static char
+MHD_websocket_encode_is_masked (struct MHD_WebSocketStream*ws)
+{
+  return (ws->flags & MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT) ==
+         MHD_WEBSOCKET_FLAG_CLIENT ? 0x80 : 0x00;
+}
+
+
+/**
+ * Calculates the size of the overhead in bytes
+ */
+static char
+MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream*ws,
+                                    size_t payload_len)
+{
+  return 2 + (MHD_websocket_encode_is_masked (ws) != 0 ? 4 : 0) + (125 <
+                                                                   payload_len 
?
+                                                                   (65535 <
+                                                                    payload_len
+  ? 8 : 2) : 0);
+}
+
+
+/**
+ * Copies the payload to the destination (using mask)
+ */
+static void
+MHD_websocket_copy_payload (char*dst,
+                            const char*src,
+                            size_t len,
+                            unsigned long mask,
+                            unsigned long mask_offset)
+{
+  if (0 != len)
+  {
+    if (0 == mask)
+    {
+      /* when the mask is zero, we can just copy the data */
+      memcpy (dst, src, len);
+    }
+    else
+    {
+      /* mask is used */
+      char mask_[4];
+      *((unsigned long *) mask_) = mask;
+      for (size_t i = 0; i < len; ++i)
+      {
+        dst[i] = src[i] ^ mask_[(i + mask_offset) & 3];
+      }
+    }
+  }
+}
+
+
+/**
+ * Checks a UTF-8 sequence
+ */
+static int
+MHD_websocket_check_utf8 (const char*buf,
+                          size_t buf_len,
+                          int*utf8_step,
+                          size_t*buf_offset)
+{
+  int utf8_step_ = (NULL != utf8_step) ? *utf8_step :
+                   MHD_WEBSOCKET_UTF8STEP_NORMAL;
+
+  for (size_t i = 0; i < buf_len; ++i)
+  {
+    unsigned char character = (unsigned char) buf[i];
+    switch (utf8_step_)
+    {
+    case MHD_WEBSOCKET_UTF8STEP_NORMAL:
+      if ((0x00 <= character) && (0x7F >= character))
+      {
+        /* RFC 3629 4: single byte UTF-8 sequence */
+        /* (nothing to do here) */
+      }
+      else if ((0xC2 <= character) && (0xDF >= character))
+      {
+        /* RFC 3629 4: two byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1;
+      }
+      else if (0xE0 == character)
+      {
+        /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 
0xA0-0xBF */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2;
+      }
+      else if (0xED == character)
+      {
+        /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 
0x80-0x9F */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2;
+      }
+      else if ((0xE1 <= character) && (0xEC >= character) ||
+               (0xEE <= character) && (0xEF >= character) )
+      {
+        /* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 
0x80-0xBF */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2;
+      }
+      else if (0xF0 == character)
+      {
+        /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 
0x90-0xBF */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3;
+      }
+      else if (0xF4 == character)
+      {
+        /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 
0x80-0x8F */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3;
+      }
+      else if ((0xF1 <= character) && (0xF3 >= character))
+      {
+        /* RFC 3629 4: four byte UTF-8 sequence, all three tail bytes must be 
0x80-0xBF */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2:
+      if ((0xA0 <= character) && (0xBF >= character))
+      {
+        /* RFC 3629 4: Second byte of three byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2:
+      if ((0x80 <= character) && (0x9F >= character))
+      {
+        /* RFC 3629 4: Second byte of three byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2:
+      if ((0x80 <= character) && (0xBF >= character))
+      {
+        /* RFC 3629 4: Second byte of three byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3:
+      if ((0x90 <= character) && (0xBF >= character))
+      {
+        /* RFC 3629 4: Second byte of four byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3:
+      if ((0x80 <= character) && (0x8F >= character))
+      {
+        /* RFC 3629 4: Second byte of four byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3:
+      if ((0x80 <= character) && (0xBF >= character))
+      {
+        /* RFC 3629 4: Second byte of four byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3:
+      if ((0x80 <= character) && (0xBF >= character))
+      {
+        /* RFC 3629 4: Third byte of four byte UTF-8 sequence */
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    /* RFC 3629 4: Second byte of two byte UTF-8 sequence */
+    case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1:
+    /* RFC 3629 4: Third byte of three byte UTF-8 sequence */
+    case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2:
+    /* RFC 3629 4: Fourth byte of four byte UTF-8 sequence */
+    case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3:
+      if ((0x80 <= character) && (0xBF >= character))
+      {
+        utf8_step_ = MHD_WEBSOCKET_UTF8STEP_NORMAL;
+      }
+      else
+      {
+        /* RFC 3629 4: Invalid UTF-8 byte */
+        if (NULL != buf_offset)
+          *buf_offset = i;
+        return MHD_WebSocket_UTF8Result_Invalid;
+      }
+      break;
+
+    default:
+      /* Invalid last step...? */
+      if (NULL != buf_offset)
+        *buf_offset = i;
+      return MHD_WebSocket_UTF8Result_Invalid;
+    }
+  }
+
+  /* return values */
+  if (NULL != utf8_step)
+    *utf8_step = utf8_step_;
+  if (NULL != buf_offset)
+    *buf_offset = buf_len;
+  if (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step_)
+  {
+    return MHD_WebSocket_UTF8Result_Incomplete;
+  }
+  return MHD_WebSocket_UTF8Result_Valid;
+}
+
+
+/**
+ * Calls srand in the scope of MHD to set the seed
+ * for the random number generator used for masking.
+ */
+_MHD_EXTERN int
+MHD_websocket_srand (unsigned long seed)
+{
+  srand (seed);
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Generates a mask for masking by calling
+ * a random number generator.
+ */
+static unsigned long
+MHD_websocket_generate_mask ()
+{
+  unsigned char mask_[4];
+  mask_ [0] = (unsigned char) (rand () & 0xFF);
+  mask_ [1] = (unsigned char) (rand () & 0xFF);
+  mask_ [2] = (unsigned char) (rand () & 0xFF);
+  mask_ [3] = (unsigned char) (rand () & 0xFF);
+
+  return *((unsigned long *) mask_);
+}
+
+
+/**
+ * Calls the malloc function associated with the websocket steam
+ */
+_MHD_EXTERN void*
+MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
+                      size_t len)
+{
+  if (NULL == ws)
+  {
+    return NULL;
+  }
+
+  return ws->malloc (len);
+}
+
+
+/**
+ * Calls the realloc function associated with the websocket steam
+ */
+_MHD_EXTERN void*
+MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
+                       void*cls,
+                       size_t len)
+{
+  if (NULL == ws)
+  {
+    return NULL;
+  }
+
+  return ws->realloc (cls, len);
+}
+
+
+/**
+ * Calls the free function associated with the websocket steam
+ */
+_MHD_EXTERN int
+MHD_websocket_free (struct MHD_WebSocketStream*ws,
+                    void*cls)
+{
+  if (NULL == ws)
+  {
+    return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+  }
+
+  ws->free (cls);
+
+  return MHD_WEBSOCKET_STATUS_OK;
+}
diff --git a/src/microhttpd_ws/sha1.c b/src/microhttpd_ws/sha1.c
new file mode 100644
index 00000000..910c1bdb
--- /dev/null
+++ b/src/microhttpd_ws/sha1.c
@@ -0,0 +1,420 @@
+/* sha1.c - Functions to compute SHA1 message digest of files or
+   memory blocks according to the NIST specification FIPS-180-1.
+
+   Copyright (C) 2000-2021 Free Software Foundation, Inc.
+
+   This program 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 program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Scott G. Miller
+   Credits:
+      Robert Klep <robert@ilse.nl>  -- Expansion function fix
+*/
+
+/*#include <config.h>*/
+
+#include "sha1.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) (n)
+#else
+# define SWAP(n) \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#endif
+
+#define BLOCKSIZE 4096
+#if BLOCKSIZE % 64 != 0
+# error "invalid BLOCKSIZE"
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/* Take a pointer to a 160 bit block of data (five 32 bit ints) and
+   initialize it to the start constants of the SHA1 algorithm.  This
+   must be called before using hash in the call to sha1_hash.  */
+void
+sha1_init_ctx (struct sha1_ctx *ctx)
+{
+  ctx->A = 0x67452301;
+  ctx->B = 0xefcdab89;
+  ctx->C = 0x98badcfe;
+  ctx->D = 0x10325476;
+  ctx->E = 0xc3d2e1f0;
+
+  ctx->total[0] = ctx->total[1] = 0;
+  ctx->buflen = 0;
+}
+
+
+/* Put result from CTX in first 20 bytes following RESBUF.  The result
+   must be in little endian byte order.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32-bit value.  */
+void *
+sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf)
+{
+  ((sha1_uint32 *) resbuf)[0] = SWAP (ctx->A);
+  ((sha1_uint32 *) resbuf)[1] = SWAP (ctx->B);
+  ((sha1_uint32 *) resbuf)[2] = SWAP (ctx->C);
+  ((sha1_uint32 *) resbuf)[3] = SWAP (ctx->D);
+  ((sha1_uint32 *) resbuf)[4] = SWAP (ctx->E);
+
+  return resbuf;
+}
+
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32-bit value.  */
+void *
+sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf)
+{
+  /* Take yet unprocessed bytes into account.  */
+  sha1_uint32 bytes = ctx->buflen;
+  size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
+
+  /* Now count remaining bytes.  */
+  ctx->total[0] += bytes;
+  if (ctx->total[0] < bytes)
+    ++ctx->total[1];
+
+  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+  ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29));
+  ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3);
+
+  memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
+
+  /* Process last bytes.  */
+  sha1_process_block (ctx->buffer, size * 4, ctx);
+
+  return sha1_read_ctx (ctx, resbuf);
+}
+
+
+/* Compute SHA1 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+int
+sha1_stream (FILE *stream, void *resblock)
+{
+  struct sha1_ctx ctx;
+  char buffer[BLOCKSIZE + 72];
+  size_t sum;
+
+  /* Initialize the computation context.  */
+  sha1_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+  {
+    /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read.  */
+    size_t n;
+    sum = 0;
+
+    /* Read block.  Take care for partial reads.  */
+    while (1)
+    {
+      n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+      sum += n;
+
+      if (sum == BLOCKSIZE)
+        break;
+
+      if (n == 0)
+      {
+        /* Check for the error flag IFF N == 0, so that we don't
+     exit the loop after a partial read due to e.g., EAGAIN
+     or EWOULDBLOCK.  */
+        if (ferror (stream))
+          return 1;
+        goto process_partial_block;
+      }
+
+      /* We've read at least one byte, so ignore errors.  But always
+         check for EOF, since feof may be true even though N > 0.
+         Otherwise, we could end up calling fread after EOF.  */
+      if (feof (stream))
+        goto process_partial_block;
+    }
+
+    /* Process buffer with BLOCKSIZE bytes.  Note that
+    BLOCKSIZE % 64 == 0
+     */
+    sha1_process_block (buffer, BLOCKSIZE, &ctx);
+  }
+
+process_partial_block:;
+
+  /* Process any remaining bytes.  */
+  if (sum > 0)
+    sha1_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  sha1_finish_ctx (&ctx, resblock);
+  return 0;
+}
+
+
+/* Compute SHA1 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+sha1_buffer (const char *buffer, size_t len, void *resblock)
+{
+  struct sha1_ctx ctx;
+
+  /* Initialize the computation context.  */
+  sha1_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 64 bytes.  */
+  sha1_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return sha1_finish_ctx (&ctx, resblock);
+}
+
+
+void
+sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx)
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+  {
+    size_t left_over = ctx->buflen;
+    size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+    memcpy (&((char *) ctx->buffer)[left_over], buffer, add);
+    ctx->buflen += add;
+
+    if (ctx->buflen > 64)
+    {
+      sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+      ctx->buflen &= 63;
+      /* The regions in the following copy operation cannot overlap.  */
+      memcpy (ctx->buffer,
+              &((char *) ctx->buffer)[(left_over + add) & ~63],
+              ctx->buflen);
+    }
+
+    buffer = (const char *) buffer + add;
+    len -= add;
+  }
+
+  /* Process available complete blocks.  */
+  if (len >= 64)
+  {
+#if ! _STRING_ARCH_unaligned
+# define alignof(type) offsetof (struct { char c; type x; }, x)
+# define UNALIGNED_P(p) (((size_t) p) % alignof (sha1_uint32) != 0)
+    if (UNALIGNED_P (buffer))
+      while (len > 64)
+      {
+        sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
+        buffer = (const char *) buffer + 64;
+        len -= 64;
+      }
+    else
+#endif
+    {
+      sha1_process_block (buffer, len & ~63, ctx);
+      buffer = (const char *) buffer + (len & ~63);
+      len &= 63;
+    }
+  }
+
+  /* Move remaining bytes in internal buffer.  */
+  if (len > 0)
+  {
+    size_t left_over = ctx->buflen;
+
+    memcpy (&((char *) ctx->buffer)[left_over], buffer, len);
+    left_over += len;
+    if (left_over >= 64)
+    {
+      sha1_process_block (ctx->buffer, 64, ctx);
+      left_over -= 64;
+      memmove (ctx->buffer, &ctx->buffer[16], left_over);
+    }
+    ctx->buflen = left_over;
+  }
+}
+
+
+/* --- Code below is the primary difference between md5.c and sha1.c --- */
+
+/* SHA1 round constants */
+#define K1 0x5a827999
+#define K2 0x6ed9eba1
+#define K3 0x8f1bbcdc
+#define K4 0xca62c1d6
+
+/* Round functions.  Note that F2 is the same as F4.  */
+#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) )
+#define F2(B,C,D) (B ^ C ^ D)
+#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) )
+#define F4(B,C,D) (B ^ C ^ D)
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.
+   Most of this code comes from GnuPG's cipher/sha1.c.  */
+
+void
+sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx)
+{
+  const sha1_uint32 *words = (const sha1_uint32*) buffer;
+  size_t nwords = len / sizeof (sha1_uint32);
+  const sha1_uint32 *endp = words + nwords;
+  sha1_uint32 x[16];
+  sha1_uint32 a = ctx->A;
+  sha1_uint32 b = ctx->B;
+  sha1_uint32 c = ctx->C;
+  sha1_uint32 d = ctx->D;
+  sha1_uint32 e = ctx->E;
+
+  /* First increment the byte count.  RFC 1321 specifies the possible
+     length of the file up to 2^64 bits.  Here we only compute the
+     number of bytes.  Do a double word increment.  */
+  ctx->total[0] += len;
+  ctx->total[1] += ((len >> 31) >> 1) + (ctx->total[0] < len);
+
+#define rol(x, n) (((x) << (n)) | ((sha1_uint32) (x) >> (32 - (n))))
+
+#define M(I) ( tm =   x[I&0x0f] ^ x[(I-14)&0x0f] \
+        ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \
+         , (x[I&0x0f] = rol(tm, 1)) )
+
+#define R(A,B,C,D,E,F,K,M)  do { E += rol( A, 5 )     \
+              + F( B, C, D )  \
+              + K       \
+              + M;        \
+         B = rol( B, 30 );    \
+             } while(0)
+
+  while (words < endp)
+  {
+    sha1_uint32 tm;
+    int t;
+    for (t = 0; t < 16; t++)
+    {
+      x[t] = SWAP (*words);
+      words++;
+    }
+
+    R (a, b, c, d, e, F1, K1, x[ 0]);
+    R (e, a, b, c, d, F1, K1, x[ 1]);
+    R (d, e, a, b, c, F1, K1, x[ 2]);
+    R (c, d, e, a, b, F1, K1, x[ 3]);
+    R (b, c, d, e, a, F1, K1, x[ 4]);
+    R (a, b, c, d, e, F1, K1, x[ 5]);
+    R (e, a, b, c, d, F1, K1, x[ 6]);
+    R (d, e, a, b, c, F1, K1, x[ 7]);
+    R (c, d, e, a, b, F1, K1, x[ 8]);
+    R (b, c, d, e, a, F1, K1, x[ 9]);
+    R (a, b, c, d, e, F1, K1, x[10]);
+    R (e, a, b, c, d, F1, K1, x[11]);
+    R (d, e, a, b, c, F1, K1, x[12]);
+    R (c, d, e, a, b, F1, K1, x[13]);
+    R (b, c, d, e, a, F1, K1, x[14]);
+    R (a, b, c, d, e, F1, K1, x[15]);
+    R (e, a, b, c, d, F1, K1, M (16) );
+    R (d, e, a, b, c, F1, K1, M (17) );
+    R (c, d, e, a, b, F1, K1, M (18) );
+    R (b, c, d, e, a, F1, K1, M (19) );
+    R (a, b, c, d, e, F2, K2, M (20) );
+    R (e, a, b, c, d, F2, K2, M (21) );
+    R (d, e, a, b, c, F2, K2, M (22) );
+    R (c, d, e, a, b, F2, K2, M (23) );
+    R (b, c, d, e, a, F2, K2, M (24) );
+    R (a, b, c, d, e, F2, K2, M (25) );
+    R (e, a, b, c, d, F2, K2, M (26) );
+    R (d, e, a, b, c, F2, K2, M (27) );
+    R (c, d, e, a, b, F2, K2, M (28) );
+    R (b, c, d, e, a, F2, K2, M (29) );
+    R (a, b, c, d, e, F2, K2, M (30) );
+    R (e, a, b, c, d, F2, K2, M (31) );
+    R (d, e, a, b, c, F2, K2, M (32) );
+    R (c, d, e, a, b, F2, K2, M (33) );
+    R (b, c, d, e, a, F2, K2, M (34) );
+    R (a, b, c, d, e, F2, K2, M (35) );
+    R (e, a, b, c, d, F2, K2, M (36) );
+    R (d, e, a, b, c, F2, K2, M (37) );
+    R (c, d, e, a, b, F2, K2, M (38) );
+    R (b, c, d, e, a, F2, K2, M (39) );
+    R (a, b, c, d, e, F3, K3, M (40) );
+    R (e, a, b, c, d, F3, K3, M (41) );
+    R (d, e, a, b, c, F3, K3, M (42) );
+    R (c, d, e, a, b, F3, K3, M (43) );
+    R (b, c, d, e, a, F3, K3, M (44) );
+    R (a, b, c, d, e, F3, K3, M (45) );
+    R (e, a, b, c, d, F3, K3, M (46) );
+    R (d, e, a, b, c, F3, K3, M (47) );
+    R (c, d, e, a, b, F3, K3, M (48) );
+    R (b, c, d, e, a, F3, K3, M (49) );
+    R (a, b, c, d, e, F3, K3, M (50) );
+    R (e, a, b, c, d, F3, K3, M (51) );
+    R (d, e, a, b, c, F3, K3, M (52) );
+    R (c, d, e, a, b, F3, K3, M (53) );
+    R (b, c, d, e, a, F3, K3, M (54) );
+    R (a, b, c, d, e, F3, K3, M (55) );
+    R (e, a, b, c, d, F3, K3, M (56) );
+    R (d, e, a, b, c, F3, K3, M (57) );
+    R (c, d, e, a, b, F3, K3, M (58) );
+    R (b, c, d, e, a, F3, K3, M (59) );
+    R (a, b, c, d, e, F4, K4, M (60) );
+    R (e, a, b, c, d, F4, K4, M (61) );
+    R (d, e, a, b, c, F4, K4, M (62) );
+    R (c, d, e, a, b, F4, K4, M (63) );
+    R (b, c, d, e, a, F4, K4, M (64) );
+    R (a, b, c, d, e, F4, K4, M (65) );
+    R (e, a, b, c, d, F4, K4, M (66) );
+    R (d, e, a, b, c, F4, K4, M (67) );
+    R (c, d, e, a, b, F4, K4, M (68) );
+    R (b, c, d, e, a, F4, K4, M (69) );
+    R (a, b, c, d, e, F4, K4, M (70) );
+    R (e, a, b, c, d, F4, K4, M (71) );
+    R (d, e, a, b, c, F4, K4, M (72) );
+    R (c, d, e, a, b, F4, K4, M (73) );
+    R (b, c, d, e, a, F4, K4, M (74) );
+    R (a, b, c, d, e, F4, K4, M (75) );
+    R (e, a, b, c, d, F4, K4, M (76) );
+    R (d, e, a, b, c, F4, K4, M (77) );
+    R (c, d, e, a, b, F4, K4, M (78) );
+    R (b, c, d, e, a, F4, K4, M (79) );
+
+    a = ctx->A += a;
+    b = ctx->B += b;
+    c = ctx->C += c;
+    d = ctx->D += d;
+    e = ctx->E += e;
+  }
+}
diff --git a/src/microhttpd_ws/sha1.h b/src/microhttpd_ws/sha1.h
new file mode 100644
index 00000000..be0d190b
--- /dev/null
+++ b/src/microhttpd_ws/sha1.h
@@ -0,0 +1,145 @@
+/* Declarations of functions and data types used for SHA1 sum
+   library functions.
+   Copyright (C) 2000-2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 3, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef SHA1_H
+# define SHA1_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/*#include "ansidecl.h"*/
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   doing that would require that the configure script compile and *run*
+   the resulting executable.  Locally running cross-compiled executables
+   is usually not possible.  */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t sha1_uint32;
+typedef uintptr_t sha1_uintptr;
+#elif defined (HAVE_SYS_TYPES_H) && defined (HAVE_STDINT_H)
+#include <stdint.h>
+#include <sys/types.h>
+typedef uint32_t sha1_uint32;
+typedef uintptr_t sha1_uintptr;
+#else
+#  define INT_MAX_32_BITS 2147483647
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+# ifndef INT_MAX
+#  define INT_MAX INT_MAX_32_BITS
+# endif
+
+# if INT_MAX == INT_MAX_32_BITS
+typedef unsigned int sha1_uint32;
+# else
+#  if SHRT_MAX == INT_MAX_32_BITS
+typedef unsigned short sha1_uint32;
+#  else
+#   if LONG_MAX == INT_MAX_32_BITS
+typedef unsigned long sha1_uint32;
+#   else
+/* The following line is intended to evoke an error.
+        Using #error is not portable enough.  */
+"Cannot determine unsigned 32-bit data type."
+#   endif
+#  endif
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure to save state of computation between the single steps.  */
+struct sha1_ctx
+{
+  sha1_uint32 A;
+  sha1_uint32 B;
+  sha1_uint32 C;
+  sha1_uint32 D;
+  sha1_uint32 E;
+
+  sha1_uint32 total[2];
+  sha1_uint32 buflen;
+  sha1_uint32 buffer[32];
+};
+
+
+/* Initialize structure containing state of computation. */
+extern void sha1_init_ctx (struct sha1_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 64!!! */
+extern void sha1_process_block (const void *buffer, size_t len,
+                                struct sha1_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void sha1_process_bytes (const void *buffer, size_t len,
+                                struct sha1_ctx *ctx);
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 20 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF be correctly
+   aligned for a 32 bits value.  */
+extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf);
+
+
+/* Put result from CTX in first 20 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf);
+
+
+/* Compute SHA1 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 20 bytes
+   beginning at RESBLOCK.  */
+extern int sha1_stream (FILE *stream, void *resblock);
+
+/* Compute SHA1 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *sha1_buffer (const char *buffer, size_t len, void *resblock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/microhttpd_ws/test_websocket.c 
b/src/microhttpd_ws/test_websocket.c
new file mode 100644
index 00000000..7e0cf4d6
--- /dev/null
+++ b/src/microhttpd_ws/test_websocket.c
@@ -0,0 +1,8983 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2021 David Gausmann
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
+*/
+/**
+ * @file test_websocket.c
+ * @brief  Testcase for WebSocket decoding/encoding
+ * @author David Gausmann
+ */
+#include "microhttpd.h"
+#include "microhttpd_ws.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+int disable_alloc = 0;
+size_t open_allocs = 0;
+
+/**
+ * Custom `malloc()` function used for memory tests
+ */
+static void*
+test_malloc (size_t buf_len)
+{
+  if (0 != disable_alloc)
+    return NULL;
+  void*result = malloc (buf_len);
+  if (NULL != result)
+    ++open_allocs;
+  return result;
+}
+
+
+/**
+ * Custom `realloc()` function used for memory tests
+ */
+static void*
+test_realloc (void*buf, size_t buf_len)
+{
+  if (0 != disable_alloc)
+    return NULL;
+  void*result = realloc (buf, buf_len);
+  if ((NULL != result) && (NULL == buf))
+    ++open_allocs;
+  return result;
+}
+
+
+/**
+ * Custom `free()` function used for memory tests
+ */
+static void
+test_free (void*buf)
+{
+  if (NULL != buf)
+    --open_allocs;
+  free (buf);
+}
+
+
+/**
+ * Helper function which allocates a big amount of data
+ */
+static void
+allocate_length_test_data (char**buf1,
+                           char**buf2,
+                           size_t buf_len,
+                           const char*buf1_prefix,
+                           size_t buf1_prefix_len)
+{
+  if (NULL != *buf1)
+    free (*buf1);
+  if (NULL != *buf2)
+    free (*buf2);
+  *buf1 = (char*) malloc (buf_len + buf1_prefix_len);
+  *buf2 = (char*) malloc (buf_len);
+  if ((NULL == buf1) || (NULL == buf2))
+    return;
+  memcpy (*buf1,
+          buf1_prefix,
+          buf1_prefix_len);
+  for (size_t i = 0; i < buf_len; i += 64)
+  {
+    size_t bytes_to_copy = buf_len - i;
+    if (64 < bytes_to_copy)
+      bytes_to_copy = 64;
+    memcpy (*buf1 + i + buf1_prefix_len,
+            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-",
+            bytes_to_copy);
+    memcpy (*buf2 + i,
+            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-",
+            bytes_to_copy);
+  }
+}
+
+
+/**
+ * Helper function which performs a single decoder test
+ */
+static int
+test_decode_single (unsigned int test_line,
+                    int flags, size_t max_payload_size, size_t decode_count,
+                    size_t buf_step,
+                    const char*buf, size_t buf_len,
+                    const char*expected_payload, size_t expected_payload_len,
+                    int expected_return, int expected_valid, size_t
+                    expected_streambuf_read_len)
+{
+  struct MHD_WebSocketStream *ws = NULL;
+  int ret = MHD_WEBSOCKET_STATUS_OK;
+
+  /* initialize stream */
+  ret = MHD_websocket_stream_init (&ws, flags, max_payload_size);
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "Allocation failed for decode test in line %u.\n",
+             (unsigned int) test_line);
+    return 1;
+  }
+
+  /* perform decoding in a loop */
+  size_t streambuf_read_len = 0;
+  size_t payload_len = 0;
+  char*payload = NULL;
+  for (size_t i = 0; i < decode_count; ++i)
+  {
+    size_t streambuf_read_len_ = 0;
+    size_t bytes_to_take = buf_len - streambuf_read_len;
+    if ((0 != buf_step) && (buf_step < bytes_to_take))
+      bytes_to_take = buf_step;
+    ret = MHD_websocket_decode (ws, buf + streambuf_read_len, bytes_to_take,
+                                &streambuf_read_len_, &payload, &payload_len);
+    streambuf_read_len += streambuf_read_len_;
+    if (i + 1 < decode_count)
+    {
+      if (payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+        payload_len = 0;
+      }
+    }
+  }
+
+  /* check the (last) result */
+  if (ret != expected_return)
+  {
+    fprintf (stderr,
+             "Decode test failed in line %u: The return value should be %d, 
but is %d\n",
+             (unsigned int) test_line,
+             (int) expected_return,
+             (int) ret);
+    MHD_websocket_free (ws, payload);
+    MHD_websocket_stream_free (ws);
+    return 1;
+  }
+  if (payload_len != expected_payload_len)
+  {
+    fprintf (stderr,
+             "Decode test failed in line %u: The payload_len should be %u, but 
is %u\n",
+             (unsigned int) test_line,
+             (unsigned int) expected_payload_len,
+             (unsigned int) payload_len);
+    MHD_websocket_free (ws, payload);
+    MHD_websocket_stream_free (ws);
+    return 1;
+  }
+  if (0 != payload_len)
+  {
+    if (NULL == payload)
+    {
+      fprintf (stderr,
+               "Decode test failed in line %u: The payload is NULL\n",
+               (unsigned int) test_line);
+      MHD_websocket_free (ws, payload);
+      MHD_websocket_stream_free (ws);
+      return 1;
+    }
+    else if (NULL == expected_payload)
+    {
+      fprintf (stderr,
+               "Decode test failed in line %u: The expected_payload is NULL 
(wrong test declaration)\n",
+               (unsigned int) test_line);
+      MHD_websocket_free (ws, payload);
+      MHD_websocket_stream_free (ws);
+      return 1;
+    }
+    else if (0 != memcmp (payload, expected_payload, payload_len))
+    {
+      fprintf (stderr,
+               "Decode test failed in line %u: The payload differs from the 
expected_payload\n",
+               (unsigned int) test_line);
+      MHD_websocket_free (ws, payload);
+      MHD_websocket_stream_free (ws);
+      return 1;
+    }
+  }
+  else
+  {
+    if (NULL != payload)
+    {
+      fprintf (stderr,
+               "Decode test failed in line %u: The payload is not NULL, but 
payload_len is 0\n",
+               (unsigned int) test_line);
+      MHD_websocket_free (ws, payload);
+      MHD_websocket_stream_free (ws);
+      return 1;
+    }
+    else if (NULL != expected_payload)
+    {
+      fprintf (stderr,
+               "Decode test failed in line %u: The expected_payload is not 
NULL, but expected_payload_len is 0 (wrong test declaration)\n",
+               (unsigned int) test_line);
+      MHD_websocket_free (ws, payload);
+      MHD_websocket_stream_free (ws);
+      return 1;
+    }
+  }
+  if (streambuf_read_len != expected_streambuf_read_len)
+  {
+    fprintf (stderr,
+             "Decode test failed in line %u: The streambuf_read_len should be 
%u, but is %u\n",
+             (unsigned int) test_line,
+             (unsigned int) expected_streambuf_read_len,
+             (unsigned int) streambuf_read_len);
+    MHD_websocket_free (ws, payload);
+    MHD_websocket_stream_free (ws);
+    return 1;
+  }
+  ret = MHD_websocket_stream_is_valid (ws);
+  if (ret != expected_valid)
+  {
+    fprintf (stderr,
+             "Decode test failed in line %u: The stream validity should be %u, 
but is %u\n",
+             (unsigned int) test_line,
+             (int) expected_valid,
+             (int) ret);
+    MHD_websocket_free (ws, payload);
+    MHD_websocket_stream_free (ws);
+    return 1;
+  }
+
+  /* cleanup */
+  MHD_websocket_free (ws, payload);
+  MHD_websocket_stream_free (ws);
+
+  return 0;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_stream_init()` and
+ * `MHD_websocket_stream_init()2`
+ */
+int
+test_inits ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*ws;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    All valid flags
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: all valid flags for init */
+  for (int i = 0; i < 7; ++i)
+  {
+    ws = NULL;
+    ret = MHD_websocket_stream_init (&ws,
+                                     i,
+                                     0);
+    if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+        (NULL == ws) )
+    {
+      fprintf (stderr,
+               "Init test failed in line %u for flags %d.\n",
+               (unsigned int) __LINE__,
+               (int) i);
+      ++failed;
+    }
+    if (NULL != ws)
+    {
+      MHD_websocket_stream_free (ws);
+      ws = NULL;
+    }
+  }
+  /* Regular test: all valid flags for init2 */
+  for (int i = 0; i < 7; ++i)
+  {
+    ws = NULL;
+    ret = MHD_websocket_stream_init2 (&ws,
+                                      i,
+                                      0,
+                                      test_malloc,
+                                      test_realloc,
+                                      test_free);
+    if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+        (NULL == ws) )
+    {
+      fprintf (stderr,
+               "Init test failed in line %u for flags %d.\n",
+               (unsigned int) __LINE__,
+               (int) i);
+      ++failed;
+    }
+    if (NULL != ws)
+    {
+      MHD_websocket_stream_free (ws);
+      ws = NULL;
+    }
+  }
+  /* Fail test: Invalid flags for init */
+  for (int i = 4; i < 32; ++i)
+  {
+    int flags = 1 << i;
+    ws = NULL;
+    ret = MHD_websocket_stream_init (&ws,
+                                     flags,
+                                     0);
+    if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+        (NULL != ws) )
+    {
+      fprintf (stderr,
+               "Init test failed in line %u for invalid flags %d.\n",
+               (unsigned int) __LINE__,
+               (int) flags);
+      ++failed;
+    }
+    if (NULL != ws)
+    {
+      MHD_websocket_stream_free (ws);
+      ws = NULL;
+    }
+  }
+  /* Fail test: Invalid flag for init2 */
+  for (int i = 4; i < 32; ++i)
+  {
+    int flags = 1 << i;
+    ws = NULL;
+    ret = MHD_websocket_stream_init2 (&ws,
+                                      flags,
+                                      0,
+                                      test_malloc,
+                                      test_realloc,
+                                      test_free);
+    if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+        (NULL != ws) )
+    {
+      fprintf (stderr,
+               "Init test failed in line %u for invalid flags %d.\n",
+               (unsigned int) __LINE__,
+               (int) flags);
+      ++failed;
+    }
+    if (NULL != ws)
+    {
+      MHD_websocket_stream_free (ws);
+      ws = NULL;
+    }
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    max_payload_size
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: max_payload_size = 0 for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (&ws,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   0);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 0.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Regular test: max_payload_size = 0 for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 0.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (success): max_payload_size = 1 for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (&ws,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   1);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 1.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (success): max_payload_size = 1 for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    1,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 1.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Regular test: max_payload_size = 1000 for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (&ws,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   1000);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 1000.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Regular test: max_payload_size = 1000 for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    1000,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 1000.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (&ws,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   (uint64_t) 0x7FFFFFFFFFFFFFFF);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 
0x7FFFFFFFFFFFFFFF.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    (uint64_t) 0x7FFFFFFFFFFFFFFF,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (NULL == ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 
0x7FFFFFFFFFFFFFFF.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (fail): max_payload_size = 0x8000000000000000 for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (&ws,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   (uint64_t) 0x8000000000000000);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 
0x8000000000000000.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Edge test (fail): max_payload_size = 0x8000000000000000 for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    (uint64_t) 0x8000000000000000,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u for max_payload_size 
0x8000000000000000.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: websocket stream variable missing for init */
+  ws = NULL;
+  ret = MHD_websocket_stream_init (NULL,
+                                   MHD_WEBSOCKET_FLAG_SERVER
+                                   | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                   0);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Fail test: websocket stream variable missing for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (NULL,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Fail test: malloc missing for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    NULL,
+                                    test_realloc,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Fail test: realloc missing for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    NULL,
+                                    test_free);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+  /* Fail test: free missing for init2 */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
+
+  return failed != 0 ? 0x01 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_create_accept()`
+ */
+int
+test_accept ()
+{
+  int failed = 0;
+  char accept_key[29];
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    accepting
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Test case from RFC6455 4.2.2 */
+  memset (accept_key, 0, 29);
+  ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==",
+                                     accept_key);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (0 != memcmp (accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", 29)))
+  {
+    fprintf (stderr,
+             "Accept test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: missing sec-key value */
+  memset (accept_key, 0, 29);
+  ret = MHD_websocket_create_accept (NULL,
+                                     accept_key);
+  if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret)
+  {
+    fprintf (stderr,
+             "Accept test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: missing accept variable */
+  memset (accept_key, 0, 29);
+  ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==",
+                                     NULL);
+  if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret)
+  {
+    fprintf (stderr,
+             "Accept test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x02 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_decode()`
+ */
+int
+test_decodes ()
+{
+  int failed = 0;
+  char *buf1 = NULL, *buf2 = NULL;
+
+  /*
+  
------------------------------------------------------------------------------
+    text frame
+  
------------------------------------------------------------------------------
+   */
+  /* Regular test: Masked text frame from RFC 6455, must succeed for server */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58",
+                                11,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Unmasked text frame from RFC 6455, must succeed for client 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x05\x48\x65\x6c\x6c\x6f",
+                                7,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Fail test: Unmasked text frame from RFC 6455, must fail for server */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x05\x48\x65\x6c\x6c\x6f",
+                                7,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Masked text frame from RFC 6455, must fail for client */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Text frame with UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x90\x00\x00\x00\x00" "This is my n"
+                                "\xC3\xB6" "te",
+                                22,
+                                "This is my n" "\xC3\xB6" "te",
+                                16,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                22);
+  /* Fail test: Text frame with with invalid UTF-8 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8F\x00\x00\x00\x00" "This is my n" 
"\xFF"
+                                "te",
+                                21,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                18);
+  /* Fail test: Text frame with broken UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8F\x00\x00\x00\x00" "This is my n" 
"\xC3"
+                                "te",
+                                21,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                19);
+  /* Regular test: Text frame without payload and mask (caller = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x80\x01\x02\x03\x04",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Fail test: Text frame without payload and no mask (caller = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Text frame without payload and mask (caller = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                2);
+  /* Fail test: Text frame without payload and no mask (caller = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x80\x01\x02\x03\x04",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+
+  /*
+  
------------------------------------------------------------------------------
+    binary frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Masked binary frame (decoder = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58",
+                                11,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Unmasked binary frame (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x05\x48\x65\x6c\x6c\x6f",
+                                7,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Fail test: Unmasked binary frame (decoder = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x05\x48\x65\x6c\x6c\x6f",
+                                7,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Masked binary frame (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Binary frame without payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Regular test: Fragmented binary frame without payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                12);
+  /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                12);
+  /* Regular test: Fragmented binary frame wit payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06",
+                                18,
+                                "\x01\x02\x03\x04\x05\x06",
+                                6,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                18);
+  /* Regular test: Fragmented binary frame with payload, fragments to the 
caller, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06",
+                                18,
+                                "\x01\x02\x03",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                9);
+  /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06",
+                                18,
+                                "\x04\x05\x06",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                18);
+  /* Regular test: Binary frame with bytes which look like invalid UTF-8 
character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x85\x00\x00\x00\x00" "Hell\xf6",
+                                11,
+                                "Hell\xf6",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Binary frame with bytes which look like broken UTF-8 
sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x85\x00\x00\x00\x00" "H\xC3llo",
+                                11,
+                                "H\xC3llo",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Binary frame with bytes which look like valid UTF-8 
sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x82\x85\x00\x00\x00\x00" "H\xC3\xA4lo",
+                                11,
+                                "H\xC3\xA4lo",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Fragmented binary frame with bytes which look like valid 
UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x02\x82\x00\x00\x00\x00" "H\xC3"
+                                "\x80\x83\x00\x00\x00\x00" "\xA4lo",
+                                17,
+                                "H\xC3\xA4lo",
+                                5,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented binary frame with bytes which look like valid 
UTF-8 sequence,
+     fragments to the caller, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x02\x82\x00\x00\x00\x00" "H\xC3"
+                                "\x80\x83\x00\x00\x00\x00" "\xA4lo",
+                                17,
+                                "H\xC3",
+                                2,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                8);
+  /* Regular test: Fragmented binary frame with bytes which look like valid 
UTF-8 sequence,
+     fragments to the caller, 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x02\x82\x00\x00\x00\x00" "H\xC3"
+                                "\x80\x83\x00\x00\x00\x00" "\xA4lo",
+                                17,
+                                "\xA4lo",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+
+  /*
+  
------------------------------------------------------------------------------
+    close frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Close frame with no payload but with mask (decoder = 
server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                6);
+  /* Regular test: Close frame with no payload (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                2);
+  /* Fail test: Close frame with no payload and no mask (decoder = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Close frame with no payload but with mask (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Close frame with 2 byte payload for close reason */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x82\x00\x00\x00\x00\x03\xEB",
+                                8,
+                                "\x03\xEB",
+                                2,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                8);
+  /* Fail test: Close frame with 1 byte payload (no valid close reason) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x81\x00\x00\x00\x00\x03",
+                                7,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Close frame with close reason and UTF-8 description */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x95\x00\x00\x00\x00\x03\xEB"
+                                "Something was wrong",
+                                27,
+                                "\x03\xEB" "Something was wrong",
+                                21,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                27);
+  /* Regular test: Close frame with close reason and UTF-8 description (with 
UTF-8 sequence) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x96\x00\x00\x00\x00\x03\xEB"
+                                "Something was wr" "\xC3\xB6" "ng",
+                                28,
+                                "\x03\xEB" "Something was wr" "\xC3\xB6" "ng",
+                                22,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                28);
+  /* Fail test: Close frame with close reason and invalid UTF-8 in description 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x95\x00\x00\x00\x00\x03\xEB"
+                                "Something was wr" "\xFF" "ng",
+                                27,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                24);
+  /* Fail test: Close frame with close reason and broken UTF-8 sequence in 
description */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\x95\x00\x00\x00\x00\x03\xEB"
+                                "Something was wr" "\xC3" "ng",
+                                27,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                25);
+  /* Edge test (success): Close frame with 125 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\xFD\x00\x00\x00\x00\x03\xEB"
+                                "Something was wrong, so I decided to close 
this websocket. I hope you are not angry. But this is also the 123 cap test. 
:-)",
+                                131,
+                                "\x03\xEB"
+                                "Something was wrong, so I decided to close 
this websocket. I hope you are not angry. But this is also the 123 cap test. 
:-)",
+                                125,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                131);
+  /* Edge test (failure): Close frame with 126 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\xFE\x00\x7e\x00\x00\x00\x00\x03\xEB"
+                                "Something was wrong, so I decided to close 
this websocket. I hope you are not angry. But this is also the 123 cap test. 
>:-)",
+                                134,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Close frame with 500 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\xFE\x01\xf4\x00\x00\x00\x00\x03\xEB"
+                                "The payload of this test isn't parsed.",
+                                49,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Edge test (failure): Close frame with 65535 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x88\xFE\xff\xff\x00\x00\x00\x00\x03\xEB"
+                                "The payload of this test isn't parsed.",
+                                49,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Edge test (failure): Close frame with 65536 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x88\xFF\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xEB"
+                                "The payload of this test isn't parsed.",
+                                54,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Close frame with 1000000 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x88\xFF\x00\x00\x00\x00\x00\x0F\x42\x40\x00\x00\x00\x00\x03\xEB"
+                                "The payload of this test isn't parsed.",
+                                54,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+
+  /*
+  
------------------------------------------------------------------------------
+    ping frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Ping frame with no payload but with mask (decoder = server) 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Regular test: Ping frame with no payload (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                2);
+  /* Fail test: Ping frame with no payload and no mask (decoder = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Ping frame with no payload but with mask (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Ping frame with some (masked) payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x89\x88\x01\x20\x03\x40\xFF\xFF\xFF\xFF\x00\x00\x00\x00",
+                                14,
+                                "\xFE\xDF\xFC\xBF\x01\x20\x03\x40",
+                                8,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                14);
+  /* Edge test (success): Ping frame with one byte of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x81\x00\x00\x00\x00" "a",
+                                7,
+                                "a",
+                                1,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Edge test (success): Ping frame with 125 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\xFD\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                131,
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                125,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                131);
+  /* Edge test (fail): Ping frame with 126 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\xFE\x00\x7E\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                134,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Ping frame with UTF-8 data */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x90\x00\x00\x00\x00" "Ping is bin"
+                                "\xC3\xA4" "ry.",
+                                22,
+                                "Ping is bin" "\xC3\xA4" "ry.",
+                                16,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                22);
+  /* Regular test: Ping frame with invalid UTF-8 data */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x8F\x00\x00\x00\x00" "Ping is bin" "\xFF"
+                                "ry.",
+                                21,
+                                "Ping is bin" "\xFF" "ry.",
+                                15,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                21);
+  /* Regular test: Ping frame with broken UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x8F\x00\x00\x00\x00" "Ping is bin" "\xC3"
+                                "ry.",
+                                21,
+                                "Ping is bin" "\xC3" "ry.",
+                                15,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                21);
+
+  /*
+  
------------------------------------------------------------------------------
+    pong frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Pong frame with no payload but with mask (decoder = server) 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Regular test: Pong frame with no payload (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                2);
+  /* Fail test: Pong frame with no payload and no mask (decoder = server) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x00",
+                                2,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Fail test: Pong frame with no payload but with mask (decoder = client) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_CLIENT
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Pong frame with some (masked) payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x8A\x88\x01\x20\x03\x40\xFF\xFF\xFF\xFF\x00\x00\x00\x00",
+                                14,
+                                "\xFE\xDF\xFC\xBF\x01\x20\x03\x40",
+                                8,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                14);
+  /* Edge test (success): Pong frame with one byte of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x81\x00\x00\x00\x00" "a",
+                                7,
+                                "a",
+                                1,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Edge test (success): Pong frame with 125 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\xFD\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                131,
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                125,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                131);
+  /* Edge test (fail): Pong frame with 126 bytes of payload */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\xFE\x00\x7E\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                134,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                1);
+  /* Regular test: Pong frame with UTF-8 data */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x90\x00\x00\x00\x00" "Pong is bin"
+                                "\xC3\xA4" "ry.",
+                                22,
+                                "Pong is bin" "\xC3\xA4" "ry.",
+                                16,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                22);
+  /* Regular test: Pong frame with invalid UTF-8 data */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x8F\x00\x00\x00\x00" "Pong is bin" "\xFF"
+                                "ry.",
+                                21,
+                                "Pong is bin" "\xFF" "ry.",
+                                15,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                21);
+  /* Regular test: Pong frame with broken UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8A\x8F\x00\x00\x00\x00" "Pong is bin" "\xC3"
+                                "ry.",
+                                21,
+                                "Pong is bin" "\xC3" "ry.",
+                                15,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                21);
+
+  /*
+  
------------------------------------------------------------------------------
+    fragmentation
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Fragmented, masked text frame, we are the server and don't 
want fragments as caller */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58",
+                                17,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented, masked text frame, we are the server and don't 
want fragments as caller, but call decode two times */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, one call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58",
+                                17,
+                                "Hel",
+                                3,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                9);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, second call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58",
+                                17,
+                                "lo",
+                                2,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, third call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                3,
+                                0,
+                                
"\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+
+
+  /*
+  
------------------------------------------------------------------------------
+    invalid flags
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Template with valid data for the next tests (this one must 
succeed) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00Hello",
+                                11,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Fail test: RSV1 flag set */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x91\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: RSV2 flag set */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\xA1\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: RSV3 flag set */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\xC1\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+
+  /*
+  
------------------------------------------------------------------------------
+    invalid opcodes
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: Invalid opcode 0 (0 is usually valid, but only if there was a 
data frame before) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x80\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 3 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x83\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 4 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x84\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 5 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x85\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 6 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x86\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 7 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x87\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 0x0B */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8B\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 0x0C */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8c\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 0x0D */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8d\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 0x0E */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8e\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Invalid opcode 0x0F */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x8f\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+
+
+  /*
+  
------------------------------------------------------------------------------
+    control frames without FIN flag
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: Close frame without FIN flag */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x08\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Ping frame without FIN flag */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x09\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Fail test: Pong frame without FIN flag */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x0a\x85\x00\x00\x00\x00Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+
+  /*
+  
------------------------------------------------------------------------------
+    length checks (without max_payload_len)
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): 0 bytes of payload (requires 1 byte length) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x80\x00\x00\x00\x00",
+                                6,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                6);
+  /* Edge test (success): 1 byte of payload (requires 1 byte length) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x81\x00\x00\x00\x00" "a",
+                                7,
+                                "a",
+                                1,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Edge test (success): 125 bytes of payload (requires 1 byte length) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xfd\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                131,
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                125,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                131);
+  /* Edge test (success): 126 bytes of payload (requires 2 byte length) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xfe\x00\x7e\x00\x00\x00\x00"
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                134,
+                                
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                126,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                134);
+  /* Edge test (success): 65535 bytes of payload (requires 2 byte length) */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65535,
+                             "\x81\xfe\xff\xff\x00\x00\x00\x00",
+                             8);
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                buf1,
+                                65535 + 8,
+                                buf2,
+                                65535,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                65535 + 8);
+  /* Edge test (success): 65536 bytes of payload (requires 8 byte length) */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65536,
+                             
"\x81\xff\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00",
+                             14);
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                buf1,
+                                65536 + 14,
+                                buf2,
+                                65536,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                65536 + 14);
+  /* Regular test: 1 MB of payload */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             1048576,
+                             
"\x81\xff\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00",
+                             14);
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                buf1,
+                                1048576 + 14,
+                                buf2,
+                                1048576,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                1048576 + 14);
+  /* Regular test: 100 MB of payload */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             104857600,
+                             
"\x81\xff\x00\x00\x00\x00\x06\x40\x00\x00\x00\x00\x00\x00",
+                             14);
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                buf1,
+                                104857600 + 14,
+                                buf2,
+                                104857600,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                104857600 + 14);
+  if (NULL != buf1)
+  {
+    free (buf1);
+    buf1 = NULL;
+  }
+  if (NULL != buf2)
+  {
+    free (buf2);
+    buf2 = NULL;
+  }
+  /* Edge test (success): Maximum allowed length (here is only the header 
checked) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xff\x7f\xff\xff\xff\xff\xff\xff\xff",
+                                10,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                10);
+  /* Edge test (fail): Too big payload length */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xff\x80\x00\x00\x00\x00\x00\x00\x00",
+                                10,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+  /* Edge test (fail): Too big payload length */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+                                10,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+  /* Fail test: Not the smallest payload length syntax used (2 byte instead of 
1 byte) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\xfe\x00\x05\x00\x00\x00\x00" "abcde",
+                                13,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                4);
+  /* Fail test: Not the smallest payload length syntax used (8 byte instead of 
1 byte) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x81\xff\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00"
+                                "abcde",
+                                13,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+  /* Fail test: Not the smallest payload length syntax used (8 byte instead of 
2 byte) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x81\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00"
+                                "abcde",
+                                13,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+
+  /*
+  
------------------------------------------------------------------------------
+    length checks (with max_payload_len)
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Frame with less payload than specified as limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                100,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00" "Hello",
+                                11,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Edge test (success): Frame with the same payload as the specified limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                5,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00" "Hello",
+                                11,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Edge test (fail): Frame with more payload than specified as limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                4,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00" "Hello",
+                                11,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                2);
+  /* Regular test: Fragmented frames with the sum of payload less than 
specified as limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                100,
+                                1,
+                                0,
+                                "\x01\x83\x00\x00\x00\x00"
+                                "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                17,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Edge test (success): Fragmented frames with the sum of payload equal to 
the specified limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                5,
+                                1,
+                                0,
+                                "\x01\x83\x00\x00\x00\x00"
+                                "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                17,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Edge test (fail): Fragmented frames with the sum of payload more than 
specified as limit */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                4,
+                                1,
+                                0,
+                                "\x01\x83\x00\x00\x00\x00"
+                                "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                15);
+  /* Edge test (success): Fragmented frames with the sum of payload greater 
than
+     the specified limit, but we take fragments (one call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                5,
+                                1,
+                                0,
+                                "\x01\x83\x00\x00\x00\x00"
+                                "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                17,
+                                "Hel",
+                                3,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                9);
+  /* Edge test (success): Fragmented frames with the sum of payload greater 
than
+     the specified limit, but we take fragments (two calls) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                5,
+                                2,
+                                0,
+                                "\x01\x83\x00\x00\x00\x00"
+                                "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                17,
+                                "lo",
+                                2,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+
+  /*
+  
------------------------------------------------------------------------------
+    UTF-8 sequences
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: No UTF-8 characters  */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 a        ",
+                                16,
+                                " a        ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Fail test: A UTF-8 tail character without sequence start character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xA4        ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Regular test: A two byte UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xC3\xA4       ",
+                                16,
+                                " \xC3\xA4       ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Fail test: A broken two byte UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xC3        ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Fail test: A two byte UTF-8 sequence with one UTF-8 tail too much */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xC3\xA4\xA4      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                9);
+  /* Regular test: A three byte UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\x8F\x8F      ",
+                                16,
+                                " \xEF\x8F\x8F      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Fail test: A broken byte UTF-8 sequence (two of three bytes) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\x8F       ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                9);
+  /* Fail test: A broken byte UTF-8 sequence (one of three bytes) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF        ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Fail test: A three byte UTF-8 sequence followed by one UTF-8 tail byte */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\x8F\x8F\x8F     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+  /* Regular test: A four byte UTF-8 sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F\x8F     
",
+                                16,
+                                " \xF2\x8F\x8F\x8F     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Fail test: A broken four byte UTF-8 sequence (three of four bytes) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+  /* Fail test: A broken four byte UTF-8 sequence (two of four bytes) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF2\x8F       ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                9);
+  /* Fail test: A broken four byte UTF-8 sequence (one of four bytes) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF2        ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Fail test: A four byte UTF-8 sequence followed by UTF-8 tail */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F\x8F\x8F 
   ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                11);
+  /* Fail test: A five byte UTF-8 sequence (only up to four bytes allowed) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xFB\x8F\x8F\x8F\x8F 
   ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Fail test: A six byte UTF-8 sequence (only up to four bytes allowed) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 
\xFD\x8F\x8F\x8F\x8F\x8F   ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Fail test: A seven byte UTF-8 sequence (only up to four bytes allowed) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 
\xFE\x8F\x8F\x8F\x8F\x8F\x8F  ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Fail test: A eight byte UTF-8 sequence (only up to four bytes allowed) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 
\xFF\x8F\x8F\x8F\x8F\x8F\x8F\x8F ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Edge test (success): The maxium allowed UTF-8 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF4\x8F\xBF\xBF     
",
+                                16,
+                                " \xF4\x8F\xBF\xBF     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The maxium allowed UTF-8 character + 1 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF4\x90\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The last valid UTF8-1 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \x7F        ",
+                                16,
+                                " \x7F        ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the last valid UTF8-1 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \x80        ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Edge test (fail): The value before the first valid UTF8-2 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xC1\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Edge test (success): The first valid UTF8-2 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xC2\x80       ",
+                                16,
+                                " \xC2\x80       ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-2 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xDF\xBF       ",
+                                16,
+                                " \xDF\xBF       ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the lst valid UTF8-2 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE0\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): The value before the first valid UTF8-3 character (tail 
1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE0\x9F\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-3 character (tail 1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE0\xA0\x80      ",
+                                16,
+                                " \xE0\xA0\x80      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-3 character (tail 1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE0\xBF\xBF      ",
+                                16,
+                                " \xE0\xBF\xBF      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the first valid UTF8-3 character (tail 
1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE0\xC0\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-3 character (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xE1\x80\x80      ",
+                                16,
+                                " \xE1\x80\x80      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-3 character (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEC\xBF\xBF      ",
+                                16,
+                                " \xEC\xBF\xBF      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the last valid UTF8-3 character (tail 
2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEC\xC0\xBF      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): The value before the first valid UTF8-3 character (tail 
3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xED\x7F\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-3 character (tail 3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xED\x80\x80      ",
+                                16,
+                                " \xED\x80\x80      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-3 character (tail 3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xED\x9F\xBF      ",
+                                16,
+                                " \xED\x9F\xBF      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the last valid UTF8-3 character (tail 
3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xED\xA0\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): The value before the first valid UTF8-3 character (tail 
4) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEE\x7F\x80      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-3 character (tail 4) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEE\x80\x80      ",
+                                16,
+                                " \xEE\x80\x80      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-3 character (tail 4) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\xBF\xBF      ",
+                                16,
+                                " \xEF\xBF\xBF      ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the last valid UTF8-3 character (tail 
4) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\xBF\xC0      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                9);
+  /* Edge test (fail): The value after the last valid UTF8-3 character (tail 
4) #2 */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xEF\xC0\xBF      ",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): The value before the first valid UTF8-4 character (tail 
1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF0\x8F\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-4 character (tail 1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF0\x90\x80\x80     
",
+                                16,
+                                " \xF0\x90\x80\x80     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-4 character (tail 1) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF0\xBF\xBF\xBF     
",
+                                16,
+                                " \xF0\xBF\xBF\xBF     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The first valid UTF8-4 character (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF1\x80\x80\x80     
",
+                                16,
+                                " \xF1\x80\x80\x80     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-4 character (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF3\xBF\xBF\xBF     
",
+                                16,
+                                " \xF3\xBF\xBF\xBF     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): A value before the last valid UTF8-4 character in the 
second byte (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF3\x7F\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): A value after the last valid UTF8-4 character in the 
second byte (tail 2) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF3\xC0\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (success): The first valid UTF8-4 character (tail 3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF4\x80\x80\x80     
",
+                                16,
+                                " \xF4\x80\x80\x80     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (success): The last valid UTF8-4 character (tail 3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF4\x8F\xBF\xBF     
",
+                                16,
+                                " \xF4\x8F\xBF\xBF     ",
+                                10,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Edge test (fail): The value after the last valid UTF8-4 character (tail 
3) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF4\x90\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                8);
+  /* Edge test (fail): The first byte value the last valid UTF8-4 character */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x8A\x00\x00\x00\x00 \xF5\x90\x80\x80     
",
+                                16,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+
+  /*
+  
------------------------------------------------------------------------------
+    Unfinished UTF-8 sequence between fragmented text frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: UTF-8 sequence between fragments, no fragmentation for the 
caller */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x8D\x00\x00\x00\x00" "This is my n"
+                                "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te",
+                                28,
+                                "This is my n" "\xC3\xB6" "te",
+                                16,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                28);
+  /* Regular test: UTF-8 sequence between fragments, fragmentation for the 
caller, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x8D\x00\x00\x00\x00" "This is my n"
+                                "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te",
+                                28,
+                                "This is my n",
+                                12,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                19);
+  /* Regular test: UTF-8 sequence between fragments, fragmentation for the 
caller, 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x01\x8D\x00\x00\x00\x00" "This is my n"
+                                "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te",
+                                28,
+                                "\xC3\xB6" "te",
+                                4,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                28);
+  /* Edge test (success): UTF-8 sequence between fragments, but nothing 
before, fragmentation for the caller, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x01\x81\x00\x00\x00\x00\xC3\x80\x81\x00\x00\x00\x00\xB6",
+                                14,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                7);
+  /* Edge test (success): UTF-8 sequence between fragments, but nothing 
before, fragmentation for the caller, 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x01\x81\x00\x00\x00\x00\xC3\x80\x81\x00\x00\x00\x00\xB6",
+                                14,
+                                "\xC3\xB6",
+                                2,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                14);
+
+  /*
+  
------------------------------------------------------------------------------
+    Decoding with broken stream
+  
------------------------------------------------------------------------------
+  */
+  /* Failure test: Invalid sequence */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\xFF\x81\x85\x00\x00\x00\x00" "Hello",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Failure test: Call after invalidated stream */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\xFF\x81\x85\x00\x00\x00\x00" "Hello",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_STREAM_BROKEN,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Failure test: Call after invalidated stream (but with different buffer) */
+  {
+    struct MHD_WebSocketStream*ws;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws,
+                                                              
MHD_WEBSOCKET_FLAG_SERVER
+                                                              |
+                                                              
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                              0))
+    {
+      size_t streambuf_read_len = 0;
+      char*payload = NULL;
+      size_t payload_len = 0;
+      int ret = 0;
+      ret = MHD_websocket_decode (ws,
+                                  "\xFF",
+                                  1,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if (MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR != ret)
+      {
+        fprintf (stderr,
+                 "Test failed in line %u: The return value should be -1, but 
is %d\n",
+                 (unsigned int) __LINE__,
+                 (int) ret);
+        ++failed;
+      }
+      else
+      {
+        ret = MHD_websocket_decode (ws,
+                                    "\x81\x85\x00\x00\x00\x00" "Hello",
+                                    11,
+                                    &streambuf_read_len,
+                                    &payload,
+                                    &payload_len);
+        if (MHD_WEBSOCKET_STATUS_STREAM_BROKEN != ret)
+        {
+          fprintf (stderr,
+                   "Test failed in line %u: The return value should be -2, but 
is %d\n",
+                   (unsigned int) __LINE__,
+                   (int) ret);
+          ++failed;
+        }
+      }
+      MHD_websocket_stream_free (ws);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Individual test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    frame after close frame
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x81\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                6);
+  /* Failure test: Text frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x81\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                6);
+  /* Failure test: Binary frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x82\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                6);
+  /* Failure test: Continue frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x80\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                6);
+  /* Regular test: Ping frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x89\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                17);
+  /* Regular test: Pong frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x8A\x85\x00\x00\x00\x00"
+                                "Hello",
+                                17,
+                                "Hello",
+                                5,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                17);
+  /* Regular test: Close frame after close frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                
"\x88\x80\x00\x00\x00\x00\x88\x80\x00\x00\x00\x00",
+                                12,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                12);
+
+  /*
+  
------------------------------------------------------------------------------
+    decoding byte-by-byte
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Text frame, 2 bytes per loop, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                2,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                2);
+  /* Regular test: Text frame, 2 bytes per loop, 11th call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                11,
+                                2,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                22);
+  /* Regular test: Text frame, 2 bytes per loop, 12th call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                12,
+                                2,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                "This is the test.",
+                                17,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                23);
+  /* Regular test: Text frame, 1 byte per loop, 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                1,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                1);
+  /* Regular test: Text frame, 1 byte per loop, 22nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                22,
+                                1,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_OK,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                22);
+  /* Regular test: Text frame, 1 byte per loop, 23rd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                23,
+                                1,
+                                "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/",
+                                23,
+                                "This is the test.",
+                                17,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                23);
+
+  /*
+  
------------------------------------------------------------------------------
+    mix of fragmented data frames and control frames
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Fragmented text frame mixed with one ping frame (1st call) 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x89\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented text frame mixed with one ping frame (2nd call) 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x89\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                "This is the test.",
+                                17,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                35);
+  /* Regular test: Fragmented text frame mixed with one close frame (1st call) 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x88\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                17);
+  /* Fail test: Fragmented text frame mixed with one ping frame (2nd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x88\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                17);
+  /* Regular test: Fragmented text frame mixed with one ping frame, the caller 
wants fragments (1st call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x89\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                "This ",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Fragmented text frame mixed with one ping frame, the caller 
wants fragments (2nd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x89\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                17);
+  /* Regular test: Fragmented text frame mixed with one ping frame, the caller 
wants fragments (3rd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                3,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x89\x80\x00\x00\x00\x00"
+                                "\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                35,
+                                "is the test.",
+                                12,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                35);
+
+  /*
+  
------------------------------------------------------------------------------
+    mix of fragmented data frames and data frames
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: Fragmented text frame mixed with one non-fragmented binary 
frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x82\x81\x00\x00\x00\x00"
+                                "a\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                36,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                11);
+  /* Regular test: Fragmented text frame mixed with one non-fragmented binary 
frame; the caller wants fragments; 1st call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x82\x81\x00\x00\x00\x00"
+                                "a\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                36,
+                                "This ",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Fail test: Fragmented text frame mixed with one non-fragmented binary 
frame; the caller wants fragments; 2nd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x82\x81\x00\x00\x00\x00"
+                                "a\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                36,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                11);
+  /* Fail test: Fragmented text frame mixed with one fragmented binary frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x02\x81\x00\x00\x00\x00"
+                                "a\x80\x8C\x00\x00\x00\x00" "is the test.",
+                                36,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                11);
+  /* Fail test: Fragmented text frame, continue frame, non-fragmented binary 
frame */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x00\x8C\x00\x00\x00\x00"
+                                "is the test.\x82\x81\x00\x00\x00\x00" "a",
+                                36,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                29);
+  /* Fail test: Fragmented text frame, continue frame, fragmented binary frame 
*/
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x01\x85\x00\x00\x00\x00"
+                                "This \x00\x8C\x00\x00\x00\x00"
+                                "is the test.\x02\x81\x00\x00\x00\x00" "a",
+                                36,
+                                NULL,
+                                0,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                29);
+
+  /*
+  
------------------------------------------------------------------------------
+    multiple data frames
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Text frame, binary frame, text frame (1st call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00"
+                                "This \x82\x87\x00\x00\x00\x00"
+                                "is the \x81\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "This ",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Text frame, binary frame, text frame (2nd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00"
+                                "This \x82\x87\x00\x00\x00\x00"
+                                "is the \x81\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "is the ",
+                                7,
+                                MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                24);
+  /* Regular test: Text frame, binary frame, text frame (3rd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                3,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00"
+                                "This \x82\x87\x00\x00\x00\x00"
+                                "is the \x81\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "test.",
+                                5,
+                                MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                35);
+  /*
+  
------------------------------------------------------------------------------
+    multiple control frames
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Ping frame, pong frame, close frame (1st call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                1,
+                                0,
+                                "\x89\x85\x00\x00\x00\x00"
+                                "This \x8A\x87\x00\x00\x00\x00"
+                                "is the \x88\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "This ",
+                                5,
+                                MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                11);
+  /* Regular test: Ping frame, pong frame, close frame (2nd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                2,
+                                0,
+                                "\x89\x85\x00\x00\x00\x00"
+                                "This \x8A\x87\x00\x00\x00\x00"
+                                "is the \x88\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "is the ",
+                                7,
+                                MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                24);
+  /* Regular test: Ping frame, pong frame, close frame (3rd call) */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                0,
+                                3,
+                                0,
+                                "\x89\x85\x00\x00\x00\x00"
+                                "This \x8A\x87\x00\x00\x00\x00"
+                                "is the \x88\x85\x00\x00\x00\x00" "test.",
+                                35,
+                                "test.",
+                                5,
+                                MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                35);
+
+  /*
+  
------------------------------------------------------------------------------
+    generated close frames for errors
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Close frame generated for protocol error */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
+                                |
+                                
MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR,
+                                0,
+                                1,
+                                0,
+                                "\xFF",
+                                1,
+                                "\x88\x02\x03\xEA",
+                                4,
+                                MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                0);
+  /* Regular test: Close frame generated for UTF-8 sequence error */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
+                                |
+                                
MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR,
+                                0,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00T\xFFst.",
+                                11,
+                                "\x88\x02\x03\xEF",
+                                4,
+                                MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                7);
+  /* Regular test: Close frame generated for message size exceeded */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
+                                |
+                                
MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR,
+                                3,
+                                1,
+                                0,
+                                "\x81\x85\x00\x00\x00\x00T\xFFst.",
+                                11,
+                                "\x88\x02\x03\xF1",
+                                4,
+                                MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                2);
+
+  /*
+  
------------------------------------------------------------------------------
+    terminating NUL character
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*ws;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws,
+                                                              
MHD_WEBSOCKET_FLAG_SERVER
+                                                              |
+                                                              
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                              0))
+    {
+      size_t streambuf_read_len = 0;
+      char*payload = NULL;
+      size_t payload_len = 0;
+      int ret = 0;
+
+      /* Regular test: text frame */
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00" "Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) ||
+          (5 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("Hello", payload, 5 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: text frame fragment */
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x83\x00\x00\x00\x00"
+                                  "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                  17,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) ||
+          (5 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("Hello", payload, 5 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: binary frame */
+      ret = MHD_websocket_decode (ws,
+                                  "\x82\x85\x00\x00\x00\x00" "Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_BINARY_FRAME != ret) ||
+          (5 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("Hello", payload, 5 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: binary frame fragment */
+      ret = MHD_websocket_decode (ws,
+                                  "\x02\x83\x00\x00\x00\x00"
+                                  "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                  17,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_BINARY_FRAME != ret) ||
+          (5 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("Hello", payload, 5 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      MHD_websocket_stream_free (ws);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Individual decode test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+  {
+    struct MHD_WebSocketStream*ws;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws,
+                                                              
MHD_WEBSOCKET_FLAG_SERVER
+                                                              |
+                                                              
MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                                              0))
+    {
+      size_t streambuf_read_len = 0;
+      char*payload = NULL;
+      size_t payload_len = 0;
+      int ret = 0;
+
+      /* Regular test: text frame fragment (caller wants fragment, 1st call) */
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x83\x00\x00\x00\x00"
+                                  "Hel\x80\x82\x00\x00\x00\x00" "lo",
+                                  17,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) ||
+          (3 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("Hel", payload, 3 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: text frame fragment (caller wants fragment, 2nd call) */
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x83\x00\x00\x00\x00"
+                                  "Hel\x80\x82\x00\x00\x00\x00" "lo"
+                                  + streambuf_read_len,
+                                  17 - streambuf_read_len,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT != ret) ||
+          (2 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("lo", payload, 2 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: text frame fragment with broken UTF-8 sequence (caller 
wants fragment, 1st call) */
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x83\x00\x00\x00\x00"
+                                  "He\xC3\x80\x82\x00\x00\x00\x00" "\xB6o",
+                                  17,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) ||
+          (2 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("He", payload, 2 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+
+      /* Regular test: text frame fragment with broken UTF-8 sequence (caller 
wants fragment, 2nd call) */
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x83\x00\x00\x00\x00"
+                                  "He\xC3\x80\x82\x00\x00\x00\x00" "\xB6o"
+                                  + streambuf_read_len,
+                                  17 - streambuf_read_len,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT != ret) ||
+          (3 != payload_len) ||
+          (NULL == payload) ||
+          (0 != memcmp ("\xC3\xB6o", payload, 3 + 1)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+        payload = NULL;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Individual decode test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+
+  /*
+  
------------------------------------------------------------------------------
+    invalid parameters
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*ws;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws,
+                                                              
MHD_WEBSOCKET_FLAG_SERVER
+                                                              |
+                                                              
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                              0))
+    {
+      size_t streambuf_read_len = 0;
+      char*payload = NULL;
+      size_t payload_len = 0;
+      int ret = 0;
+
+      /* Failure test: `ws` is NULL */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (NULL,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      /* Failure test: `buf` is NULL, while `buf_len` != 0 */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  NULL,
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      /* Failure test: `streambuf_read_len` is NULL */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  NULL,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      /* Failure test: `payload` is NULL */
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  NULL,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+          (0 != payload_len) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      /* Failure test: `payload_len` is NULL */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  NULL);
+      if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+          (NULL != payload) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      /* Regular test: `buf` is NULL and `buf_len` is 0 */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  NULL,
+                                  0,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      /* Regular test: `buf` is not NULL and `buf_len` is 0 */
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  0,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (0 != streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+
+      MHD_websocket_stream_free (ws);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Parameter decode tests failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*ws;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      size_t streambuf_read_len = 0;
+      char*payload = NULL;
+      size_t payload_len = 0;
+      int ret = 0;
+
+      /* Failure test: No memory allocation at the start */
+      disable_alloc = 1;
+      payload = (char *) (uintptr_t) 0xBAADF00D;
+      payload_len = 0x87654321;
+      streambuf_read_len = 1000;
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (NULL != payload) ||
+          (0 != payload_len) ||
+          (1000 == streambuf_read_len) ||
+          (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws)))
+      {
+        fprintf (stderr,
+                 "Decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+      {
+        payload = NULL;
+      }
+      if (NULL != payload)
+      {
+        MHD_websocket_free (ws, payload);
+      }
+      MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                                 
MHD_WEBSOCKET_FLAG_SERVER
+                                                                 |
+                                                                 
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                                 0,
+                                                                 test_malloc,
+                                                                 test_realloc,
+                                                                 test_free))
+      {
+        /* Failure test: No memory allocation after fragmented frame */
+        disable_alloc = 0;
+        payload = (char *) (uintptr_t) 0xBAADF00D;
+        payload_len = 0x87654321;
+        streambuf_read_len = 1000;
+        ret = MHD_websocket_decode (ws,
+                                    "\x01\x83\x00\x00\x00\x00" "Hel",
+                                    9,
+                                    &streambuf_read_len,
+                                    &payload,
+                                    &payload_len);
+        if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+            (NULL != payload) ||
+            (0 != payload_len) ||
+            (9 != streambuf_read_len) ||
+            (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (
+               ws)))
+        {
+          fprintf (stderr,
+                   "Decode test failed in line %u\n",
+                   (unsigned int) __LINE__);
+          ++failed;
+        }
+        if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+        {
+          payload = NULL;
+        }
+        if (NULL != payload)
+        {
+          MHD_websocket_free (ws, payload);
+        }
+        disable_alloc = 1;
+        payload = (char *) (uintptr_t) 0xBAADF00D;
+        payload_len = 0x87654321;
+        streambuf_read_len = 1000;
+        ret = MHD_websocket_decode (ws,
+                                    "\x80\x82\x00\x00\x00\x00" "lo",
+                                    8,
+                                    &streambuf_read_len,
+                                    &payload,
+                                    &payload_len);
+        if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+            (NULL != payload) ||
+            (0 != payload_len) ||
+            (1000 == streambuf_read_len) ||
+            (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (
+               ws)))
+        {
+          fprintf (stderr,
+                   "Decode test failed in line %u\n",
+                   (unsigned int) __LINE__);
+          ++failed;
+        }
+        if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+        {
+          payload = NULL;
+        }
+        if (NULL != payload)
+        {
+          MHD_websocket_free (ws, payload);
+        }
+        /* Regular test: Success after memory allocation ok again */
+        /* (streambuf_read_len may not be overwritten for this test) */
+        disable_alloc = 0;
+        payload = (char *) (uintptr_t) 0xBAADF00D;
+        payload_len = 0x87654321;
+        size_t old_streambuf_read_len = streambuf_read_len;
+        ret = MHD_websocket_decode (ws,
+                                    "\x80\x82\x00\x00\x00\x00lo"
+                                    + old_streambuf_read_len,
+                                    8 - old_streambuf_read_len,
+                                    &streambuf_read_len,
+                                    &payload,
+                                    &payload_len);
+        if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) ||
+            (NULL == payload) ||
+            (5 != payload_len) ||
+            (8 != streambuf_read_len + old_streambuf_read_len) ||
+            (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (
+               ws)) ||
+            (0 != memcmp ("Hello", payload, 5)))
+        {
+          fprintf (stderr,
+                   "Decode test failed in line %u\n",
+                   (unsigned int) __LINE__);
+          ++failed;
+        }
+        if (((char *) (uintptr_t) 0xBAADF00D) == payload)
+        {
+          payload = NULL;
+        }
+        if (NULL != payload)
+        {
+          MHD_websocket_free (ws, payload);
+        }
+
+        MHD_websocket_stream_free (ws);
+      }
+      else
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory decode tests failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    memory leak test, when freeing while decoding
+  
------------------------------------------------------------------------------
+  */
+  {
+    disable_alloc = 0;
+    struct MHD_WebSocketStream*ws;
+    size_t streambuf_read_len = 0;
+    char*payload = NULL;
+    size_t payload_len = 0;
+    int ret = 0;
+
+    /* Regular test: Free while decoding of data frame */
+    open_allocs = 0;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      ret = MHD_websocket_decode (ws,
+                                  "\x81\x85\x00\x00\x00\x00Hel",
+                                  9,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (9 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (0 != open_allocs)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u (memory leak 
detected)\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+
+    /* Regular test: Free while decoding of control frame */
+    open_allocs = 0;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      ret = MHD_websocket_decode (ws,
+                                  "\x88\x85\x00\x00\x00\x00Hel",
+                                  9,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (9 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (0 != open_allocs)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u (memory leak 
detected)\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+
+    /* Regular test: Free while decoding of fragmented data frame */
+    open_allocs = 0;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (11 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (0 != open_allocs)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u (memory leak 
detected)\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+    /* Regular test: Free while decoding of continued fragmented data frame */
+    open_allocs = 0;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (11 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_decode (ws,
+                                  "\x80\x85\x00\x00\x00\x00Hel",
+                                  9,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (9 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (0 != open_allocs)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u (memory leak 
detected)\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+    /* Regular test: Free while decoding of control frame during fragmented 
data frame */
+    open_allocs = 0;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER
+                                                               |
+                                                               
MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      ret = MHD_websocket_decode (ws,
+                                  "\x01\x85\x00\x00\x00\x00Hello",
+                                  11,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (11 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_decode (ws,
+                                  "\x88\x85\x00\x00\x00\x00Hel",
+                                  9,
+                                  &streambuf_read_len,
+                                  &payload,
+                                  &payload_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (0 != payload_len) ||
+          (NULL != payload) ||
+          (9 != streambuf_read_len) )
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      ret = MHD_websocket_stream_free (ws);
+      if (MHD_WEBSOCKET_STATUS_OK != ret)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (0 != open_allocs)
+      {
+        fprintf (stderr,
+                 "Memory decode test failed in line %u (memory leak 
detected)\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+    }
+    else
+    {
+      fprintf (stderr,
+               "Memory test failed in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+  {
+    free (buf1);
+    buf1 = NULL;
+  }
+  if (NULL != buf2)
+  {
+    free (buf2);
+    buf2 = NULL;
+  }
+  return failed != 0 ? 0x04 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_encode_text()`
+ */
+int
+test_encodes_text ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*wss;
+  struct MHD_WebSocketStream*wsc;
+  int ret;
+  char *buf1 = NULL, *buf2 = NULL;
+  char*frame = NULL;
+  size_t frame_len = 0;
+  int utf8_step = 0;
+
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
+                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode text tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    return 0x08;
+  }
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss,
+                                                            
MHD_WEBSOCKET_FLAG_SERVER,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode text tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    if (NULL != wsc)
+      MHD_websocket_stream_free (wsc);
+    return 0x08;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Encoding
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data without UTF-8, we are server */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data without UTF-8, we are client */
+  ret = MHD_websocket_encode_text (wsc,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (15 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "blablabla",
+                                  9,
+                                  MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data with UTF-8, we are server */
+  ret = MHD_websocket_encode_text (wss,
+                                   "bla" "\xC3\xA4" "blabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x0B" "bla" "\xC3\xA4" "blabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data with UTF-8, we are client */
+  ret = MHD_websocket_encode_text (wsc,
+                                   "bla" "\xC3\xA4" "blabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (17 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "bla" "\xC3\xA4" "blabla",
+                                  11,
+                                  MHD_WEBSOCKET_STATUS_TEXT_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Some data with NUL characters, we are server */
+  ret = MHD_websocket_encode_text (wss,
+                                   "bla" "\0\0\0" "bla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x09" "bla" "\0\0\0" "bla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: Some data with broken UTF-8, we are server */
+  ret = MHD_websocket_encode_text (wss,
+                                   "bla" "\xC3" "blabla",
+                                   10,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Fragmentation
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data without UTF-8 */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x81\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: First fragment without UTF-8 */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FIRST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x01\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Middle fragment without UTF-8 */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x00\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment without UTF-8 */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabla",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): First fragment with UTF-8 on the edge */
+  ret = MHD_websocket_encode_text (wss,
+                                   "blablabl\xC3",
+                                   9,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FIRST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 != utf8_step) ||
+      (0 != memcmp (frame, "\x01\x09" "blablabl\xC3", 11)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Last fragment with UTF-8 on the edge */
+  ret = MHD_websocket_encode_text (wss,
+                                   "\xA4" "blablabla",
+                                   10,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (12 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0A" "\xA4" "blablabla", 12)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: Last fragment with UTF-8 on the edge (here with wrong old 
utf8_step) */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\xA4" "blablabla",
+                                   10,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF2TAIL_1OF1 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\xA4" "blablabla",
+                                   10,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (12 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0A" "\xA4" "blablabla", 12)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL1_1OF2 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\xA0\x80" "blablabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0B" "\xA0\x80" "blablabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL2_1OF2 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80\x80" "blablabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0B" "\x80\x80" "blablabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL_1OF2 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80\x80" "blablabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0B" "\x80\x80" "blablabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL_2OF2 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80" " blablabla",
+                                   11,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0B" "\x80" " blablabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL1_1OF3 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x90\x80\x80" "blablabla",
+                                   12,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (14 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0C" "\x90\x80\x80" "blablabla", 14)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL2_1OF3 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80\x80\x80" "blablabla",
+                                   12,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (14 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0C" "\x80\x80\x80" "blablabla", 14)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_1OF3 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80\x80\x80" "blablabla",
+                                   12,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (14 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0C" "\x80\x80\x80" "blablabla", 14)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_2OF3 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80\x80" " blablabla",
+                                   12,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (14 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0C" "\x80\x80" " blablabla", 14)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_3OF3 */
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3;
+  ret = MHD_websocket_encode_text (wss,
+                                   "\x80" "  blablabla",
+                                   12,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (14 != frame_len) ||
+      (NULL == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x0C" "\x80" "  blablabla", 14)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Length checks
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): Text frame without data */
+  ret = MHD_websocket_encode_text (wss,
+                                   NULL,
+                                   0,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Text frame with 1 byte of data */
+  ret = MHD_websocket_encode_text (wss,
+                                   "a",
+                                   1,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (3 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x01" "a", 3)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Text frame with 125 bytes of data */
+  ret = MHD_websocket_encode_text (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                   125,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (127 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x7D"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                    127)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Text frame with 126 bytes of data */
+  ret = MHD_websocket_encode_text (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                   126,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (130 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x81\x7E\x00\x7E"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                    130)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Text frame with 65535 bytes of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65535,
+                             "\x81\x7E\xFF\xFF",
+                             4);
+  ret = MHD_websocket_encode_text (wss,
+                                   buf2,
+                                   65535,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (65535 + 4 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 65535 + 4)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Text frame with 65536 bytes of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65536,
+                             "\x81\x7F\x00\x00\x00\x00\x00\x01\x00\x00",
+                             10);
+  ret = MHD_websocket_encode_text (wss,
+                                   buf2,
+                                   65536,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (65536 + 10 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 65536 + 10)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Text frame with 100 MB of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             104857600,
+                             "\x81\x7F\x00\x00\x00\x00\x06\x40\x00\x00",
+                             10);
+  ret = MHD_websocket_encode_text (wss,
+                                   buf2,
+                                   104857600,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (104857600 + 10 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 104857600 + 10)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  if (NULL != buf1)
+  {
+    free (buf1);
+    buf1 = NULL;
+  }
+  if (NULL != buf2)
+  {
+    free (buf2);
+    buf2 = NULL;
+  }
+  /* Fail test: frame_len is greater than 0x7FFFFFFFFFFFFFFF
+     (this is the maximum allowed payload size) */
+  frame_len = 0;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   (uint64_t) 0x8000000000000000,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `ws` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (NULL,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `payload_utf8` not passed, but `payload_utf8_len` != 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   NULL,
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `payload_utf8` passed, but `payload_utf8_len` == 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   0,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x81\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `frame` not passed */
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   NULL,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: `frame_len` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   NULL,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `utf8_step` passed for non-fragmentation
+     (is allowed and `utf8_step` will be filled then) */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = -99;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (5 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x81\x03" "abc", 5)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `utf8_step` passed for non-fragmentation with invalid UTF-8
+     (is allowed and `utf8_step` will be filled then) */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = -99;
+  ret = MHD_websocket_encode_text (wss,
+                                   "ab\xC3",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 != utf8_step) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `utf8_step` not passed for fragmentation #1 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FIRST,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `utf8_step` not passed for fragmentation #2 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `utf8_step` not passed for fragmentation #3 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `utf8_step` passed for fragmentation #1 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = -99;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FIRST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (5 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x01\x03" "abc", 5)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `utf8_step` passed for fragmentation #2 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (5 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x00\x03" "abc", 5)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `utf8_step` passed for fragmentation #3 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (5 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ||
+      (0 != memcmp (frame, "\x80\x03" "abc", 5)))
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `fragmentation` has an invalid value */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  utf8_step = -99;
+  ret = MHD_websocket_encode_text (wss,
+                                   "abc",
+                                   3,
+                                   MHD_WEBSOCKET_FRAGMENTATION_LAST + 1,
+                                   &frame,
+                                   &frame_len,
+                                   &utf8_step);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) ||
+      (-99 != utf8_step) )
+  {
+    fprintf (stderr,
+             "Encode text test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*wsx;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      /* Fail test: allocation while no memory available */
+      disable_alloc = 1;
+      ret = MHD_websocket_encode_text (wsx,
+                                       "abc",
+                                       3,
+                                       MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                       &frame,
+                                       &frame_len,
+                                       NULL);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (0 != frame_len) ||
+          (NULL != frame) )
+      {
+        fprintf (stderr,
+                 "Encode text test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+      /* Regular test: allocation while memory is available again */
+      disable_alloc = 0;
+      ret = MHD_websocket_encode_text (wsx,
+                                       "abc",
+                                       3,
+                                       MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                       &frame,
+                                       &frame_len,
+                                       NULL);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (5 != frame_len) ||
+          (NULL == frame) ||
+          (0 != memcmp (frame, "\x81\x03" "abc", 5)))
+      {
+        fprintf (stderr,
+                 "Encode text test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+
+      MHD_websocket_stream_free (wsx);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Couldn't perform memory test for text encoding in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+    free (buf1);
+  if (NULL != buf2)
+    free (buf2);
+  if (NULL != wsc)
+    MHD_websocket_stream_free (wsc);
+  if (NULL != wss)
+    MHD_websocket_stream_free (wss);
+
+  return failed != 0 ? 0x08 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_encode_binary()`
+ */
+int
+test_encodes_binary ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*wss;
+  struct MHD_WebSocketStream*wsc;
+  int ret;
+  char *buf1 = NULL, *buf2 = NULL;
+  char*frame = NULL;
+  size_t frame_len = 0;
+
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
+                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode binary tests possible due to failed stream init in 
line %u\n",
+             (unsigned int) __LINE__);
+    return 0x10;
+  }
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss,
+                                                            
MHD_WEBSOCKET_FLAG_SERVER,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode binary tests possible due to failed stream init in 
line %u\n",
+             (unsigned int) __LINE__);
+    if (NULL != wsc)
+      MHD_websocket_stream_free (wsc);
+    return 0x10;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Encoding
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data, we are server */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data, we are client */
+  ret = MHD_websocket_encode_binary (wsc,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (15 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "blablabla",
+                                  9,
+                                  MHD_WEBSOCKET_STATUS_BINARY_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Some data with NUL characters, we are server */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "bla" "\0\0\0" "bla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x09" "bla" "\0\0\0" "bla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data which looks like broken UTF-8, we are server */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "bla" "\xC3" "blabla",
+                                     10,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (12 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x0A" "bla" "\xC3" "blabla", 12)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Fragmentation
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: First fragment */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_FIRST,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x02\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Middle fragment */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x00\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Last fragment  */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "blablabla",
+                                     9,
+                                     MHD_WEBSOCKET_FRAGMENTATION_LAST,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x80\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Length checks
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): Binary frame without data */
+  ret = MHD_websocket_encode_binary (wss,
+                                     NULL,
+                                     0,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Binary frame with 1 byte of data */
+  ret = MHD_websocket_encode_binary (wss,
+                                     "a",
+                                     1,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (3 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x01" "a", 3)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Binary frame with 125 bytes of data */
+  ret = MHD_websocket_encode_binary (wss,
+                                     
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                     125,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (127 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x7D"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                    127)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Binary frame with 126 bytes of data */
+  ret = MHD_websocket_encode_binary (wss,
+                                     
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                     126,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (130 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x82\x7E\x00\x7E"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                    130)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Binary frame with 65535 bytes of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65535,
+                             "\x82\x7E\xFF\xFF",
+                             4);
+  ret = MHD_websocket_encode_binary (wss,
+                                     buf2,
+                                     65535,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (65535 + 4 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 65535 + 4)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Binary frame with 65536 bytes of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             65536,
+                             "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00",
+                             10);
+  ret = MHD_websocket_encode_binary (wss,
+                                     buf2,
+                                     65536,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (65536 + 10 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 65536 + 10)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Binary frame with 100 MB of data */
+  allocate_length_test_data (&buf1,
+                             &buf2,
+                             104857600,
+                             "\x82\x7F\x00\x00\x00\x00\x06\x40\x00\x00",
+                             10);
+  ret = MHD_websocket_encode_binary (wss,
+                                     buf2,
+                                     104857600,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (104857600 + 10 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, buf1, 104857600 + 10)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  if (NULL != buf1)
+  {
+    free (buf1);
+    buf1 = NULL;
+  }
+  if (NULL != buf2)
+  {
+    free (buf2);
+    buf2 = NULL;
+  }
+  /* Fail test: `frame_len` is greater than 0x7FFFFFFFFFFFFFFF
+     (this is the maximum allowed payload size) */
+  frame_len = 0;
+  ret = MHD_websocket_encode_binary (wss,
+                                     "abc",
+                                     (uint64_t) 0x8000000000000000,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `ws` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_binary (NULL,
+                                     "abc",
+                                     3,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `payload` not passed, but `payload_len` != 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_binary (wss,
+                                     NULL,
+                                     3,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `payload` passed, but `payload_len` == 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_binary (wss,
+                                     "abc",
+                                     0,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x82\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `frame` not passed */
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_binary (wss,
+                                     "abc",
+                                     3,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     NULL,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: `frame_len` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  ret = MHD_websocket_encode_binary (wss,
+                                     "abc",
+                                     3,
+                                     MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                     &frame,
+                                     NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `fragmentation` has an invalid value */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_binary (wss,
+                                     "abc",
+                                     3,
+                                     MHD_WEBSOCKET_FRAGMENTATION_LAST + 1,
+                                     &frame,
+                                     &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode binary test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*wsx;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      /* Fail test: allocation while no memory available */
+      disable_alloc = 1;
+      ret = MHD_websocket_encode_binary (wsx,
+                                         "abc",
+                                         3,
+                                         MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                         &frame,
+                                         &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (0 != frame_len) ||
+          (NULL != frame) )
+      {
+        fprintf (stderr,
+                 "Encode binary test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+      /* Regular test: allocation while memory is available again */
+      disable_alloc = 0;
+      ret = MHD_websocket_encode_binary (wsx,
+                                         "abc",
+                                         3,
+                                         MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                                         &frame,
+                                         &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (5 != frame_len) ||
+          (NULL == frame) ||
+          (0 != memcmp (frame, "\x82\x03" "abc", 5)))
+      {
+        fprintf (stderr,
+                 "Encode binary test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+
+      MHD_websocket_stream_free (wsx);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Couldn't perform memory test for binary encoding in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+    free (buf1);
+  if (NULL != buf2)
+    free (buf2);
+  if (NULL != wsc)
+    MHD_websocket_stream_free (wsc);
+  if (NULL != wss)
+    MHD_websocket_stream_free (wss);
+
+  return failed != 0 ? 0x10 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_encode_close()`
+ */
+int
+test_encodes_close ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*wss;
+  struct MHD_WebSocketStream*wsc;
+  int ret;
+  char *buf1 = NULL, *buf2 = NULL;
+  char*frame = NULL;
+  size_t frame_len = 0;
+
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
+                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode close tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    return 0x10;
+  }
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss,
+                                                            
MHD_WEBSOCKET_FLAG_SERVER,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode close tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    if (NULL != wsc)
+      MHD_websocket_stream_free (wsc);
+    return 0x10;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Encoding
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data, we are server */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "blablabla",
+                                    9,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x0B\x03\xE8" "blablabla", 13)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data, we are client */
+  ret = MHD_websocket_encode_close (wsc,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "blablabla",
+                                    9,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (17 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "\x03\xE8" "blablabla",
+                                  11,
+                                  MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                  
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Close reason without text, we are server */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (4 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x02\x03\xE8", 4)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Close reason without text, we are client */
+  ret = MHD_websocket_encode_close (wsc,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (8 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "\x03\xE8",
+                                  2,
+                                  MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                  
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Close without reason, we are server */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_NO_REASON,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Close without reason, we are client */
+  ret = MHD_websocket_encode_close (wsc,
+                                    MHD_WEBSOCKET_CLOSEREASON_NO_REASON,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (6 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  NULL,
+                                  0,
+                                  MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                  
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Close with UTF-8 sequence in reason, we are client */
+  ret = MHD_websocket_encode_close (wsc,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "bla" "\xC3\xA4" "blabla",
+                                    11,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (19 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "\x03\xE8" "bla" "\xC3\xA4" "blabla",
+                                  13,
+                                  MHD_WEBSOCKET_STATUS_CLOSE_FRAME,
+                                  
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Close reason with NUL characters, we are server */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY,
+                                    "bla" "\0\0\0" "bla",
+                                    9,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (13 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x0B\x03\xE9" "bla" "\0\0\0" "bla", 13)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: Some data with broken UTF-8, we are server */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "bla" "\xC3" "blabla",
+                                    10,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Length checks
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): Close frame without payload */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_NO_REASON,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Close frame only reason code */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    NULL,
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (4 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x02\x03\xE8", 4)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Close frame with 1 bytes of reason text */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "a",
+                                    1,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (5 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x03\x03\xE8" "a", 5)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Close frame with 123 bytes of reason text */
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456",
+                                    123,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (127 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x88\x7D\x03\xE8"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456",
+                    127)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (fail): Close frame with 124 bytes of reason text*/
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567",
+                                    124,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `ws` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (NULL,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `payload` not passed, but `payload_len` != 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    NULL,
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `payload` passed, but `payload_len` == 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "abc",
+                                    0,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (4 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x88\x02\x03\xE8", 4)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `frame` not passed */
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "abc",
+                                    3,
+                                    NULL,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: `frame_len` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: no reason code passed, but reason text */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    MHD_WEBSOCKET_CLOSEREASON_NO_REASON,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (fail): Invalid reason code */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    1,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (fail): Invalid reason code */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    999,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Custom reason code */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_close (wss,
+                                    2000,
+                                    "abc",
+                                    3,
+                                    &frame,
+                                    &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (7 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x88\x05\x07\xD0" "abc", 7)))
+  {
+    fprintf (stderr,
+             "Encode close test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*wsx;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      /* Fail test: allocation while no memory available */
+      disable_alloc = 1;
+      ret = MHD_websocket_encode_close (wsx,
+                                        MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                        "abc",
+                                        3,
+                                        &frame,
+                                        &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (0 != frame_len) ||
+          (NULL != frame) )
+      {
+        fprintf (stderr,
+                 "Encode close test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+      /* Regular test: allocation while memory is available again */
+      disable_alloc = 0;
+      ret = MHD_websocket_encode_close (wsx,
+                                        MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+                                        "abc",
+                                        3,
+                                        &frame,
+                                        &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (7 != frame_len) ||
+          (NULL == frame) ||
+          (0 != memcmp (frame, "\x88\x05\x03\xE8" "abc", 7)))
+      {
+        fprintf (stderr,
+                 "Encode close test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+
+      MHD_websocket_stream_free (wsx);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Couldn't perform memory test for close encoding in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+    free (buf1);
+  if (NULL != buf2)
+    free (buf2);
+  if (NULL != wsc)
+    MHD_websocket_stream_free (wsc);
+  if (NULL != wss)
+    MHD_websocket_stream_free (wss);
+
+  return failed != 0 ? 0x20 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_encode_ping()`
+ */
+int
+test_encodes_ping ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*wss;
+  struct MHD_WebSocketStream*wsc;
+  int ret;
+  char *buf1 = NULL, *buf2 = NULL;
+  char*frame = NULL;
+  size_t frame_len = 0;
+
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
+                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode ping tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    return 0x10;
+  }
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss,
+                                                            
MHD_WEBSOCKET_FLAG_SERVER,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode ping tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    if (NULL != wsc)
+      MHD_websocket_stream_free (wsc);
+    return 0x10;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Encoding
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data, we are server */
+  ret = MHD_websocket_encode_ping (wss,
+                                   "blablabla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data, we are client */
+  ret = MHD_websocket_encode_ping (wsc,
+                                   "blablabla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (15 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "blablabla",
+                                  9,
+                                  MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Ping without payload, we are server */
+  ret = MHD_websocket_encode_ping (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Ping without payload, we are client */
+  ret = MHD_websocket_encode_ping (wsc,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (6 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  NULL,
+                                  0,
+                                  MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Ping with something like UTF-8 sequence in payload, we are 
client */
+  ret = MHD_websocket_encode_ping (wsc,
+                                   "bla" "\xC3\xA4" "blabla",
+                                   11,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (17 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "bla" "\xC3\xA4" "blabla",
+                                  11,
+                                  MHD_WEBSOCKET_STATUS_PING_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Ping payload with NUL characters, we are server */
+  ret = MHD_websocket_encode_ping (wss,
+                                   "bla" "\0\0\0" "bla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x09" "bla" "\0\0\0" "bla", 11)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Ping payload with with something which looks like broken 
UTF-8, we are server */
+  ret = MHD_websocket_encode_ping (wss,
+                                   "bla" "\xC3" "blabla",
+                                   10,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (12 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x0A" "bla" "\xC3" "blabla", 12)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Length checks
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): Ping frame without payload */
+  ret = MHD_websocket_encode_ping (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Ping frame with one byte of payload */
+  ret = MHD_websocket_encode_ping (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Ping frame with 125 bytes of payload */
+  ret = MHD_websocket_encode_ping (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                   125,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (127 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x89\x7D"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                    127)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (fail): Ping frame with 126 bytes of payload */
+  ret = MHD_websocket_encode_ping (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                   126,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `ws` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_ping (NULL,
+                                   "abc",
+                                   3,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `payload` not passed, but `payload_len` != 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_ping (wss,
+                                   NULL,
+                                   3,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `payload` passed, but `payload_len` == 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_ping (wss,
+                                   "abc",
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x89\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `frame` not passed */
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_ping (wss,
+                                   "abc",
+                                   3,
+                                   NULL,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: `frame_len` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  ret = MHD_websocket_encode_ping (wss,
+                                   "abc",
+                                   3,
+                                   &frame,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode ping test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*wsx;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      /* Fail test: allocation while no memory available */
+      disable_alloc = 1;
+      ret = MHD_websocket_encode_ping (wsx,
+                                       "abc",
+                                       3,
+                                       &frame,
+                                       &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (0 != frame_len) ||
+          (NULL != frame) )
+      {
+        fprintf (stderr,
+                 "Encode ping test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+      /* Regular test: allocation while memory is available again */
+      disable_alloc = 0;
+      ret = MHD_websocket_encode_ping (wsx,
+                                       "abc",
+                                       3,
+                                       &frame,
+                                       &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (5 != frame_len) ||
+          (NULL == frame) ||
+          (0 != memcmp (frame, "\x89\x03" "abc", 5)))
+      {
+        fprintf (stderr,
+                 "Encode ping test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+
+      MHD_websocket_stream_free (wsx);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Couldn't perform memory test for ping encoding in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+    free (buf1);
+  if (NULL != buf2)
+    free (buf2);
+  if (NULL != wsc)
+    MHD_websocket_stream_free (wsc);
+  if (NULL != wss)
+    MHD_websocket_stream_free (wss);
+
+  return failed != 0 ? 0x40 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_encode_pong()`
+ */
+int
+test_encodes_pong ()
+{
+  int failed = 0;
+  struct MHD_WebSocketStream*wss;
+  struct MHD_WebSocketStream*wsc;
+  int ret;
+  char *buf1 = NULL, *buf2 = NULL;
+  char*frame = NULL;
+  size_t frame_len = 0;
+
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
+                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode pong tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    return 0x10;
+  }
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss,
+                                                            
MHD_WEBSOCKET_FLAG_SERVER,
+                                                            0))
+  {
+    fprintf (stderr,
+             "No encode pong tests possible due to failed stream init in line 
%u\n",
+             (unsigned int) __LINE__);
+    if (NULL != wsc)
+      MHD_websocket_stream_free (wsc);
+    return 0x10;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Encoding
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Some data, we are server */
+  ret = MHD_websocket_encode_pong (wss,
+                                   "blablabla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x09" "blablabla", 11)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Some data, we are client */
+  ret = MHD_websocket_encode_pong (wsc,
+                                   "blablabla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (15 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "blablabla",
+                                  9,
+                                  MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Pong without payload, we are server */
+  ret = MHD_websocket_encode_pong (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Pong without payload, we are client */
+  ret = MHD_websocket_encode_pong (wsc,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (6 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  NULL,
+                                  0,
+                                  MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Regular test: Pong with something like UTF-8 sequence in payload, we are 
client */
+  ret = MHD_websocket_encode_pong (wsc,
+                                   "bla" "\xC3\xA4" "blabla",
+                                   11,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (17 != frame_len) ||
+      (NULL == frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  else
+  {
+    failed += test_decode_single (__LINE__,
+                                  MHD_WEBSOCKET_FLAG_SERVER
+                                  | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                  0,
+                                  1,
+                                  0,
+                                  frame,
+                                  frame_len,
+                                  "bla" "\xC3\xA4" "blabla",
+                                  11,
+                                  MHD_WEBSOCKET_STATUS_PONG_FRAME,
+                                  MHD_WEBSOCKET_VALIDITY_VALID,
+                                  frame_len);
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wsc, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Pong payload with NUL characters, we are server */
+  ret = MHD_websocket_encode_pong (wss,
+                                   "bla" "\0\0\0" "bla",
+                                   9,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (11 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x09" "bla" "\0\0\0" "bla", 11)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: Pong payload with with something which looks like broken 
UTF-8, we are server */
+  ret = MHD_websocket_encode_pong (wss,
+                                   "bla" "\xC3" "blabla",
+                                   10,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (12 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x0A" "bla" "\xC3" "blabla", 12)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Length checks
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): Pong frame without payload */
+  ret = MHD_websocket_encode_pong (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Pong frame with one byte of payload */
+  ret = MHD_websocket_encode_pong (wss,
+                                   NULL,
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (success): Pong frame with 125 bytes of payload */
+  ret = MHD_websocket_encode_pong (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                                   125,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (127 != frame_len) ||
+      (NULL == frame) ||
+      (0 != memcmp (frame, "\x8A\x7D"
+                    
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678",
+                    127)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Edge test (fail): Pong frame with 126 bytes of payload */
+  ret = MHD_websocket_encode_pong (wss,
+                                   
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+                                   126,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `ws` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_pong (NULL,
+                                   "abc",
+                                   3,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `payload` not passed, but `payload_len` != 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_pong (wss,
+                                   NULL,
+                                   3,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Regular test: `payload` passed, but `payload_len` == 0 */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_pong (wss,
+                                   "abc",
+                                   0,
+                                   &frame,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (2 != frame_len) ||
+      (NULL == frame) ||
+      (((char*) (uintptr_t) 0xBAADF00D) == frame) ||
+      (0 != memcmp (frame, "\x8A\x00", 2)))
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+  /* Fail test: `frame` not passed */
+  frame_len = 0x87654321;
+  ret = MHD_websocket_encode_pong (wss,
+                                   "abc",
+                                   3,
+                                   NULL,
+                                   &frame_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (0 != frame_len) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: `frame_len` not passed */
+  frame = (char*) (uintptr_t) 0xBAADF00D;
+  ret = MHD_websocket_encode_pong (wss,
+                                   "abc",
+                                   3,
+                                   &frame,
+                                   NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != frame) )
+  {
+    fprintf (stderr,
+             "Encode pong test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  if (((char*) (uintptr_t) 0xBAADF00D) == frame)
+  {
+    frame = NULL;
+  }
+  if (NULL != frame)
+  {
+    MHD_websocket_free (wss, frame);
+    frame = NULL;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    validity after temporary out-of-memory
+  
------------------------------------------------------------------------------
+  */
+  {
+    struct MHD_WebSocketStream*wsx;
+    if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx,
+                                                               
MHD_WEBSOCKET_FLAG_SERVER,
+                                                               0,
+                                                               test_malloc,
+                                                               test_realloc,
+                                                               test_free))
+    {
+      /* Fail test: allocation while no memory available */
+      disable_alloc = 1;
+      ret = MHD_websocket_encode_pong (wsx,
+                                       "abc",
+                                       3,
+                                       &frame,
+                                       &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) ||
+          (0 != frame_len) ||
+          (NULL != frame) )
+      {
+        fprintf (stderr,
+                 "Encode pong test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+      /* Regular test: allocation while memory is available again */
+      disable_alloc = 0;
+      ret = MHD_websocket_encode_pong (wsx,
+                                       "abc",
+                                       3,
+                                       &frame,
+                                       &frame_len);
+      if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+          (5 != frame_len) ||
+          (NULL == frame) ||
+          (0 != memcmp (frame, "\x8A\x03" "abc", 5)))
+      {
+        fprintf (stderr,
+                 "Encode pong test failed in line %u\n",
+                 (unsigned int) __LINE__);
+        ++failed;
+      }
+      if (NULL != frame)
+      {
+        MHD_websocket_free (wsx, frame);
+        frame = NULL;
+      }
+
+      MHD_websocket_stream_free (wsx);
+    }
+    else
+    {
+      fprintf (stderr,
+               "Couldn't perform memory test for pong encoding in line %u\n",
+               (unsigned int) __LINE__);
+      ++failed;
+    }
+  }
+
+  if (NULL != buf1)
+    free (buf1);
+  if (NULL != buf2)
+    free (buf2);
+  if (NULL != wsc)
+    MHD_websocket_stream_free (wsc);
+  if (NULL != wss)
+    MHD_websocket_stream_free (wss);
+
+  return failed != 0 ? 0x80 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_split_close_reason()`
+ */
+int
+test_split_close_reason ()
+{
+  int failed = 0;
+  const char*payload;
+  unsigned short reason_code;
+  const char*reason_utf8;
+  size_t reason_utf8_len;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    Normal splits
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Reason code + Reason text */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = "\x03\xE8" "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          5,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) ||
+      (3 != reason_utf8_len) ||
+      (payload + 2 != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: Reason code */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = "\x03\xE8";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          2,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) ||
+      (0 != reason_utf8_len) ||
+      (NULL != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: No payload */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = NULL;
+  ret = MHD_websocket_split_close_reason (payload,
+                                          0,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) ||
+      (0 != reason_utf8_len) ||
+      (NULL != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: `payload` is not NULL given, but `payload_len` == 0 */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          0,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) ||
+      (0 != reason_utf8_len) ||
+      (NULL != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Wrong parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: `payload` not passed, but `payload_len` != 0 */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = NULL;
+  ret = MHD_websocket_split_close_reason (payload,
+                                          3,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) ||
+      (0 != reason_utf8_len) ||
+      (NULL != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: `reason_code` not passed */
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  reason_utf8_len = 12345;
+  payload = "\x03\xE8" "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          5,
+                                          NULL,
+                                          &reason_utf8,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (3 != reason_utf8_len) ||
+      (payload + 2 != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: `reason_utf8` not passed */
+  reason_code = 9999;
+  reason_utf8_len = 12345;
+  payload = "\x03\xE8" "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          5,
+                                          &reason_code,
+                                          NULL,
+                                          &reason_utf8_len);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) ||
+      (3 != reason_utf8_len) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: `reason_utf8_len` not passed */
+  reason_code = 9999;
+  reason_utf8 = (const char*) (intptr_t) 0xBAADF00D;
+  payload = "\x03\xE8" "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          5,
+                                          &reason_code,
+                                          &reason_utf8,
+                                          NULL);
+  if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+      (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) ||
+      (payload + 2 != reason_utf8) )
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: `reason_code`, `reason_utf8` and `reason_utf8_len` not 
passed */
+  /* (this is not prohibited, although it doesn't really make sense) */
+  payload = "\x03\xE8" "abc";
+  ret = MHD_websocket_split_close_reason (payload,
+                                          5,
+                                          NULL,
+                                          NULL,
+                                          NULL);
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "split close reason test failed in line %u\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x100 : 0x00;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  (void) argc; (void) argv;  /* Unused. Silent compiler warning. */
+
+  /* seed random number generator */
+  MHD_websocket_srand ((unsigned long) time (NULL));
+
+  /* perform tests */
+  errorCount += test_inits ();
+  errorCount += test_accept ();
+  errorCount += test_decodes ();
+  errorCount += test_encodes_text ();
+  errorCount += test_encodes_binary ();
+  errorCount += test_encodes_close ();
+  errorCount += test_encodes_ping ();
+  errorCount += test_encodes_pong ();
+  errorCount += test_split_close_reason ();
+
+  /* output result */
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+
+  return errorCount != 0;       /* 0 == pass */
+}

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



reply via email to

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