gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] 02/05: websocket update


From: gnunet
Subject: [libmicrohttpd] 02/05: websocket update
Date: Sun, 31 Oct 2021 14:37:32 +0100

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

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit ddad59c1e3a5d20b19003b1d47673501b0665b36
Author: David Gausmann <David.Gausmann@measX.com>
AuthorDate: Sun Oct 17 19:53:09 2021 +0200

    websocket update
    
    - added API documentation to libmicrohttpd.texi
    - added websocket tutorial chapter to libmicrohttpd-tutorial and an much 
easier example for the tutorial
    - added additional helper functions to ease the HTTP websocket handshake
    - the code can now be compiled on Linux without errors
    - changed sha1.c and sha1.h to the files provided by Evgeny (I replaced 
those files in src/microhttpd_ws/ with the files from src/microhttpd/ - maybe 
there is a smarter way...?)
    - removed dependency for "htons" and "htonl" (these functions are now 
implemented in MHD_websocket.c; no need for OS-dependent files anymore)
    - added an additional test script for testing of the library with any 
webbrowser (for manual practice test)
    - several bugfixes
    - parameters renamed
    - special things clarified (fragmentation, RNG for client mode)
    
    The new version of the API is at some points incompatible with the old 
version, but since it was in an experimental phase and it didn't compile on 
Linux, I guess this shouldn't bother anyone.
    From my point of view, I am now finished with the library and it could go 
out of experimental.
---
 doc/chapters/bibliography.inc               |    3 +
 doc/chapters/websocket.inc                  |  886 ++++++++++++++++++
 doc/examples/websocket.c                    |  446 ++++++++++
 doc/libmicrohttpd-tutorial.texi             |   12 +
 doc/libmicrohttpd.texi                      | 1281 +++++++++++++++++++++++++++
 src/examples/websocket_chatserver_example.c | 1051 +++++++++++-----------
 src/include/microhttpd_ws.h                 |  534 ++++++-----
 src/microhttpd_ws/mhd_websocket.c           |  587 +++++++++---
 src/microhttpd_ws/sha1.c                    |  720 +++++++--------
 src/microhttpd_ws/sha1.h                    |  245 +++--
 src/microhttpd_ws/test_websocket.c          | 1247 ++++++++++++++++++++++++--
 src/microhttpd_ws/test_websocket_browser.c  |  563 ++++++++++++
 12 files changed, 6141 insertions(+), 1434 deletions(-)

diff --git a/doc/chapters/bibliography.inc b/doc/chapters/bibliography.inc
index cc288bc0..bdaa6187 100644
--- a/doc/chapters/bibliography.inc
+++ b/doc/chapters/bibliography.inc
@@ -16,6 +16,9 @@ All referenced RFCs can be found on the website of @emph{The 
Internet Engineerin
 @emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., 
Leach, P.,
 Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access 
Authentication", RFC 2617, June 1999.
 
+@item
+@emph{RFC 6455}: Fette, I., Melnikov, A., "The WebSocket Protocol", RFC 6455, 
December 2011.
+
 
 @item 
 A well--structured @emph{HTML} reference can be found on
diff --git a/doc/chapters/websocket.inc b/doc/chapters/websocket.inc
new file mode 100644
index 00000000..a480fd13
--- /dev/null
+++ b/doc/chapters/websocket.inc
@@ -0,0 +1,886 @@
+Websockets are a genuine way to implement push notifications,
+where the server initiates the communication while the client can be idle.
+Usually a HTTP communication is half-duplex and always requested by the client,
+but websockets are full-duplex and only initialized by the client.
+In the further communication both sites can use the websocket at any time
+to send data to the other site.
+
+To initialize a websocket connection the client sends a special HTTP request
+to the server and initializes
+a handshake between client and server which switches from the HTTP protocol
+to the websocket protocol.
+Thus both the server as well as the client must support websockets.
+If proxys are used, they must support websockets too.
+In this chapter we take a look on server and client, but with a focus on
+the server with @emph{libmicrohttpd}.
+
+Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
+which is required for switching from the HTTP protocol.
+Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
+to support the websocket protocol.
+
+@heading Upgrading connections with libmicrohttpd
+
+To support websockets we need to enable upgrading of HTTP connections first.
+This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
+@code{MHD_start_daemon()}.
+
+
+@verbatim
+daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
+                           MHD_USE_THREAD_PER_CONNECTION |
+                           MHD_ALLOW_UPGRADE |
+                           MHD_USE_ERROR_LOG,
+                           PORT, NULL, NULL,
+                           &access_handler, NULL,
+                           MHD_OPTION_END);
+@end verbatim
+@noindent
+
+
+The next step is to turn a specific request into an upgraded connection.
+This done in our @code{access_handler} by calling
+@code{MHD_create_response_for_upgrade()}.
+An @code{upgrade_handler} will be passed to perform the low-level actions
+on the socket.
+
+@emph{Please note that the socket here is just a regular socket as provided
+by the operating system.
+To use it as a websocket, some more steps from the following
+chapters are required.}
+
+
+@verbatim
+static enum MHD_Result
+access_handler (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr)
+{
+  /* ... */
+  /* some code to decide whether to upgrade or not */
+  /* ... */
+
+  /* create the response for upgrade */
+  response = MHD_create_response_for_upgrade (&upgrade_handler,
+                                              NULL);
+
+  /* ... */
+  /* additional headers, etc. */
+  /* ... */
+
+  ret = MHD_queue_response (connection,
+                            MHD_HTTP_SWITCHING_PROTOCOLS,
+                            response);
+  MHD_destroy_response (response);
+
+  return ret;
+}
+@end verbatim
+@noindent
+
+
+In the @code{upgrade_handler} we receive the low-level socket,
+which is used for the communication with the specific client.
+In addition to the low-level socket we get:
+@itemize @bullet
+@item
+Some data, which has been read too much while @emph{libmicrohttpd} was
+switching the protocols.
+This value is usually empty, because it would mean that the client
+has sent data before the handshake was complete.
+
+@item
+A @code{struct MHD_UpgradeResponseHandle} which is used to perform
+special actions like closing, corking or uncorking the socket.
+These commands are executed by passing the handle
+to @code{MHD_upgrade_action()}.
+
+
+@end itemize
+
+Depending of the flags specified while calling @code{MHD_start_deamon()}
+our @code{upgrade_handler} is either executed in the same thread
+as our deamon or in a thread specific for each connection.
+If it is executed in the same thread then @code{upgrade_handler} is
+a blocking call for our webserver and
+we should finish it as fast as possible (i. e. by creating a thread and
+passing the information there).
+If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
+@code{MHD_start_daemon()} then a separate thread is used and
+thus our @code{upgrade_handler} needs not to start a separate thread.
+
+An @code{upgrade_handler}, which is called with a separate thread
+per connection, could look like this:
+
+
+@verbatim
+static void
+upgrade_handler (void *cls,
+                 struct MHD_Connection *connection,
+                 void *con_cls,
+                 const char *extra_in,
+                 size_t extra_in_size,
+                 MHD_socket fd,
+                 struct MHD_UpgradeResponseHandle *urh)
+{
+  /* ... */
+  /* do something with the socket `fd` like `recv()` or `send()` */
+  /* ... */
+
+  /* close the socket when it is not needed anymore */
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CLOSE);
+}
+@end verbatim
+@noindent
+
+
+This is all you need to know for upgrading connections
+with @emph{libmicrohttpd}.
+The next chapters focus on using the websocket protocol
+with @emph{libmicrohttpd_ws}.
+
+
+@heading Websocket handshake with libmicrohttpd_ws
+
+To request a websocket connection the client must send
+the following information with the HTTP request:
+
+@itemize @bullet
+@item
+A @code{GET} request must be sent.
+
+@item
+The version of the HTTP protocol must be 1.1 or higher.
+
+@item
+A @code{Host} header field must be sent
+
+@item
+A @code{Upgrade} header field containing the keyword "websocket"
+(case-insensitive).
+Please note that the client could pass multiple protocols separated by comma.
+
+@item
+A @code{Connection} header field that includes the token "Upgrade"
+(case-insensitive).
+Please note that the client could pass multiple tokens separated by comma.
+
+@item
+A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
+The decoded the value is 16 bytes long
+and has been generated randomly by the client.
+
+@item
+A @code{Sec-WebSocket-Version} header field with the value "13".
+
+@end itemize
+
+
+Optionally the client can also send the following information:
+
+
+@itemize @bullet
+@item
+A @code{Origin} header field can be used to determine the source
+of the client (i. e. the website).
+
+@item
+A @code{Sec-WebSocket-Protocol} header field can contain a list
+of supported protocols by the client, which can be sent over the websocket.
+
+@item
+A @code{Sec-WebSocket-Extensions} header field which may contain extensions
+to the websocket protocol. The extensions must be registered by IANA.
+
+@end itemize
+
+
+A valid example request from the client could look like this:
+
+
+@verbatim
+GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+@end verbatim
+@noindent
+
+
+To complete the handshake the server must respond with
+some specific response headers:
+
+@itemize @bullet
+@item
+The HTTP response code @code{101 Switching Protocols} must be answered.
+
+@item
+An @code{Upgrade} header field containing the value "websocket" must be sent.
+
+@item
+A @code{Connection} header field containing the value "Upgrade" must be sent.
+
+@item
+A @code{Sec-WebSocket-Accept} header field containing a value, which
+has been calculated from the @code{Sec-WebSocket-Key} request header field,
+must be sent.
+
+@end itemize
+
+
+Optionally the server may send following headers:
+
+
+@itemize @bullet
+@item
+A @code{Sec-WebSocket-Protocol} header field containing a protocol
+of the list specified in the corresponding request header field.
+
+@item
+A @code{Sec-WebSocket-Extension} header field containing all used extensions
+of the list specified in the corresponding request header field.
+
+@end itemize
+
+
+A valid websocket HTTP response could look like this:
+
+@verbatim
+HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+@end verbatim
+@noindent
+
+
+To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
+some helper functions for the @code{access_handler} callback function:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_check_http_version()} checks whether the HTTP version
+is 1.1 or above.
+
+@item
+@code{MHD_websocket_check_connection_header()} checks whether the value
+of the @code{Connection} request header field contains
+an "Upgrade" token (case-insensitive).
+
+@item
+@code{MHD_websocket_check_upgrade_header()} checks whether the value
+of the @code{Upgrade} request header field contains
+the "websocket" keyword (case-insensitive).
+
+@item
+@code{MHD_websocket_check_version_header()} checks whether the value
+of the @code{Sec-WebSocket-Version} request header field is "13".
+
+@item
+@code{MHD_websocket_create_accept_header()} takes the value from
+the @code{Sec-WebSocket-Key} request header and calculates the value
+for the @code{Sec-WebSocket-Accept} response header field.
+
+@end itemize
+
+
+The @code{access_handler} example of the previous chapter can now be
+extended with these helper functions to perform the websocket handshake:
+
+@verbatim
+static enum MHD_Result
+access_handler (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  (void) cls;               /* Unused. Silent compiler warning. */
+  (void) upload_data;       /* Unused. Silent compiler warning. */
+  (void) upload_data_size;  /* Unused. Silent compiler warning. */
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+  {
+    /* do never respond on first call */
+    *ptr = &aptr;
+    return MHD_YES;
+  }
+  *ptr = NULL;                  /* reset when done */
+
+  if (0 == strcmp (url, "/"))
+  {
+    /* Default page for visiting the server */
+    struct MHD_Response *response = MHD_create_response_from_buffer (
+                                      strlen (PAGE),
+                                      PAGE,
+                                      MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_OK,
+                              response);
+    MHD_destroy_response (response);
+  }
+  else if (0 == strcmp (url, "/chat"))
+  {
+    char is_valid = 1;
+    const char* value = NULL;
+    char sec_websocket_accept[29];
+
+    if (0 != MHD_websocket_check_http_version (version))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_CONNECTION);
+    if (0 != MHD_websocket_check_connection_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_UPGRADE);
+    if (0 != MHD_websocket_check_upgrade_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         
MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+    if (0 != MHD_websocket_check_version_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+    {
+      is_valid = 0;
+    }
+
+    if (1 == is_valid)
+    {
+      /* upgrade the connection */
+      response = MHD_create_response_for_upgrade (&upgrade_handler,
+                                                  NULL);
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_CONNECTION,
+                               "Upgrade");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_UPGRADE,
+                               "websocket");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+                               sec_websocket_accept);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_SWITCHING_PROTOCOLS,
+                                response);
+      MHD_destroy_response (response);
+    }
+    else
+    {
+      /* return error page */
+      struct MHD_Response*response = MHD_create_response_from_buffer (
+                                       strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+                                       PAGE_INVALID_WEBSOCKET_REQUEST,
+                                       MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_BAD_REQUEST,
+                                response);
+      MHD_destroy_response (response);
+    }
+  }
+  else
+  {
+    struct MHD_Response*response = MHD_create_response_from_buffer (
+                                     strlen (PAGE_NOT_FOUND),
+                                     PAGE_NOT_FOUND,
+                                     MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_NOT_FOUND,
+                              response);
+    MHD_destroy_response (response);
+  }
+
+  return ret;
+}
+@end verbatim
+@noindent
+
+Please note that we skipped the check of the Host header field here,
+because we don't know the host for this example.
+
+@heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
+
+Once the websocket connection is established you can receive/send frame data
+with the low-level socket functions @code{recv()} and @code{send()}.
+The frame data which goes over the low-level socket is encoded according
+to the websocket protocol.
+To use received payload data, you need to decode the frame data first.
+To send payload data, you need to encode it into frame data first.
+
+@emph{libmicrohttpd_ws} provides serveral functions for encoding of
+payload data and decoding of frame data:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_decode()} decodes received frame data.
+The payload data may be of any kind, depending upon what the client has sent.
+So this decode function is used for all kind of frames and returns
+the frame type along with the payload data.
+
+@item
+@code{MHD_websocket_encode_text()} encodes text.
+The text must be encoded with UTF-8.
+
+@item
+@code{MHD_websocket_encode_binary()} encodes binary data.
+
+@item
+@code{MHD_websocket_encode_ping()} encodes a ping request to
+check whether the websocket is still valid and to test latency.
+
+@item
+@code{MHD_websocket_encode_ping()} encodes a pong response to
+answer a received ping request.
+
+@item
+@code{MHD_websocket_encode_close()} encodes a close request.
+
+@item
+@code{MHD_websocket_free()} frees data returned by the encode/decode functions.
+
+@end itemize
+
+Since you could receive or send fragmented data (i. e. due to a too
+small buffer passed to @code{recv}) all of these encode/decode
+functions require a pointer to a @code{struct MHD_WebSocketStream} passed
+as argument.
+In this structure @emph{libmicrohttpd_ws} stores information
+about encoding/decoding of the particular websocket.
+For each websocket you need a unique @code{struct MHD_WebSocketStream}
+to encode/decode with this library.
+
+To create or destroy @code{struct MHD_WebSocketStream}
+we have additional functions:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_stream_init()} allocates and initializes
+a new @code{struct MHD_WebSocketStream}.
+You can specify some options here to alter the behavior of the websocket 
stream.
+
+@item
+@code{MHD_websocket_stream_free()} frees a previously allocated
+@code{struct MHD_WebSocketStream}.
+
+@end itemize
+
+With these encode/decode functions we can improve our @code{upgrade_handler}
+callback function from an earlier example to a working websocket:
+
+
+@verbatim
+static void
+upgrade_handler (void *cls,
+                 struct MHD_Connection *connection,
+                 void *con_cls,
+                 const char *extra_in,
+                 size_t extra_in_size,
+                 MHD_socket fd,
+                 struct MHD_UpgradeResponseHandle *urh)
+{
+  /* make the socket blocking (operating-system-dependent code) */
+  make_blocking (fd);
+
+  /* create a websocket stream for this connection */
+  struct MHD_WebSocketStream* ws;
+  int result = MHD_websocket_stream_init (&ws,
+                                          0,
+                                          0);
+  if (0 != result)
+  {
+    /* Couldn't create the websocket stream.
+     * So we close the socket and leave
+     */
+    MHD_upgrade_action (urh,
+                        MHD_UPGRADE_ACTION_CLOSE);
+    return;
+  }
+
+  /* Let's wait for incoming data */
+  const size_t buf_len = 256;
+  char buf[buf_len];
+  ssize_t got;
+  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+  {
+    got = recv (fd,
+                buf,
+                buf_len,
+                0);
+    if (0 >= got)
+    {
+      /* the TCP/IP socket has been closed */
+      break;
+    }
+
+    /* parse the entire received data */
+    size_t buf_offset = 0;
+    while (buf_offset < (size_t) got)
+    {
+      size_t new_offset = 0;
+      char *frame_data = NULL;
+      size_t frame_len  = 0;
+      int status = MHD_websocket_decode (ws,
+                                         buf + buf_offset,
+                                         ((size_t) got) - buf_offset,
+                                         &new_offset,
+                                         &frame_data,
+                                         &frame_len);
+      if (0 > status)
+      {
+        /* an error occurred and the connection must be closed */
+        if (NULL != frame_data)
+        {
+          MHD_websocket_free (ws, frame_data);
+        }
+        break;
+      }
+      else
+      {
+        buf_offset += new_offset;
+        if (0 < status)
+        {
+          /* the frame is complete */
+          switch (status)
+          {
+          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+            /* The client has sent some text.
+             * We will display it and answer with a text frame.
+             */
+            if (NULL != frame_data)
+            {
+              printf ("Received message: %s\n", frame_data);
+              MHD_websocket_free (ws, frame_data);
+              frame_data = NULL;
+            }
+            result = MHD_websocket_encode_text (ws,
+                                                "Hello",
+                                                5,  /* length of "Hello" */
+                                                0,
+                                                &frame_data,
+                                                &frame_len,
+                                                NULL);
+            if (0 == result)
+            {
+              send_all (fd,
+                        frame_data,
+                        frame_len);
+            }
+            break;
+
+          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+            /* if we receive a close frame, we will respond with one */
+            MHD_websocket_free (ws,
+                                frame_data);
+            frame_data = NULL;
+
+            result = MHD_websocket_encode_close (ws,
+                                                 0,
+                                                 NULL,
+                                                 0,
+                                                 &frame_data,
+                                                 &frame_len);
+            if (0 == result)
+            {
+              send_all (fd,
+                        frame_data,
+                        frame_len);
+            }
+            break;
+
+          case MHD_WEBSOCKET_STATUS_PING_FRAME:
+            /* if we receive a ping frame, we will respond */
+            /* with the corresponding pong frame */
+            {
+              char *pong = NULL;
+              size_t pong_len = 0;
+              result = MHD_websocket_encode_pong (ws,
+                                                  frame_data,
+                                                  frame_len,
+                                                  &pong,
+                                                  &pong_len);
+              if (0 == result)
+              {
+                send_all (fd,
+                          pong,
+                          pong_len);
+              }
+              MHD_websocket_free (ws,
+                                  pong);
+            }
+            break;
+
+          default:
+            /* Other frame types are ignored
+             * in this minimal example.
+             * This is valid, because they become
+             * automatically skipped if we receive them unexpectedly
+             */
+            break;
+          }
+        }
+        if (NULL != frame_data)
+        {
+          MHD_websocket_free (ws, frame_data);
+        }
+      }
+    }
+  }
+
+  /* free the websocket stream */
+  MHD_websocket_stream_free (ws);
+
+  /* close the socket when it is not needed anymore */
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CLOSE);
+}
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+          const char *buf,
+          size_t len)
+{
+  ssize_t ret;
+  size_t off;
+
+  for (off = 0; off < len; off += ret)
+  {
+    ret = send (fd,
+                &buf[off],
+                (int) (len - off),
+                0);
+    if (0 > ret)
+    {
+      if (EAGAIN == errno)
+      {
+        ret = 0;
+        continue;
+      }
+      break;
+    }
+    if (0 == ret)
+      break;
+  }
+}
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+  int flags;
+
+  flags = fcntl (fd, F_GETFL);
+  if (-1 == flags)
+    return;
+  if ((flags & ~O_NONBLOCK) != flags)
+    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+      abort ();
+#elif defined(MHD_WINSOCK_SOCKETS)
+  unsigned long flags = 0;
+
+  ioctlsocket (fd, FIONBIO, &flags);
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+@end verbatim
+@noindent
+
+
+Please note that the websocket in this example is only half-duplex.
+It waits until the blocking @code{recv()} call returns and
+only does then something.
+In this example all frame types are decoded by @emph{libmicrohttpd_ws},
+but we only do something when a text, ping or close frame is received.
+Binary and pong frames are ignored in our code.
+This is legit, because the server is only required to implement at
+least support for ping frame or close frame (the other frame types
+could be skipped in theory, because they don't require an answer).
+The pong frame doesn't require an answer and whether text frames or
+binary frames get an answer simply belongs to your server application.
+So this is a valid minimal example.
+
+Until this point you've learned everything you need to basically
+use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
+These libraries offer much more functions for some specific cases.
+
+
+The further chapters of this tutorial focus on some specific problems
+and the client site programming.
+
+
+@heading Using full-duplex websockets
+
+To use full-duplex websockets you can simply create two threads
+per websocket connection.
+One of these threads is used for receiving data with
+a blocking @code{recv()} call and the other thread is triggered
+by the application internal codes and sends the data.
+
+A full-duplex websocket example is implemented in the example file
+@code{websocket_chatserver_example.c}.
+
+@heading Error handling
+
+The most functions of @emph{libmicrohttpd_ws} return a value
+of @code{enum MHD_WEBSOCKET_STATUS}.
+The values of this enumeration can be converted into an integer
+and have an easy interpretation:
+
+@itemize @bullet
+@item
+If the value is less than zero an error occurred and the call has failed.
+Check the enumeration values for more specific information.
+
+@item
+If the value is equal to zero, the call succeeded.
+
+@item
+If the value is greater than zero, the call succeeded and the value
+specifies the decoded frame type.
+Currently positive values are only returned by @code{MHD_websocket_decode()}
+(of the functions with this return enumeration type).
+
+@end itemize
+
+A websocket stream can also get broken when invalid frame data is received.
+Also the other site could send a close frame which puts the stream into
+a state where it may not be used for regular communication.
+Whether a stream has become broken, can be checked with
+@code{MHD_websocket_stream_is_valid()}.
+
+
+@heading Fragmentation
+
+In addition to the regular TCP/IP fragmentation the websocket protocol also
+supports fragmentation.
+Fragmentation could be used for continuous payload data such as video data
+from a webcam.
+Whether or not you want to receive fragmentation is specified upon
+initialization of the websocket stream.
+If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
+of @code{MHD_websocket_stream_init()} then you can receive fragments.
+If you don't pass this flag (in the most cases you just pass zero as flags)
+then you don't want to handle fragments on your own.
+@emph{libmicrohttpd_ws} removes then the fragmentation for you
+in the background.
+You only get the completely assembled frames.
+
+Upon encoding you specify whether or not you want to create a fragmented frame
+by passing a flag to the corresponding encode function.
+Only @code{MHD_websocket_encode_text()} and 
@code{MHD_websocket_encode_binary()}
+can be used for fragmentation, because the other frame types may
+not be fragmented.
+Encoding fragmented frames is independent of
+the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
+
+@heading Quick guide to websockets in JavaScript
+
+Websockets are supported in all modern web browsers.
+You initialize a websocket connection by creating an instance of
+the @code{WebSocket} class provided by the web browser.
+
+There are some simple rules for using websockets in the browser:
+
+@itemize @bullet
+@item
+When you initialize the instance of the websocket class you must pass an URL.
+The URL must either start with @code{ws://}
+(for not encrypted websocket protocol) or @code{wss://}
+(for TLS-encrypted websocket protocol).
+
+@strong{IMPORTANT:} If your website is accessed via @code{https://}
+then you are in a security context, which means that you are only allowed to
+access other secure protocols.
+So you can only use @code{wss://} for websocket connections then.
+If you try to @code{ws://} instead then your websocket connection will
+automatically fail.
+
+@item
+The WebSocket class uses events to handle the receiving of data.
+JavaScript is per definition a single-threaded language so
+the receiving events will never overlap.
+Sending is done directly by calling a method of the instance of
+the WebSocket class.
+
+@end itemize
+
+
+Here is a short example for receiving/sending data to the same host
+as the website is running on:
+
+@verbatim
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Websocket Demo</title>
+<script>
+
+let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
+          window.location.host + '/chat';
+let socket = null;
+
+window.onload = function(event) {
+  socket = new WebSocket(url);
+  socket.onopen = function(event) {
+    document.write('The websocket connection has been established.<br>');
+
+    // Send some text
+    socket.send('Hello from JavaScript!');
+  }
+
+  socket.onclose = function(event) {
+    document.write('The websocket connection has been closed.<br>');
+  }
+
+  socket.onerror = function(event) {
+    document.write('An error occurred during the websocket 
communication.<br>');
+  }
+
+  socket.onmessage = function(event) {
+    document.write('Websocket message received: ' + event.data + '<br>');
+  }
+}
+
+</script>
+</head>
+<body>
+</body>
+</html>
+
+@end verbatim
+@noindent
diff --git a/doc/examples/websocket.c b/doc/examples/websocket.c
new file mode 100644
index 00000000..39995479
--- /dev/null
+++ b/doc/examples/websocket.c
@@ -0,0 +1,446 @@
+/* Feel free to use this example code in any way
+   you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <microhttpd_ws.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define PORT 80
+
+#define PAGE \
+  "<!DOCTYPE html>\n" \
+  "<html>\n" \
+  "<head>\n" \
+  "<meta charset=\"UTF-8\">\n" \
+  "<title>Websocket Demo</title>\n" \
+  "<script>\n" \
+  "\n" \
+  "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
+    "  + '://' +\n" \
+  "          window.location.host + '/chat';\n" \
+  "let socket = null;\n" \
+  "\n" \
+  "window.onload = function(event) {\n" \
+  "  socket = new WebSocket(url);\n" \
+  "  socket.onopen = function(event) {\n" \
+  "    document.write('The websocket connection has been " \
+    "established.<br>');\n" \
+  "\n" \
+  "    // Send some text\n" \
+  "    socket.send('Hello from JavaScript!');\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onclose = function(event) {\n" \
+  "    document.write('The websocket connection has been closed.<br>');\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onerror = function(event) {\n" \
+  "    document.write('An error occurred during the websocket " \
+    "communication.<br>');\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onmessage = function(event) {\n" \
+  "    document.write('Websocket message received: ' + " \
+    "event.data + '<br>');\n" \
+  "  }\n" \
+  "}\n" \
+  "\n" \
+  "</script>\n" \
+  "</head>\n" \
+  "<body>\n" \
+  "</body>\n" \
+  "</html>"
+
+#define PAGE_NOT_FOUND \
+  "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+  "Invalid WebSocket request!"
+
+static void
+send_all (MHD_socket fd,
+          const char *buf,
+          size_t len);
+static void
+make_blocking (MHD_socket fd);
+
+static void
+upgrade_handler (void *cls,
+                 struct MHD_Connection *connection,
+                 void *con_cls,
+                 const char *extra_in,
+                 size_t extra_in_size,
+                 MHD_socket fd,
+                 struct MHD_UpgradeResponseHandle *urh)
+{
+  /* make the socket blocking (operating-system-dependent code) */
+  make_blocking (fd);
+
+  /* create a websocket stream for this connection */
+  struct MHD_WebSocketStream* ws;
+  int result = MHD_websocket_stream_init (&ws,
+                                          0,
+                                          0);
+  if (0 != result)
+  {
+    /* Couldn't create the websocket stream.
+     * So we close the socket and leave
+     */
+    MHD_upgrade_action (urh,
+                        MHD_UPGRADE_ACTION_CLOSE);
+    return;
+  }
+
+  /* Let's wait for incoming data */
+  const size_t buf_len = 256;
+  char buf[buf_len];
+  ssize_t got;
+  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+  {
+    got = recv (fd,
+                buf,
+                buf_len,
+                0);
+    if (0 >= got)
+    {
+      /* the TCP/IP socket has been closed */
+      break;
+    }
+
+    /* parse the entire received data */
+    size_t buf_offset = 0;
+    while (buf_offset < (size_t) got)
+    {
+      size_t new_offset = 0;
+      char *frame_data = NULL;
+      size_t frame_len  = 0;
+      int status = MHD_websocket_decode (ws,
+                                         buf + buf_offset,
+                                         ((size_t) got) - buf_offset,
+                                         &new_offset,
+                                         &frame_data,
+                                         &frame_len);
+      if (0 > status)
+      {
+        /* an error occurred and the connection must be closed */
+        if (NULL != frame_data)
+        {
+          MHD_websocket_free (ws, frame_data);
+        }
+        break;
+      }
+      else
+      {
+        buf_offset += new_offset;
+        if (0 < status)
+        {
+          /* the frame is complete */
+          switch (status)
+          {
+          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+            /* The client has sent some text.
+             * We will display it and answer with a text frame.
+             */
+            if (NULL != frame_data)
+            {
+              printf ("Received message: %s\n", frame_data);
+              MHD_websocket_free (ws, frame_data);
+              frame_data = NULL;
+            }
+            result = MHD_websocket_encode_text (ws,
+                                                "Hello",
+                                                5,  /* length of "Hello" */
+                                                0,
+                                                &frame_data,
+                                                &frame_len,
+                                                NULL);
+            if (0 == result)
+            {
+              send_all (fd,
+                        frame_data,
+                        frame_len);
+            }
+            break;
+
+          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+            /* if we receive a close frame, we will respond with one */
+            MHD_websocket_free (ws,
+                                frame_data);
+            frame_data = NULL;
+
+            result = MHD_websocket_encode_close (ws,
+                                                 0,
+                                                 NULL,
+                                                 0,
+                                                 &frame_data,
+                                                 &frame_len);
+            if (0 == result)
+            {
+              send_all (fd,
+                        frame_data,
+                        frame_len);
+            }
+            break;
+
+          case MHD_WEBSOCKET_STATUS_PING_FRAME:
+            /* if we receive a ping frame, we will respond */
+            /* with the corresponding pong frame */
+            {
+              char *pong = NULL;
+              size_t pong_len = 0;
+              result = MHD_websocket_encode_pong (ws,
+                                                  frame_data,
+                                                  frame_len,
+                                                  &pong,
+                                                  &pong_len);
+              if (0 == result)
+              {
+                send_all (fd,
+                          pong,
+                          pong_len);
+              }
+              MHD_websocket_free (ws,
+                                  pong);
+            }
+            break;
+
+          default:
+            /* Other frame types are ignored
+             * in this minimal example.
+             * This is valid, because they become
+             * automatically skipped if we receive them unexpectedly
+             */
+            break;
+          }
+        }
+        if (NULL != frame_data)
+        {
+          MHD_websocket_free (ws, frame_data);
+        }
+      }
+    }
+  }
+
+  /* free the websocket stream */
+  MHD_websocket_stream_free (ws);
+
+  /* close the socket when it is not needed anymore */
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CLOSE);
+}
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+          const char *buf,
+          size_t len)
+{
+  ssize_t ret;
+  size_t off;
+
+  for (off = 0; off < len; off += ret)
+  {
+    ret = send (fd,
+                &buf[off],
+                (int) (len - off),
+                0);
+    if (0 > ret)
+    {
+      if (EAGAIN == errno)
+      {
+        ret = 0;
+        continue;
+      }
+      break;
+    }
+    if (0 == ret)
+      break;
+  }
+}
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#ifndef _WIN32
+  int flags;
+
+  flags = fcntl (fd, F_GETFL);
+  if (-1 == flags)
+    return;
+  if ((flags & ~O_NONBLOCK) != flags)
+    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+      abort ();
+#else
+  unsigned long flags = 0;
+
+  ioctlsocket (fd, FIONBIO, &flags);
+#endif
+}
+
+static enum MHD_Result
+access_handler (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  (void) cls;               /* Unused. Silent compiler warning. */
+  (void) upload_data;       /* Unused. Silent compiler warning. */
+  (void) upload_data_size;  /* Unused. Silent compiler warning. */
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+  {
+    /* do never respond on first call */
+    *ptr = &aptr;
+    return MHD_YES;
+  }
+  *ptr = NULL;                  /* reset when done */
+
+  if (0 == strcmp (url, "/"))
+  {
+    /* Default page for visiting the server */
+    struct MHD_Response *response = MHD_create_response_from_buffer (
+                                      strlen (PAGE),
+                                      PAGE,
+                                      MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_OK,
+                              response);
+    MHD_destroy_response (response);
+  }
+  else if (0 == strcmp (url, "/chat"))
+  {
+    char is_valid = 1;
+    const char* value = NULL;
+    char sec_websocket_accept[29];
+
+    if (0 != MHD_websocket_check_http_version (version))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_CONNECTION);
+    if (0 != MHD_websocket_check_connection_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_UPGRADE);
+    if (0 != MHD_websocket_check_upgrade_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         
MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+    if (0 != MHD_websocket_check_version_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+    {
+      is_valid = 0;
+    }
+
+    if (1 == is_valid)
+    {
+      /* upgrade the connection */
+      response = MHD_create_response_for_upgrade (&upgrade_handler,
+                                                  NULL);
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_CONNECTION,
+                               "Upgrade");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_UPGRADE,
+                               "websocket");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+                               sec_websocket_accept);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_SWITCHING_PROTOCOLS,
+                                response);
+      MHD_destroy_response (response);
+    }
+    else
+    {
+      /* return error page */
+      struct MHD_Response*response = MHD_create_response_from_buffer (
+                                       strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+                                       PAGE_INVALID_WEBSOCKET_REQUEST,
+                                       MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_BAD_REQUEST,
+                                response);
+      MHD_destroy_response (response);
+    }
+  }
+  else
+  {
+    struct MHD_Response*response = MHD_create_response_from_buffer (
+                                     strlen (PAGE_NOT_FOUND),
+                                     PAGE_NOT_FOUND,
+                                     MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_NOT_FOUND,
+                              response);
+    MHD_destroy_response (response);
+  }
+
+  return ret;
+}
+
+int
+main (int argc,
+      char *const *argv)
+{
+  (void) argc;               /* Unused. Silent compiler warning. */
+  (void) argv;               /* Unused. Silent compiler warning. */
+  struct MHD_Daemon *daemon;
+
+  daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
+                             MHD_USE_THREAD_PER_CONNECTION |
+                             MHD_ALLOW_UPGRADE |
+                             MHD_USE_ERROR_LOG,
+                             PORT, NULL, NULL,
+                             &access_handler, NULL,
+                             MHD_OPTION_END);
+
+  if (NULL == daemon)
+    return 1;
+  (void) getc (stdin);
+
+  MHD_stop_daemon (daemon);
+
+  return 0;
+}
diff --git a/doc/libmicrohttpd-tutorial.texi b/doc/libmicrohttpd-tutorial.texi
index de040fe8..7d3cd23a 100644
--- a/doc/libmicrohttpd-tutorial.texi
+++ b/doc/libmicrohttpd-tutorial.texi
@@ -68,6 +68,7 @@ Free Documentation License".
 * Improved processing of POST data::
 * Session management::
 * Adding a layer of security::
+* Websockets::
 * Bibliography::
 * License text::
 * Example programs::
@@ -109,6 +110,10 @@ Free Documentation License".
 @chapter Adding a layer of security
 @include chapters/tlsauthentication.inc
 
+@node Websockets
+@chapter Websockets
+@include chapters/websocket.inc
+
 @node Bibliography
 @appendix Bibliography
 @include chapters/bibliography.inc
@@ -128,6 +133,7 @@ Free Documentation License".
 * largepost.c::
 * sessions.c::
 * tlsauthentication.c::
+* websocket.c::
 @end menu
 
 @node hellobrowser.c
@@ -178,4 +184,10 @@ Free Documentation License".
 @verbatiminclude examples/tlsauthentication.c
 @end smalldisplay
 
+@node websocket.c
+@section websocket.c
+@smalldisplay
+@verbatiminclude examples/websocket.c
+@end smalldisplay
+
 @bye
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 8e275a3b..a6bd12eb 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -67,6 +67,7 @@ Free Documentation License".
 * microhttpd-post::             Adding a @code{POST} processor.
 * microhttpd-info::             Obtaining and modifying status information.
 * microhttpd-util::             Utilities.
+* microhttpd-websocket::        Websockets.
 
 Appendices
 
@@ -1246,6 +1247,493 @@ list.
 @end deftp
 
 
+@deftp {Enumeration} MHD_WEBSOCKET_FLAG
+@cindex websocket
+Options for the MHD websocket stream.
+
+This is used for initialization of a websocket stream when calling
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} and
+alters the behavior of the websocket stream.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_FLAG_SERVER
+The websocket stream is initialized in server mode (default).
+Thus all outgoing payload will not be masked.
+All incoming payload must be masked.
+
+This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_CLIENT}.
+
+@item MHD_WEBSOCKET_FLAG_CLIENT
+The websocket stream is initialized in client mode.
+You will usually never use that mode in combination with @emph{libmicrohttpd},
+because @emph{libmicrohttpd} provides a server and not a client.
+In client mode all outgoing payload will be masked
+(XOR-ed with random values).
+All incoming payload must be unmasked.
+If you use this mode, you must always call @code{MHD_websocket_stream_init2}
+instead of @code{MHD_websocket_stream_init}, because you need
+to pass a random number generator callback function for masking.
+
+This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_SERVER}.
+
+@item MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
+You don't want to get fragmented data while decoding (default).
+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 @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}.
+
+@item MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+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 sequence (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 @code{MHD_WEBSOCKET_FLAG_NO_FRAGMENTS}.
+
+@item MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
+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
+@code{payload} and @code{payload_len} of @code{MHD_websocket_decode} and
+the return value is negative (a value of @code{enum MHD_WEBSOCKET_STATUS}).
+
+The generated close frame must be freed by the caller
+with @code{MHD_websocket_free}.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_FRAGMENTATION
+@cindex websocket
+This enumeration is used to specify the fragmentation behavior
+when encoding of data (text/binary) for a websocket stream.
+This is used with @code{MHD_websocket_encode_text} or
+@code{MHD_websocket_encode_binary}.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_FRAGMENTATION_NONE
+You don't want to use fragmentation.
+The encoded frame consists of only one frame.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_FIRST
+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.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING
+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.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_LAST
+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 types of frames again.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_STATUS
+@cindex websocket
+This enumeration is used for the return value of almost
+every websocket stream function.
+Errors are negative and values equal to or above zero mean a success.
+Positive values are only used by @code{MHD_websocket_decode}.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_STATUS_OK
+The call succeeded.
+Especially for @code{MHD_websocket_decode} this means that no error occurred,
+but also no frame has been completed yet.
+For other functions this means simply a success.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FRAME
+@code{MHD_websocket_decode} has decoded a text frame.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_FRAME
+@code{MHD_websocket_decode} has decoded a binary frame.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_CLOSE_FRAME
+@code{MHD_websocket_decode} has decoded a close frame.
+This means you must close the socket using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may respond with a close frame before closing.
+The parameters @code{payload} and @code{payload_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 @code{enum MHD_WEBSOCKET_CLOSEREASON}).
+After these two bytes a UTF-8 encoded close reason may follow.
+You can call @code{MHD_websocket_split_close_reason} to split that
+close reason.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PING_FRAME
+@code{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 @code{payload} and @code{payload_len} are filled with
+the binary ping data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PONG_FRAME
+@code{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 @code{payload} and @code{payload_len} are filled with
+the binary pong data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
+@code{MHD_websocket_decode} has decoded a text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FRAME}, but it can only
+appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
+the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
+@code{MHD_websocket_decode} has decoded a binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FRAME}, but it can only
+appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
+the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT
+@code{MHD_websocket_decode} has decoded the next text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
+only after the first and before the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT
+@code{MHD_websocket_decode} has decoded the next binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
+only after the first and before the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT
+@code{MHD_websocket_decode} has decoded the last text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
+only for the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT
+@code{MHD_websocket_decode} has decoded the last binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
+only for the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR
+The call failed and the stream is invalid now for decoding.
+You must close the websocket now using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+This is only used by @code{MHD_websocket_decode} and happens
+if the stream contains errors (i. e. invalid byte data).
+
+@item MHD_WEBSOCKET_STATUS_STREAM_BROKEN
+You tried to decode something, but the stream has already
+been marked invalid.
+You must close the websocket now using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+This is only used by @code{MHD_websocket_decode} and happens
+if you call @code{MDM_websocket_decode} again after
+has been invalidated.
+You can call @code{MHD_websocket_stream_is_valid} at any time
+to check whether a stream is invalid or not.
+
+@item MHD_WEBSOCKET_STATUS_MEMORY_ERROR
+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
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} to
+avoid this and close the websocket instead.
+
+@item MHD_WEBSOCKET_STATUS_PARAMETER_ERROR
+You passed invalid parameters during the function call
+(i. e. a NULL pointer for a required parameter).
+The stream remains valid.
+
+@item MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED
+The maximum payload size has been exceeded.
+If you got this return code from @code{MHD_websocket_decode} then
+the stream becomes invalid and the websocket must be closed
+using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+The maximum payload size is specified during the call of
+@code{MHD_websocket_stream_init} or @code{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 GiB on 32 bit systems).
+If you got this return code from @code{MHD_websocket_encode_close},
+@code{MHD_websocket_encode_ping} or @code{MHD_websocket_encode_pong} then
+you passed to much payload data. The stream remains valid then.
+
+@item MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR
+An UTF-8 sequence is invalid.
+If you got this return code from @code{MHD_websocket_decode} then
+the stream becomes invalid and you must close the websocket
+using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+If you got this from @code{MHD_websocket_encode_text} or
+@code{MHD_websocket_encode_close} then you passed invalid UTF-8 text.
+The stream remains valid then.
+
+@item MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER
+A check routine for the HTTP headers came to the conclusion that
+the header value isn't valid for a websocket handshake request.
+This value can only be returned from the following functions:
+@code{MHD_websocket_check_http_version},
+@code{MHD_websocket_check_connection_header},
+@code{MHD_websocket_check_upgrade_header},
+@code{MHD_websocket_check_version_header},
+@code{MHD_websocket_create_accept_header}
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_CLOSEREASON
+@cindex websocket
+Enumeration of possible close reasons for websocket 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:
+@itemize @bullet
+@item 0-999 are never used (if you pass 0 in
+@code{MHD_websocket_encode_close} then no close reason is used).
+@item 1000-2999 are specified by RFC 6455.
+@item 3000-3999 are specified by libraries, etc. but must be registered by 
IANA.
+@item 4000-4999 are reserved for private use.
+@end itemize
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_CLOSEREASON_NO_REASON
+This value is used as placeholder for @code{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 be a result of decoding, because this value
+is not a valid close reason for the websocket protocol.
+
+@item MHD_WEBSOCKET_CLOSEREASON_REGULAR
+You close the websocket because it fulfilled its purpose and shall
+now be closed in a normal, planned way.
+
+@item MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY
+You close the websocket because you are shutting down the server or
+something similar.
+
+@item MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR
+You close the websocket because a protocol error occurred
+during decoding (i. e. invalid byte data).
+
+@item MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE
+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.
+
+@item MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8
+You close the websocket because it contains malformed UTF-8.
+The UTF-8 validity is automatically checked by @code{MHD_websocket_decode},
+so you don't need to check it on your own.
+UTF-8 is specified in RFC 3629.
+
+@item MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED
+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
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
+
+@item MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION
+This status code can be sent by the client if it
+expected a specific extension, but this extension hasn't been negotiated.
+
+@item MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION
+The server closes the websocket because it encountered
+an unexpected condition that prevented it from fulfilling the request.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_UTF8STEP
+@cindex websocket
+Enumeration of possible UTF-8 check steps for websocket functions
+
+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.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_UTF8STEP_NORMAL
+There is no open UTF-8 sequence.
+The next byte must be 0x00-0x7F or 0xC2-0xF4.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1
+The second byte of a two byte UTF-8 sequence.
+The first byte was 0xC2-0xDF.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2
+The second byte of a three byte UTF-8 sequence.
+The first byte was 0xE0.
+The next byte must be 0xA0-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2
+The second byte of a three byte UTF-8 sequence.
+The first byte was 0xED.
+The next byte must by 0x80-0x9F.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2
+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.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2
+The third byte of a three byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF0.
+The next byte must be 0x90-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF4.
+The next byte must be 0x80-0x8F.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF1-0xF3.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3
+The third byte of a four byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3
+The fourth byte of a four byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_VALIDITY
+@cindex websocket
+Enumeration of validity values of a websocket stream
+
+These values are used for @code{MHD_websocket_stream_is_valid}
+and specify the validity status.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_VALIDITY_INVALID
+The stream is invalid.
+It cannot be used for decoding anymore.
+
+@item MHD_WEBSOCKET_VALIDITY_VALID
+The stream is valid.
+Decoding works as expected.
+
+@item MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES
+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.
+
+@end table
+@end deftp
+
+
 @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 @c ------------------------------------------------------------
@@ -1291,6 +1779,12 @@ Information about an MHD daemon.
 @end deftp
 
 
+@deftp {C Struct} MHD_WebSocketStream
+@cindex websocket
+Information about a MHD websocket stream.
+@end deftp
+
+
 @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 @c ------------------------------------------------------------
@@ -1549,6 +2043,95 @@ iteration.
 @end deftypefn
 
 
+@deftypefn {Function Pointer} void* {*MHD_WebSocketMallocCallback} (size_t 
buf_len)
+@cindex websocket
+This callback function is used internally by many websocket functions
+for allocating data.
+By default @code{malloc} is used.
+You can use your own allocation function with @code{MHD_websocket_stream_init2}
+if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{malloc} callback of
+a websocket stream with @code{MHD_websocket_malloc}.
+
+@table @var
+@item buf_len
+size of the buffer to allocate in bytes.
+@end table
+
+Return the pointer of the allocated buffer or @code{NULL} on failure.
+@end deftypefn
+
+
+@deftypefn {Function Pointer} void* {*MHD_WebSocketReallocCallback} (void 
*buf, size_t new_buf_len)
+@cindex websocket
+This callback function is used internally by many websocket
+functions for reallocating data.
+By default @code{realloc} is used.
+You can use your own reallocation function with
+@code{MHD_websocket_stream_init2} if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{realloc} callback of
+a websocket stream with @code{MHD_websocket_realloc}.
+
+@table @var
+@item buf
+current buffer, may be @code{NULL};
+
+@item new_buf_len
+new size of the buffer in bytes.
+@end table
+
+Return the pointer of the reallocated buffer or @code{NULL} on failure.
+On failure the old pointer must remain valid.
+@end deftypefn
+
+
+@deftypefn {Function Pointer} void {*MHD_WebSocketFreeCallback} (void *buf)
+@cindex websocket
+This callback function is used internally by many websocket
+functions for freeing data.
+By default @code{free} is used.
+You can use your own free function with
+@code{MHD_websocket_stream_init2} if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{free} callback of
+a websocket stream with @code{MHD_websocket_free}.
+
+@table @var
+@item cls
+current buffer to free, this may be @code{NULL} then nothing happens.
+@end table
+@end deftypefn
+
+
+@deftypefn {Function Pointer} size_t {*MHD_WebSocketRandomNumberGenerator} 
(void *cls, void* buf, size_t buf_len)
+@cindex websocket
+This callback function is used for generating random numbers
+for masking payload data in client mode.
+If you use websockets in server mode with @emph{libmicrohttpd} then
+you don't need a random number generator, because
+the server doesn't mask its outgoing messages.
+However if you wish to use a websocket stream in client mode,
+you must pass this callback function to @code{MHD_websocket_stream_init2}.
+
+@table @var
+@item cls
+closure specified in @code{MHD_websocket_stream_init2};
+@item buf
+buffer to fill with random values;
+@item buf_len
+size of buffer in bytes.
+@end table
+
+Return the number of generated random bytes.
+The return value should usually equal to buf_len.
+@end deftypefn
+
+
 @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 @c ------------------------------------------------------------
@@ -3346,6 +3929,704 @@ shorter afterwards due to elimination of escape 
sequences).
 
 
 
+
+@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket
+@chapter Websocket functions.
+
+@noindent
+Websocket functions provide what you need to use an upgraded connection
+as a websocket.
+These functions are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@menu
+* microhttpd-websocket handshake::    Websocket handshake functions
+* microhttpd-websocket stream::       Websocket stream functions
+* microhttpd-websocket decode::       Websocket decode functions
+* microhttpd-websocket encode::       Websocket encode functions
+* microhttpd-websocket memory::       Websocket memory functions
+@end menu
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket handshake
+@section Websocket handshake functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_http_version 
(const char* http_version)
+@cindex websocket
+Checks the HTTP version of the incoming request.
+Websocket requests are only allowed for HTTP/1.1 or above.
+
+@table @var
+@item http_version
+The value of the @code{version} parameter of your
+@code{access_handler} callback.
+If you pass @code{NULL} then this is handled like a not
+matching HTTP version.
+@end table
+
+Returns 0 when the HTTP version is
+valid for a websocket request and
+a value less than zero when the HTTP version isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_connection_header 
(const char* connection_header)
+@cindex websocket
+Checks the value of the @code{Connection} HTTP request header.
+Websocket requests require the token @code{Upgrade} in
+the @code{Connection} HTTP request header.
+
+@table @var
+@item connection_header
+Value of the @code{Connection} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_CONNECTION} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Connection} header value.
+@end table
+
+Returns 0 when the @code{Connection} header is
+valid for a websocket request and
+a value less than zero when the @code{Connection} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_upgrade_header 
(const char* upgrade_header)
+@cindex websocket
+Checks the value of the @code{Upgrade} HTTP request header.
+Websocket requests require the value @code{websocket} in
+the @code{Upgrade} HTTP request header.
+
+@table @var
+@item upgrade_header
+Value of the @code{Upgrade} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_UPGRADE} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Upgrade} header value.
+@end table
+
+Returns 0 when the @code{Upgrade} header is
+valid for a websocket request and
+a value less than zero when the @code{Upgrade} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_version_header 
(const char* version_header)
+@cindex websocket
+Checks the value of the @code{Sec-WebSocket-Version} HTTP request header.
+Websocket requests require the value @code{13} in
+the @code{Sec-WebSocket-Version} HTTP request header.
+
+@table @var
+@item version_header
+Value of the @code{Sec-WebSocket-Version} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Sec-WebSocket-Version} header value.
+@end table
+
+Returns 0 when the @code{Sec-WebSocket-Version} header is
+valid for a websocket request and
+a value less than zero when the @code{Sec-WebSocket-Version} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_create_accept_header 
(const char* sec_websocket_key, char* sec_websocket_accept)
+@cindex websocket
+Checks the value of the @code{Sec-WebSocket-Key}
+HTTP request header and generates the value for
+the @code{Sec-WebSocket-Accept} HTTP response header.
+The generated value must be sent to the client.
+
+@table @var
+@item sec_websocket_key
+Value of the @code{Sec-WebSocket-Key} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Sec-WebSocket-Key} header value.
+
+@item sec_websocket_accept
+Response buffer, which will receive
+the generated value for the @code{Sec-WebSocket-Accept}
+HTTP response header.
+This buffer must be at least 29 bytes long and
+will contain the response value plus a terminating @code{NUL}
+character on success.
+Must not be @code{NULL}.
+You can add this HTTP header to your response by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT} to
+@code{MHD_add_response_header()}.
+@end table
+
+Returns 0 when the @code{Sec-WebSocket-Key} header was
+not empty and a result value for the @code{Sec-WebSocket-Accept}
+was calculated.
+A value less than zero is returned when the @code{Sec-WebSocket-Key}
+header isn't valid for a websocket request or when any
+error occurred.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket stream
+@section Websocket stream functions
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init (struct 
MHD_WebSocketStream **ws, int flags, size_t max_payload_size)
+@cindex websocket
+Creates a new websocket stream, used for decoding/encoding.
+
+@table @var
+@item ws
+pointer a variable to fill with the newly created
+@code{struct MHD_WebSocketStream},
+receives @code{NULL} on error. May not be @code{NULL}.
+
+If not required anymore, free the created websocket stream with
+@code{MHD_websocket_stream_free()}.
+
+@item flags
+combination of @code{enum MHD_WEBSOCKET_FLAG} values to
+modify the behavior of the websocket stream.
+
+@item max_payload_size
+maximum size for incoming payload data in bytes. Use 0 to allow each size.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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, void* cls_rng, 
MHD_WebSocketRandomNumberGenerator callback_rng)
+@cindex websocket
+Creates a new websocket stream, used for decoding/encoding,
+but with custom memory functions for malloc, realloc and free.
+Also a random number generator can be specified for client mode.
+
+@table @var
+@item ws
+pointer a variable to fill with the newly created
+@code{struct MHD_WebSocketStream},
+receives @code{NULL} on error. Must not be @code{NULL}.
+
+If not required anymore, free the created websocket stream with
+@code{MHD_websocket_stream_free}.
+
+@item flags
+combination of @code{enum MHD_WEBSOCKET_FLAG} values to
+modify the behavior of the websocket stream.
+
+@item max_payload_size
+maximum size for incoming payload data in bytes. Use 0 to allow each size.
+
+@item callback_malloc
+callback function for allocating memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to 
@code{malloc} here.
+
+@item callback_realloc
+callback function for reallocating memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to 
@code{realloc} here.
+
+@item callback_free
+callback function for freeing memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to 
@code{free} here.
+
+@item cls_rng
+closure for the random number generator.
+This is only required when
+@code{MHD_WEBSOCKET_FLAG_CLIENT} is passed in @code{flags}.
+The given value is passed to the random number generator callback.
+May be @code{NULL} if not needed.
+Should be @code{NULL} when you are not using @code{MHD_WEBSOCKET_FLAG_CLIENT}.
+The shorter @code{MHD_websocket_stream_init} passes @code{NULL} here.
+
+@item callback_rng
+callback function for a secure random number generator.
+This is only required when @code{MHD_WEBSOCKET_FLAG_CLIENT} is
+passed in @code{flags} and must not be @code{NULL} then.
+Should be @code{NULL} otherwise.
+The shorter @code{MHD_websocket_stream_init()} passes @code{NULL} here.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_free (struct 
MHD_WebSocketStream *ws)
+@cindex websocket
+Frees a previously allocated websocket stream
+
+@table @var
+@item ws
+websocket stream to free, this value may be @code{NULL}.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_invalidate 
(struct MHD_WebSocketStream *ws)
+@cindex websocket
+Invalidates a websocket stream.
+After invalidation a websocket stream cannot be used for decoding anymore.
+Encoding is still possible.
+
+@table @var
+@item ws
+websocket stream to invalidate.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_VALIDITY} MHD_websocket_stream_is_valid 
(struct MHD_WebSocketStream *ws)
+@cindex websocket
+Queries whether a websocket stream is valid.
+Invalidated websocket streams cannot be used for decoding anymore.
+Encoding is still possible.
+
+@table @var
+@item ws
+websocket stream to invalidate.
+@end table
+
+Returns 0 if invalid, 1 if valid for all types or
+2 if valid only for control frames.
+Can be compared with @code{enum MHD_WEBSOCKET_VALIDITY}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket decode
+@section Websocket decode functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Decodes a byte sequence for a websocket stream.
+Decoding is done until either a frame is complete or
+the end of the byte sequence is reached.
+
+@table @var
+@item ws
+websocket stream for decoding.
+
+@item streambuf
+byte sequence for decoding.
+This is what you typically received via @code{recv()}.
+
+@item streambuf_len
+length of the byte sequence in parameter @code{streambuf}.
+
+@item streambuf_read_len
+pointer to a variable, which receives the number of bytes,
+that has been processed by this call.
+This value may be less than the value of @code{streambuf_len} when
+a frame is decoded before the end of the buffer is reached.
+The remaining bytes of @code{buf} must be passed to
+the next call of this function.
+
+@item payload
+pointer to a variable, which receives the allocated buffer with the payload
+data of the decoded frame. Must not be @code{NULL}.
+If no decoded data is available or an error occurred @code{NULL} is returned.
+When the returned value is not @code{NULL} then the buffer contains always
+@code{payload_len} bytes plus one terminating @code{NUL} character
+(regardless of the frame type).
+
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+If you passed the flag @code{MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR}
+upon creation of the websocket stream and a decoding error occurred
+(function 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 @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+upon creation of the 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.
+
+@item payload_len
+pointer to a variable, which receives length of the result
+@code{payload} buffer in bytes.
+Must not be @code{NULL}.
+This receives 0 when no data is available, when the decoded payload
+has a length of zero or when an error occurred.
+@end table
+
+Returns a value greater than zero when a frame is complete.
+Compare with @code{enum MHD_WEBSOCKET_STATUS} to distinguish the frame type.
+Returns 0 when the call succeeded, but no frame is available.
+Returns a value less than zero on errors.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Splits the payload of a decoded close frame.
+
+@table @var
+@item payload
+payload of the close frame.
+This parameter may only be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload}.
+
+@item reason_code
+pointer to a variable, which receives the numeric close reason.
+If there was no close reason, this is 0.
+This value can be compared with @code{enum MHD_WEBSOCKET_CLOSEREASON}.
+May be @code{NULL}.
+
+@item reason_utf8
+pointer to a variable, which receives the literal close reason.
+If there was no literal close reason, this will be @code{NULL}.
+May be @code{NULL}.
+
+Please note that no memory is allocated in this function.
+If not @code{NULL} the returned value of this parameter
+points to a position in the specified @code{payload}.
+
+@item reason_utf8_len
+pointer to a variable, which receives the length of the literal close reason.
+If there was no literal close reason, this is 0.
+May be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket encode
+@section Websocket encode functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Encodes an UTF-8 encoded text into websocket text frame
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload_utf8
+text to send. This must be UTF-8 encoded.
+If you don't want UTF-8 then send a binary frame
+with @code{MHD_websocket_encode_binary()} instead.
+May be be @code{NULL} if @code{payload_utf8_len} is 0,
+must not be @code{NULL} otherwise.
+
+@item payload_utf8_len
+length of @code{payload_utf8} in bytes.
+
+@item fragmentation
+A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
+to specify the fragmentation behavior.
+Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
+if you don't want to use fragmentation (default).
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded text frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+
+@item utf8_step
+If fragmentation is used (the parameter @code{fragmentation} is not 0)
+then is parameter is required and must not be @code{NULL}.
+If no fragmentation is used, this parameter is optional and
+should be @code{NULL}.
+
+This parameter is a pointer to a variable which contains the last check status
+of the UTF-8 sequence. It is required to continue a previous
+UTF-8 sequence check when fragmentation is used, because a UTF-8 sequence
+could be splitted upon fragments.
+
+@code{enum MHD_WEBSOCKET_UTF8STEP} is used for this value.
+If you start a new fragment using
+@code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or
+@code{MHD_WEBSOCKET_FRAGMENTATION_FIRST} the old value of this variable
+will be discarded and the value of this variable will be initialized
+to @code{MHD_WEBSOCKET_UTF8STEP_NORMAL}.
+On all other fragmentation modes the previous value of the pointed variable
+will be used to continue the UTF-8 sequence check.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_binary (struct 
MHD_WebSocketStream* ws, const char* payload, size_t payload_len, int 
fragmentation, char** frame, size_t* frame_len)
+@cindex websocket
+Encodes binary data into websocket binary frame
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary data to send.
+May be be @code{NULL} if @code{payload_len} is 0,
+must not be @code{NULL} otherwise.
+
+@item payload_len
+length of @code{payload} in bytes.
+
+@item fragmentation
+A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
+to specify the fragmentation behavior.
+Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
+if you don't want to use fragmentation (default).
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded binary frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_ping (struct 
MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, 
size_t* frame_len)
+@cindex websocket
+Encodes a websocket ping frame.
+Ping frames are used to check whether a recipient is still available
+and what latency the websocket connection has.
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary ping data to send.
+May be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload} in bytes.
+This may not exceed 125 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded ping frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_pong (struct 
MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, 
size_t* frame_len)
+@cindex websocket
+Encodes a websocket pong frame.
+Pong frames are used to answer a previously received websocket ping frame.
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary pong data to send, which should be
+the decoded payload from the received ping frame.
+May be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload} in bytes.
+This may not exceed 125 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded pong frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Encodes a websocket close frame.
+Close frames are used to close a websocket connection in a formal way.
+
+@table @var
+@item ws
+websocket stream;
+
+@item reason_code
+reason for close.
+You can use @code{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
+@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON} (or just 0) then
+no reason is encoded.
+
+@item reason_utf8
+An UTF-8 encoded text reason why the connection is closed.
+This may be @code{NULL} if @code{reason_utf8_len} is 0.
+This must be @code{NULL} if @code{reason_code} equals to zero
+(@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON}).
+
+@item reason_utf8_len
+length of the UTF-8 encoded text reason in bytes.
+This may not exceed 123 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded close frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket memory
+@section Websocket memory functions
+
+
+@deftypefun {void*} MHD_websocket_malloc (struct MHD_WebSocketStream* ws, 
size_t buf_len)
+@cindex websocket
+Allocates memory with the associated @code{malloc()} function
+of the websocket stream.
+The memory allocation function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf_len
+size of the buffer to allocate in bytes.
+@end table
+
+Returns the pointer of the allocated buffer or @code{NULL} on failure.
+@end deftypefun
+
+
+@deftypefun {void*} MHD_websocket_realloc (struct MHD_WebSocketStream* ws, 
void* buf, size_t new_buf_len)
+@cindex websocket
+Reallocates memory with the associated @code{realloc()} function
+of the websocket stream.
+The memory reallocation function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf
+current buffer, may be @code{NULL};
+
+@item new_buf_len
+new size of the buffer in bytes.
+@end table
+
+Return the pointer of the reallocated buffer or @code{NULL} on failure.
+On failure the old pointer remains valid.
+@end deftypefun
+
+
+@deftypefun {void} MHD_websocket_free (struct MHD_WebSocketStream* ws, void* 
buf)
+@cindex websocket
+Frees memory with the associated @code{free()} function
+of the websocket stream.
+The memory free function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf
+buffer to free, this may be @code{NULL} then nothing happens.
+@end table
+
+@end deftypefun
+
+
+
+
+
 @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 
diff --git a/src/examples/websocket_chatserver_example.c 
b/src/examples/websocket_chatserver_example.c
index 701684cc..0893279b 100644
--- a/src/examples/websocket_chatserver_example.c
+++ b/src/examples/websocket_chatserver_example.c
@@ -43,6 +43,12 @@
   See: https://github.com/coapp-packages/pthreads/issues/2
 */
 #include "pthread_windows.h"
+
+/*
+  On Windows we will use stricmp instead of strcasecmp (strcasecmp is 
undefined there).
+*/
+#define strcasecmp stricmp
+
 #else
 /*
   On Unix systems we can use pthread.
@@ -180,429 +186,429 @@
   "  function window_onload(event)\n" \
   "  {\n" \
   "    // Determine the base url (for http:// this is ws:// for https:// this 
must be wss://)\n" \
-       //   "    baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' 
: '') + '://' + window.location.host + '/ChatServerWebSocket';\n" \
-       //   "    chat_generate();\n" \
-       //   "    chat_connect();\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This function generates the chat using DOM\n" \
-       //   "  */\n" \
-       //   "  function chat_generate()\n" \
-       //   "  {\n" \
-       //   "    document.body.innerHTML = '';\n" \
-       //   "    let chat = document.createElement('div');\n" \
-       //   "    document.body.appendChild(chat);\n" \
-       //   "    chat.id = 'Chat';\n" \
-       //   "    let messagesAndInput = document.createElement('div');\n" \
-       //   "    chat.appendChild(messagesAndInput);\n" \
-       //   "    messagesAndInput.classList.add('MessagesAndInput');\n" \
-       //   "    let messages = document.createElement('div');\n" \
-       //   "    messagesAndInput.appendChild(messages);\n" \
-       //   "    messages.id = 'Messages';\n" \
-       //   "    let input = document.createElement('div');\n" \
-       //   "    messagesAndInput.appendChild(input);\n" \
-       //   "    input.classList.add('Input');\n" \
-       //   "    let inputMessage = document.createElement('input');\n" \
-       //   "    input.appendChild(inputMessage);\n" \
-       //   "    inputMessage.type = 'text';\n" \
-       //   "    inputMessage.id = 'InputMessage';\n" \
-       //   "    inputMessage.disabled = true;\n" \
-       //   "    inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
-       //   "    let inputMessageSend = document.createElement('button');\n" \
-       //   "    input.appendChild(inputMessageSend);\n" \
-       //   "    inputMessageSend.id = 'InputMessageButton';\n" \
-       //   "    inputMessageSend.disabled = true;\n" \
-       //   "    inputMessageSend.innerText = 'send';\n" \
-       //   "    inputMessageSend.addEventListener('click', 
chat_onSendClicked);\n" \
-       //   "    let inputImage = document.createElement('input');\n" \
-       //   "    input.appendChild(inputImage);\n" \
-       //   "    inputImage.id = 'InputImage';\n" \
-       //   "    inputImage.type = 'file';\n" \
-       //   "    inputImage.accept = 'image/*';\n" \
-       //   "    inputImage.style.display = 'none';\n" \
-       //   "    inputImage.addEventListener('change', 
chat_onImageSelected);\n" \
-       //   "    let inputImageButton = document.createElement('button');\n" \
-       //   "    input.appendChild(inputImageButton);\n" \
-       //   "    inputImageButton.id = 'InputImageButton';\n" \
-       //   "    inputImageButton.disabled = true;\n" \
-       //   "    inputImageButton.innerText = 'image';\n" \
-       //   "    inputImageButton.addEventListener('click', 
chat_onImageClicked);\n" \
-       //   "    let users = document.createElement('div');\n" \
-       //   "    chat.appendChild(users);\n" \
-       //   "    users.id = 'Users';\n" \
-       //   "    users.addEventListener('click', chat_onUserClicked);\n" \
-       //   "    let allUsers = document.createElement('div');\n" \
-       //   "    users.appendChild(allUsers);\n" \
-       //   "    allUsers.classList.add('selected');\n" \
-       //   "    allUsers.innerText = '<everyone>';\n" \
-       //   "    allUsers.setAttribute('data-user', '0');\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This function creates and connects a WebSocket\n" \
-       //   "  */\n" \
-       //   "  function chat_connect()\n" \
-       //   "  {\n" \
-       //   "    chat_addMessage(`Connecting to libmicrohttpd chat server demo 
(${baseUrl})...`, { type: 'system' });\n" \
-       //   "    socket = new WebSocket(baseUrl);\n" \
-       //   "    socket.binaryType = 'arraybuffer';\n" \
-       //   "    socket.onopen    = socket_onopen;\n" \
-       //   "    socket.onclose   = socket_onclose;\n" \
-       //   "    socket.onerror   = socket_onerror;\n" \
-       //   "    socket.onmessage = socket_onmessage;\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This function adds new text to the chat list\n" \
-       //   "  */\n" \
-       //   "  function chat_addMessage(text, options)\n" \
-       //   "  {\n" \
-       //   "    let type = options && options.type || 'regular';\n" \
-       //   "    
if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
-       //   "      type = 'regular';\n" \
-       //   "    let message = document.createElement('div');\n" \
-       //   "    message.classList.add('Message');\n" \
-       //   "    message.classList.add(type);\n" \
-       //   "    if(typeof(text) === 'string')\n" \
-       //   "    {\n" \
-       //   "      let content = document.createElement('span');\n" \
-       //   "      message.appendChild(content);\n" \
-       //   "      if(options && options.from)\n" \
-       //   "        content.innerText = `${options.from}: ${text}`;\n" \
-       //   "      else\n" \
-       //   "        content.innerText = text;\n" \
-       //   "      if(options && options.reconnect)\n" \
-       //   "      {\n" \
-       //   "        let span = document.createElement('span');\n" \
-       //   "        span.appendChild(document.createTextNode(' ('));\n" \
-       //   "        let reconnect = document.createElement('a');\n" \
-       //   "        reconnect.href = 'javascript:chat_connect()';\n" \
-       //   "        reconnect.innerText = 'reconnect';\n" \
-       //   "        span.appendChild(reconnect);\n" \
-       //   "        span.appendChild(document.createTextNode(')'));\n" \
-       //   "        message.appendChild(span);\n" \
-       //   "      }\n" \
-       //   "    }\n" \
-       //   "    else\n" \
-       //   "    {\n" \
-       //   "      let content = document.createElement('span');\n" \
-       //   "      message.appendChild(content);\n" \
-       //   "      if(options && options.from)\n" \
-       //   "      {\n" \
-       //   "        content.innerText = `${options.from}:\\n`;\n" \
-       //   "      }\n" \
-       //   "      if(options && options.pictureType && text instanceof 
Uint8Array)\n" \
-       //   "      {\n" \
-       //   "        let img = document.createElement('img');\n" \
-       //   "        content.appendChild(img);\n" \
-       //   "        img.src = URL.createObjectURL(new Blob([ text.buffer ], { 
type: options.pictureType }));\n" \
-       //   "      }\n" \
-       //   "    }\n" \
-       //   "    document.getElementById('Messages').appendChild(message);\n" \
-       //   "    message.scrollIntoView();\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is a keydown event handler, which allows that you can 
just press enter instead of clicking the 'send' button\n" \
-       //   "  */\n" \
-       //   "  function chat_onKeyDown(event)\n" \
-       //   "  {\n" \
-       //   "    if(event.key == 'Enter')\n" \
-       //   "      chat_onSendClicked();\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the code to send a message or command, when clicking 
the 'send' button\n" \
-       //   "  */\n" \
-       //   "  function chat_onSendClicked(event)\n" \
-       //   "  {\n" \
-       //   "    let message = 
document.getElementById('InputMessage').value;\n" \
-       //   "    if(message.length == 0)\n" \
-       //   "      return;\n" \
-       //   "    if(message.substr(0, 1) == '/')\n" \
-       //   "    {\n" \
-       //   "      // command\n" \
-       //   "      let match;\n" \
-       //   "      if(/^\\/disconnect\\s*$/.test(message))\n" \
-       //   "      {\n" \
-       //   "        socket.close(1000);\n" \
-       //   "      }\n" \
-       //   "      else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
-       //   "      {\n" \
-       //   "        message = message.substr(match[0].length);\n" \
-       //   "        let userId = chat_getUserIdByName(match[1]);\n" \
-       //   "        if(userId !== null)\n" \
-       //   "        {\n" \
-       //   "          socket.send(`private|${userId}|${message}`);\n" \
-       //   "        }\n" \
-       //   "        else\n" \
-       //   "        {\n" \
-       //   "          chat_addMessage(`Unknown user \"${match[1]}\" for 
private message: ${message}`, { type: 'error' });\n" \
-       //   "        }\n" \
-       //   "      }\n" \
-       //   "      else if((match = 
/^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
-       //   "      {\n" \
-       //   "        let userId = chat_getUserIdByName(match[1]);\n" \
-       //   "        if(userId !== null)\n" \
-       //   "        {\n" \
-       //   "          socket.send(`ping|${userId}|`);\n" \
-       //   "        }\n" \
-       //   "        else\n" \
-       //   "        {\n" \
-       //   "          chat_addMessage(`Unknown user \"${match[1]}\" for 
ping`, { type: 'error' });\n" \
-       //   "        }\n" \
-       //   "      }\n" \
-       //   "      else if((match = 
/^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
-       //   "      {\n" \
-       //   "        socket.send(`name||${match[1]}`);\n" \
-       //   "      }\n" \
-       //   "      else\n" \
-       //   "      {\n" \
-       //   "        chat_addMessage(`Unsupported command or invalid syntax: 
${message}`, { type: 'error' });\n" \
-       //   "      }\n" \
-       //   "    }\n" \
-       //   "    else\n" \
-       //   "    {\n" \
-       //   "      // regular chat message to the selected user\n" \
-       //   "      let selectedUser = document.querySelector('div#Users > 
div.selected');\n" \
-       //   "      let selectedUserId = 
parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
-       //   "      if(selectedUserId == 0)\n" \
-       //   "        socket.send(`||${message}`);\n" \
-       //   "      else\n" \
-       //   "        socket.send(`private|${selectedUserId}|${message}`);\n" \
-       //   "    }\n" \
-       //   "    document.getElementById('InputMessage').value = '';\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the user hits the 'image' button\n" \
-       //   "  */\n" \
-       //   "  function chat_onImageClicked(event)\n" \
-       //   "  {\n" \
-       //   "    document.getElementById('InputImage').click();\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the user selected an image.\n" \
-       //   "    The image will be read with the FileReader (allowed in web, 
because the user selected the file).\n" \
-       //   "  */\n" \
-       //   "  function chat_onImageSelected(event)\n" \
-       //   "  {\n" \
-       //   "    let file = event.target.files[0];\n" \
-       //   "    if(!file || !/^image\\//.test(file.type))\n" \
-       //   "      return;\n" \
-       //   "    let selectedUser = document.querySelector('div#Users > 
div.selected');\n" \
-       //   "    let selectedUserId = 
parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
-       //   "    let reader = new FileReader();\n" \
-       //   "    reader.onload = function(event) {\n" \
-       //   "      chat_onImageRead(event, file.type, selectedUserId);\n" \
-       //   "    };\n" \
-       //   "    reader.readAsArrayBuffer(file);\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the user selected image has been 
read.\n" \
-       //   "    This will add our chat protocol prefix and send it via the 
websocket.\n" \
-       //   "  */\n" \
-       //   "  function chat_onImageRead(event, fileType, selectedUserId)\n" \
-       //   "  {\n" \
-       //   "    let encoder = new TextEncoder();\n" \
-       //   "    let prefix = ((selectedUserId == 0 ? '||' : 
`private|${selectedUserId}|`) + fileType + '|');\n" \
-       //   "    prefix = encoder.encode(prefix);\n" \
-       //   "    let byteData = new Uint8Array(event.target.result);\n" \
-       //   "    let totalLength = prefix.length + byteData.length;\n" \
-       //   "    let resultByteData = new Uint8Array(totalLength);\n" \
-       //   "    resultByteData.set(prefix, 0);\n" \
-       //   "    resultByteData.set(byteData, prefix.length);\n" \
-       //   "    socket.send(resultByteData);\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the user clicked a name in the user 
list.\n" \
-       //   "    This is useful to send private messages or images without 
needing to add the /m prefix.\n" \
-       //   "  */\n" \
-       //   "  function chat_onUserClicked(event, selectedUserId)\n" \
-       //   "  {\n" \
-       //   "    let newSelected = event.target.closest('div#Users > div');\n" 
\
-       //   "    if(newSelected === null)\n" \
-       //   "      return;\n" \
-       //   "    for(let div of this.querySelectorAll(':scope > 
div.selected'))\n" \
-       //   "      div.classList.remove('selected');\n" \
-       //   "    newSelected.classList.add('selected');\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This functions returns the current id of a user identified by 
its name.\n" \
-       //   "  */\n" \
-       //   "  function chat_getUserIdByName(name)\n" \
-       //   "  {\n" \
-       //   "    let nameUpper = name.toUpperCase();\n" \
-       //   "    for(let pair of connectedUsers)\n" \
-       //   "    {\n" \
-       //   "      if(pair[1].toUpperCase() == nameUpper)\n" \
-       //   "        return pair[0];\n" \
-       //   "    }\n" \
-       //   "    return null;\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This functions clears the entire user list (needed for 
reconnecting).\n" \
-       //   "  */\n" \
-       //   "  function chat_clearUserList()\n" \
-       //   "  {\n" \
-       //   "    let users = document.getElementById('Users');\n" \
-       //   "    for(let div of users.querySelectorAll(':scope > div'))\n" \
-       //   "    {\n" \
-       //   "      if(div.getAttribute('data-user') === '0')\n" \
-       //   "      {\n" \
-       //   "        div.classList.add('selected');\n" \
-       //   "      }\n" \
-       //   "      else\n" \
-       //   "      {\n" \
-       //   "        div.parentNode.removeChild(div);\n" \
-       //   "      }\n" \
-       //   "    }\n" \
-       //   "    return null;\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the socket has established a 
connection.\n" \
-       //   "    This will initialize an empty chat and enable the 
controls.\n" \
-       //   "  */\n" \
-       //   "  function socket_onopen(event)\n" \
-       //   "  {\n" \
-       //   "    connectedUsers.clear();\n" \
-       //   "    chat_clearUserList();\n" \
-       //   "    chat_addMessage('Connected!', { type: 'system' });\n" \
-       //   "    document.getElementById('InputMessage').disabled       = 
false;\n" \
-       //   "    document.getElementById('InputMessageButton').disabled = 
false;\n" \
-       //   "    document.getElementById('InputImageButton').disabled   = 
false;\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the socket has been closed.\n" \
-       //   "    This will lock the controls.\n" \
-       //   "  */\n" \
-       //   "  function socket_onclose(event)\n" \
-       //   "  {\n" \
-       //   "    chat_addMessage('Connection closed!', { type: 'system', 
reconnect: true });\n" \
-       //   "    document.getElementById('InputMessage').disabled       = 
true;\n" \
-       //   "    document.getElementById('InputMessageButton').disabled = 
true;\n" \
-       //   "    document.getElementById('InputImageButton').disabled   = 
true;\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the socket reported an error.\n" \
-       //   "    This will just make an output.\n" \
-       //   "    In the web browser console (F12 on many browsers) will show 
you more detailed error information.\n" \
-       //   "  */\n" \
-       //   "  function socket_onerror(event)\n" \
-       //   "  {\n" \
-       //   "    console.error('WebSocket error reported: ', event);\n" \
-       //   "    chat_addMessage('The socket reported an error!', { type: 
'error' });\n" \
-       //   "  }\n" \
-       //   "\n" \
-       //   "  /**\n" \
-       //   "    This is the event when the socket has received a message.\n" \
-       //   "    This will parse the message and execute the corresponding 
command (or add the message).\n" \
-       //   "  */\n" \
-       //   "  function socket_onmessage(event)\n" \
-       //   "  {\n" \
-       //   "    if(typeof(event.data) === 'string')\n" \
-       //   "    {\n" \
-       //   "      // text message or command\n" \
-       //   "      let message = event.data.split('|', 3);\n" \
-       //   "      switch(message[0])\n" \
-       //   "      {\n" \
-       //   "      case 'userinit':\n" \
-       //   "        connectedUsers.set(message[1], message[2]);\n" \
-       //   "        {\n" \
-       //   "          let users = document.getElementById('Users');\n" \
-       //   "          let div = document.createElement('div');\n" \
-       //   "          users.appendChild(div);\n" \
-       //   "          div.innerText = message[2];\n" \
-       //   "          div.setAttribute('data-user', message[1]);\n" \
-       //   "        }\n" \
-       //   "        break;\n" \
-       //   "      case 'useradd':\n" \
-       //   "        connectedUsers.set(message[1], message[2]);\n" \
-       //   "        chat_addMessage(`The user '${message[2]}' has joined our 
lovely chatroom.`, { type: 'moderator' });\n" \
-       //   "        {\n" \
-       //   "          let users = document.getElementById('Users');\n" \
-       //   "          let div = document.createElement('div');\n" \
-       //   "          users.appendChild(div);\n" \
-       //   "          div.innerText = message[2];\n" \
-       //   "          div.setAttribute('data-user', message[1]);\n" \
-       //   "        }\n" \
-       //   "        break;\n" \
-       //   "      case 'userdel':\n" \
-       //   "        chat_addMessage(`The user 
'${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, 
{ type: 'moderator' });\n" \
-       //   "        connectedUsers.delete(message[1]);\n" \
-       //   "        {\n" \
-       //   "          let users = document.getElementById('Users');\n" \
-       //   "          let div = 
users.querySelector(`div[data-user='${message[1]}']`);\n" \
-       //   "          if(div !== null)\n" \
-       //   "          {\n" \
-       //   "            users.removeChild(div);\n" \
-       //   "            if(div.classList.contains('selected'))\n" \
-       //   "              
users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
-       //   "          }\n" \
-       //   "        }\n" \
-       //   "        break;\n" \
-       //   "      case 'username':\n" \
-       //   "        chat_addMessage(`The user 
'${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, 
{ type: 'moderator' });\n" \
-       //   "        connectedUsers.set(message[1], message[2]);\n" \
-       //   "        {\n" \
-       //   "          let users = document.getElementById('Users');\n" \
-       //   "          let div = 
users.querySelector(`div[data-user='${message[1]}']`);\n" \
-       //   "          if(div !== null)\n" \
-       //   "          {\n" \
-       //   "            div.innerText = message[2];\n" \
-       //   "          }\n" \
-       //   "        }\n" \
-       //   "        break;\n" \
-       //   "      case 'ping':\n" \
-       //   "        chat_addMessage(`The user 
'${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 
'moderator' });\n" \
-       //   "        break;\n" \
-       //   "      default:\n" \
-       //   "        chat_addMessage(message[2], { type: message[0], from: 
connectedUsers.get(message[1]) });\n" \
-       //   "        break;\n" \
-       //   "      }\n" \
-       //   "    }\n" \
-       //   "    else\n" \
-       //   "    {\n" \
-       //   "      // We received a binary frame, which means a picture 
here\n" \
-       //   "      let byteData = new Uint8Array(event.data);\n" \
-       //   "      let decoder = new TextDecoder();\n" \
-       //   "      let message  = [ ];\n" \
-       //   "      // message type\n" \
-       //   "      let j = 0;\n" \
-       //   "      let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \
-       //   "      if(i < 0)\n" \
-       //   "        return;\n" \
-       //   "      message.push(decoder.decode(byteData.slice(0, i)));\n" \
-       //   "      // picture from\n" \
-       //   "      j = i + 1;\n" \
-       //   "      i = byteData.indexOf(0x7C, j);\n" \
-       //   "      if(i < 0)\n" \
-       //   "        return;\n" \
-       //   "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
-       //   "      // picture encoding\n" \
-       //   "      j = i + 1;\n" \
-       //   "      i = byteData.indexOf(0x7C, j);\n" \
-       //   "      if(i < 0)\n" \
-       //   "        return;\n" \
-       //   "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
-       //   "      // picture\n" \
-       //   "      byteData = byteData.slice(i + 1);\n" \
-       //   "      chat_addMessage(byteData, { type: message[0], from: 
connectedUsers.get(message[1]), pictureType: message[2] });\n" \
-       //   "    }\n" \
-       //   "  }\n" \
-       //   "</script>" \
-       //   "</head>" \
-       //   "<body><noscript>Please enable JavaScript to test the 
libmicrohttpd Websocket chatserver demo!</noscript></body>" \
-       //   "</html>"
+  "    baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + 
'://' + window.location.host + '/ChatServerWebSocket';\n" \
+  "    chat_generate();\n" \
+  "    chat_connect();\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This function generates the chat using DOM\n" \
+  "  */\n" \
+  "  function chat_generate()\n" \
+  "  {\n" \
+  "    document.body.innerHTML = '';\n" \
+  "    let chat = document.createElement('div');\n" \
+  "    document.body.appendChild(chat);\n" \
+  "    chat.id = 'Chat';\n" \
+  "    let messagesAndInput = document.createElement('div');\n" \
+  "    chat.appendChild(messagesAndInput);\n" \
+  "    messagesAndInput.classList.add('MessagesAndInput');\n" \
+  "    let messages = document.createElement('div');\n" \
+  "    messagesAndInput.appendChild(messages);\n" \
+  "    messages.id = 'Messages';\n" \
+  "    let input = document.createElement('div');\n" \
+  "    messagesAndInput.appendChild(input);\n" \
+  "    input.classList.add('Input');\n" \
+  "    let inputMessage = document.createElement('input');\n" \
+  "    input.appendChild(inputMessage);\n" \
+  "    inputMessage.type = 'text';\n" \
+  "    inputMessage.id = 'InputMessage';\n" \
+  "    inputMessage.disabled = true;\n" \
+  "    inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
+  "    let inputMessageSend = document.createElement('button');\n" \
+  "    input.appendChild(inputMessageSend);\n" \
+  "    inputMessageSend.id = 'InputMessageButton';\n" \
+  "    inputMessageSend.disabled = true;\n" \
+  "    inputMessageSend.innerText = 'send';\n" \
+  "    inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \
+  "    let inputImage = document.createElement('input');\n" \
+  "    input.appendChild(inputImage);\n" \
+  "    inputImage.id = 'InputImage';\n" \
+  "    inputImage.type = 'file';\n" \
+  "    inputImage.accept = 'image/*';\n" \
+  "    inputImage.style.display = 'none';\n" \
+  "    inputImage.addEventListener('change', chat_onImageSelected);\n" \
+  "    let inputImageButton = document.createElement('button');\n" \
+  "    input.appendChild(inputImageButton);\n" \
+  "    inputImageButton.id = 'InputImageButton';\n" \
+  "    inputImageButton.disabled = true;\n" \
+  "    inputImageButton.innerText = 'image';\n" \
+  "    inputImageButton.addEventListener('click', chat_onImageClicked);\n" \
+  "    let users = document.createElement('div');\n" \
+  "    chat.appendChild(users);\n" \
+  "    users.id = 'Users';\n" \
+  "    users.addEventListener('click', chat_onUserClicked);\n" \
+  "    let allUsers = document.createElement('div');\n" \
+  "    users.appendChild(allUsers);\n" \
+  "    allUsers.classList.add('selected');\n" \
+  "    allUsers.innerText = '<everyone>';\n" \
+  "    allUsers.setAttribute('data-user', '0');\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This function creates and connects a WebSocket\n" \
+  "  */\n" \
+  "  function chat_connect()\n" \
+  "  {\n" \
+  "    chat_addMessage(`Connecting to libmicrohttpd chat server demo 
(${baseUrl})...`, { type: 'system' });\n" \
+  "    socket = new WebSocket(baseUrl);\n" \
+  "    socket.binaryType = 'arraybuffer';\n" \
+  "    socket.onopen    = socket_onopen;\n" \
+  "    socket.onclose   = socket_onclose;\n" \
+  "    socket.onerror   = socket_onerror;\n" \
+  "    socket.onmessage = socket_onmessage;\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This function adds new text to the chat list\n" \
+  "  */\n" \
+  "  function chat_addMessage(text, options)\n" \
+  "  {\n" \
+  "    let type = options && options.type || 'regular';\n" \
+  "    if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
+  "      type = 'regular';\n" \
+  "    let message = document.createElement('div');\n" \
+  "    message.classList.add('Message');\n" \
+  "    message.classList.add(type);\n" \
+  "    if(typeof(text) === 'string')\n" \
+  "    {\n" \
+  "      let content = document.createElement('span');\n" \
+  "      message.appendChild(content);\n" \
+  "      if(options && options.from)\n" \
+  "        content.innerText = `${options.from}: ${text}`;\n" \
+  "      else\n" \
+  "        content.innerText = text;\n" \
+  "      if(options && options.reconnect)\n" \
+  "      {\n" \
+  "        let span = document.createElement('span');\n" \
+  "        span.appendChild(document.createTextNode(' ('));\n" \
+  "        let reconnect = document.createElement('a');\n" \
+  "        reconnect.href = 'javascript:chat_connect()';\n" \
+  "        reconnect.innerText = 'reconnect';\n" \
+  "        span.appendChild(reconnect);\n" \
+  "        span.appendChild(document.createTextNode(')'));\n" \
+  "        message.appendChild(span);\n" \
+  "      }\n" \
+  "    }\n" \
+  "    else\n" \
+  "    {\n" \
+  "      let content = document.createElement('span');\n" \
+  "      message.appendChild(content);\n" \
+  "      if(options && options.from)\n" \
+  "      {\n" \
+  "        content.innerText = `${options.from}:\\n`;\n" \
+  "      }\n" \
+  "      if(options && options.pictureType && text instanceof Uint8Array)\n" \
+  "      {\n" \
+  "        let img = document.createElement('img');\n" \
+  "        content.appendChild(img);\n" \
+  "        img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: 
options.pictureType }));\n" \
+  "      }\n" \
+  "    }\n" \
+  "    document.getElementById('Messages').appendChild(message);\n" \
+  "    message.scrollIntoView();\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is a keydown event handler, which allows that you can just press 
enter instead of clicking the 'send' button\n" \
+  "  */\n" \
+  "  function chat_onKeyDown(event)\n" \
+  "  {\n" \
+  "    if(event.key == 'Enter')\n" \
+  "      chat_onSendClicked();\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the code to send a message or command, when clicking the 'send' 
button\n" \
+  "  */\n" \
+  "  function chat_onSendClicked(event)\n" \
+  "  {\n" \
+  "    let message = document.getElementById('InputMessage').value;\n" \
+  "    if(message.length == 0)\n" \
+  "      return;\n" \
+  "    if(message.substr(0, 1) == '/')\n" \
+  "    {\n" \
+  "      // command\n" \
+  "      let match;\n" \
+  "      if(/^\\/disconnect\\s*$/.test(message))\n" \
+  "      {\n" \
+  "        socket.close(1000);\n" \
+  "      }\n" \
+  "      else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
+  "      {\n" \
+  "        message = message.substr(match[0].length);\n" \
+  "        let userId = chat_getUserIdByName(match[1]);\n" \
+  "        if(userId !== null)\n" \
+  "        {\n" \
+  "          socket.send(`private|${userId}|${message}`);\n" \
+  "        }\n" \
+  "        else\n" \
+  "        {\n" \
+  "          chat_addMessage(`Unknown user \"${match[1]}\" for private 
message: ${message}`, { type: 'error' });\n" \
+  "        }\n" \
+  "      }\n" \
+  "      else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
+  "      {\n" \
+  "        let userId = chat_getUserIdByName(match[1]);\n" \
+  "        if(userId !== null)\n" \
+  "        {\n" \
+  "          socket.send(`ping|${userId}|`);\n" \
+  "        }\n" \
+  "        else\n" \
+  "        {\n" \
+  "          chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 
'error' });\n" \
+  "        }\n" \
+  "      }\n" \
+  "      else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
+  "      {\n" \
+  "        socket.send(`name||${match[1]}`);\n" \
+  "      }\n" \
+  "      else\n" \
+  "      {\n" \
+  "        chat_addMessage(`Unsupported command or invalid syntax: 
${message}`, { type: 'error' });\n" \
+  "      }\n" \
+  "    }\n" \
+  "    else\n" \
+  "    {\n" \
+  "      // regular chat message to the selected user\n" \
+  "      let selectedUser = document.querySelector('div#Users > 
div.selected');\n" \
+  "      let selectedUserId = parseInt(selectedUser.getAttribute('data-user') 
|| '0', 10);\n" \
+  "      if(selectedUserId == 0)\n" \
+  "        socket.send(`||${message}`);\n" \
+  "      else\n" \
+  "        socket.send(`private|${selectedUserId}|${message}`);\n" \
+  "    }\n" \
+  "    document.getElementById('InputMessage').value = '';\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the user hits the 'image' button\n" \
+  "  */\n" \
+  "  function chat_onImageClicked(event)\n" \
+  "  {\n" \
+  "    document.getElementById('InputImage').click();\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the user selected an image.\n" \
+  "    The image will be read with the FileReader (allowed in web, because the 
user selected the file).\n" \
+  "  */\n" \
+  "  function chat_onImageSelected(event)\n" \
+  "  {\n" \
+  "    let file = event.target.files[0];\n" \
+  "    if(!file || !/^image\\//.test(file.type))\n" \
+  "      return;\n" \
+  "    let selectedUser = document.querySelector('div#Users > 
div.selected');\n" \
+  "    let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || 
'0', 10);\n" \
+  "    let reader = new FileReader();\n" \
+  "    reader.onload = function(event) {\n" \
+  "      chat_onImageRead(event, file.type, selectedUserId);\n" \
+  "    };\n" \
+  "    reader.readAsArrayBuffer(file);\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the user selected image has been read.\n" \
+  "    This will add our chat protocol prefix and send it via the 
websocket.\n" \
+  "  */\n" \
+  "  function chat_onImageRead(event, fileType, selectedUserId)\n" \
+  "  {\n" \
+  "    let encoder = new TextEncoder();\n" \
+  "    let prefix = ((selectedUserId == 0 ? '||' : 
`private|${selectedUserId}|`) + fileType + '|');\n" \
+  "    prefix = encoder.encode(prefix);\n" \
+  "    let byteData = new Uint8Array(event.target.result);\n" \
+  "    let totalLength = prefix.length + byteData.length;\n" \
+  "    let resultByteData = new Uint8Array(totalLength);\n" \
+  "    resultByteData.set(prefix, 0);\n" \
+  "    resultByteData.set(byteData, prefix.length);\n" \
+  "    socket.send(resultByteData);\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the user clicked a name in the user list.\n" \
+  "    This is useful to send private messages or images without needing to 
add the /m prefix.\n" \
+  "  */\n" \
+  "  function chat_onUserClicked(event, selectedUserId)\n" \
+  "  {\n" \
+  "    let newSelected = event.target.closest('div#Users > div');\n" \
+  "    if(newSelected === null)\n" \
+  "      return;\n" \
+  "    for(let div of this.querySelectorAll(':scope > div.selected'))\n" \
+  "      div.classList.remove('selected');\n" \
+  "    newSelected.classList.add('selected');\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This functions returns the current id of a user identified by its 
name.\n" \
+  "  */\n" \
+  "  function chat_getUserIdByName(name)\n" \
+  "  {\n" \
+  "    let nameUpper = name.toUpperCase();\n" \
+  "    for(let pair of connectedUsers)\n" \
+  "    {\n" \
+  "      if(pair[1].toUpperCase() == nameUpper)\n" \
+  "        return pair[0];\n" \
+  "    }\n" \
+  "    return null;\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This functions clears the entire user list (needed for 
reconnecting).\n" \
+  "  */\n" \
+  "  function chat_clearUserList()\n" \
+  "  {\n" \
+  "    let users = document.getElementById('Users');\n" \
+  "    for(let div of users.querySelectorAll(':scope > div'))\n" \
+  "    {\n" \
+  "      if(div.getAttribute('data-user') === '0')\n" \
+  "      {\n" \
+  "        div.classList.add('selected');\n" \
+  "      }\n" \
+  "      else\n" \
+  "      {\n" \
+  "        div.parentNode.removeChild(div);\n" \
+  "      }\n" \
+  "    }\n" \
+  "    return null;\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the socket has established a connection.\n" \
+  "    This will initialize an empty chat and enable the controls.\n" \
+  "  */\n" \
+  "  function socket_onopen(event)\n" \
+  "  {\n" \
+  "    connectedUsers.clear();\n" \
+  "    chat_clearUserList();\n" \
+  "    chat_addMessage('Connected!', { type: 'system' });\n" \
+  "    document.getElementById('InputMessage').disabled       = false;\n" \
+  "    document.getElementById('InputMessageButton').disabled = false;\n" \
+  "    document.getElementById('InputImageButton').disabled   = false;\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the socket has been closed.\n" \
+  "    This will lock the controls.\n" \
+  "  */\n" \
+  "  function socket_onclose(event)\n" \
+  "  {\n" \
+  "    chat_addMessage('Connection closed!', { type: 'system', reconnect: true 
});\n" \
+  "    document.getElementById('InputMessage').disabled       = true;\n" \
+  "    document.getElementById('InputMessageButton').disabled = true;\n" \
+  "    document.getElementById('InputImageButton').disabled   = true;\n" \
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the socket reported an error.\n" \
+  "    This will just make an output.\n" \
+  "    In the web browser console (F12 on many browsers) will show you more 
detailed error information.\n" \
+  "  */\n" \
+  "  function socket_onerror(event)\n" \
+  "  {\n" \
+  "    console.error('WebSocket error reported: ', event);\n" \
+  "    chat_addMessage('The socket reported an error!', { type: 'error' });\n" 
\
+  "  }\n" \
+  "\n" \
+  "  /**\n" \
+  "    This is the event when the socket has received a message.\n" \
+  "    This will parse the message and execute the corresponding command (or 
add the message).\n" \
+  "  */\n" \
+  "  function socket_onmessage(event)\n" \
+  "  {\n" \
+  "    if(typeof(event.data) === 'string')\n" \
+  "    {\n" \
+  "      // text message or command\n" \
+  "      let message = event.data.split('|', 3);\n" \
+  "      switch(message[0])\n" \
+  "      {\n" \
+  "      case 'userinit':\n" \
+  "        connectedUsers.set(message[1], message[2]);\n" \
+  "        {\n" \
+  "          let users = document.getElementById('Users');\n" \
+  "          let div = document.createElement('div');\n" \
+  "          users.appendChild(div);\n" \
+  "          div.innerText = message[2];\n" \
+  "          div.setAttribute('data-user', message[1]);\n" \
+  "        }\n" \
+  "        break;\n" \
+  "      case 'useradd':\n" \
+  "        connectedUsers.set(message[1], message[2]);\n" \
+  "        chat_addMessage(`The user '${message[2]}' has joined our lovely 
chatroom.`, { type: 'moderator' });\n" \
+  "        {\n" \
+  "          let users = document.getElementById('Users');\n" \
+  "          let div = document.createElement('div');\n" \
+  "          users.appendChild(div);\n" \
+  "          div.innerText = message[2];\n" \
+  "          div.setAttribute('data-user', message[1]);\n" \
+  "        }\n" \
+  "        break;\n" \
+  "      case 'userdel':\n" \
+  "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has 
left our chatroom. We will miss you.`, { type: 'moderator' });\n" \
+  "        connectedUsers.delete(message[1]);\n" \
+  "        {\n" \
+  "          let users = document.getElementById('Users');\n" \
+  "          let div = 
users.querySelector(`div[data-user='${message[1]}']`);\n" \
+  "          if(div !== null)\n" \
+  "          {\n" \
+  "            users.removeChild(div);\n" \
+  "            if(div.classList.contains('selected'))\n" \
+  "              
users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
+  "          }\n" \
+  "        }\n" \
+  "        break;\n" \
+  "      case 'username':\n" \
+  "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has 
changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \
+  "        connectedUsers.set(message[1], message[2]);\n" \
+  "        {\n" \
+  "          let users = document.getElementById('Users');\n" \
+  "          let div = 
users.querySelector(`div[data-user='${message[1]}']`);\n" \
+  "          if(div !== null)\n" \
+  "          {\n" \
+  "            div.innerText = message[2];\n" \
+  "          }\n" \
+  "        }\n" \
+  "        break;\n" \
+  "      case 'ping':\n" \
+  "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a 
ping of ${message[2]} ms.`, { type: 'moderator' });\n" \
+  "        break;\n" \
+  "      default:\n" \
+  "        chat_addMessage(message[2], { type: message[0], from: 
connectedUsers.get(message[1]) });\n" \
+  "        break;\n" \
+  "      }\n" \
+  "    }\n" \
+  "    else\n" \
+  "    {\n" \
+  "      // We received a binary frame, which means a picture here\n" \
+  "      let byteData = new Uint8Array(event.data);\n" \
+  "      let decoder = new TextDecoder();\n" \
+  "      let message  = [ ];\n" \
+  "      // message type\n" \
+  "      let j = 0;\n" \
+  "      let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \
+  "      if(i < 0)\n" \
+  "        return;\n" \
+  "      message.push(decoder.decode(byteData.slice(0, i)));\n" \
+  "      // picture from\n" \
+  "      j = i + 1;\n" \
+  "      i = byteData.indexOf(0x7C, j);\n" \
+  "      if(i < 0)\n" \
+  "        return;\n" \
+  "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
+  "      // picture encoding\n" \
+  "      j = i + 1;\n" \
+  "      i = byteData.indexOf(0x7C, j);\n" \
+  "      if(i < 0)\n" \
+  "        return;\n" \
+  "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
+  "      // picture\n" \
+  "      byteData = byteData.slice(i + 1);\n" \
+  "      chat_addMessage(byteData, { type: message[0], from: 
connectedUsers.get(message[1]), pictureType: message[2] });\n" \
+  "    }\n" \
+  "  }\n" \
+  "</script>" \
+  "</head>" \
+  "<body><noscript>Please enable JavaScript to test the libmicrohttpd 
Websocket chatserver demo!</noscript></body>" \
+  "</html>"
 
 #define PAGE_NOT_FOUND \
   "404 Not Found"
@@ -708,7 +714,7 @@ make_blocking (MHD_socket fd)
 #elif defined(MHD_WINSOCK_SOCKETS)
   unsigned long flags = 0;
 
-  ioctlsocket (fd, (int) FIONBIO, &flags);
+  ioctlsocket (fd, FIONBIO, &flags);
 #endif /* MHD_WINSOCK_SOCKETS */
 
 }
@@ -739,7 +745,6 @@ send_all (struct ConnectedUser*cu,
                   0);
       if (0 > ret)
       {
-        int err = errno;
         if (EAGAIN == errno)
         {
           ret = 0;
@@ -927,7 +932,7 @@ chat_adduser (struct ConnectedUser*cu)
 {
   /* initialize the notification message of the new user */
   char user_index[32];
-  itoa ((int) cu->user_id, user_index, 10);
+  snprintf (user_index, 32, "%d", (int) cu->user_id);
   size_t user_index_len = strlen (user_index);
   size_t data_len = user_index_len + cu->user_name_len + 9;
   char*data = (char*) malloc (data_len + 1);
@@ -998,7 +1003,7 @@ chat_removeuser (struct ConnectedUser*cu)
   char user_index[32];
 
   /* initialize the chat message for the removed user */
-  itoa ((int) cu->user_id, user_index, 10);
+  snprintf (user_index, 32, "%d", (int) cu->user_id);
   size_t user_index_len = strlen (user_index);
   size_t data_len = user_index_len + 9;
   char*data = (char*) malloc (data_len + 1);
@@ -1071,9 +1076,8 @@ chat_renameuser (struct ConnectedUser*cu,
   {
     if (cu != users[i])
     {
-      if ((users[i]->user_name_len == new_name_len) && (0 == stricmp (
-                                                          users[i]->user_name,
-                                                          new_name)))
+      if ((users[i]->user_name_len == new_name_len) &&
+          (0 == strcasecmp (users[i]->user_name, new_name)))
       {
         pthread_mutex_unlock (&chat_mutex);
         return 2;
@@ -1083,7 +1087,7 @@ chat_renameuser (struct ConnectedUser*cu,
 
   /* generate the notification message */
   char user_index[32];
-  itoa ((int) cu->user_id, user_index, 10);
+  snprintf (user_index, 32, "%d", (int) cu->user_id);
   size_t user_index_len = strlen (user_index);
   size_t data_len = user_index_len + new_name_len + 10;
   char*data = (char*) malloc (data_len + 1);
@@ -1202,17 +1206,17 @@ connecteduser_parse_received_websocket_stream (struct 
ConnectedUser*cu,
                 /* no command means regular message */
                 command = 0;
               }
-              else if (0 == stricmp (frame_data, "private"))
+              else if (0 == strcasecmp (frame_data, "private"))
               {
                 /* private means private message */
                 command = 1;
               }
-              else if (0 == stricmp (frame_data, "name"))
+              else if (0 == strcasecmp (frame_data, "name"))
               {
                 /* name means chat user rename */
                 command = 2;
               }
-              else if (0 == stricmp (frame_data, "ping"))
+              else if (0 == strcasecmp (frame_data, "ping"))
               {
                 /* ping means a ping request */
                 command = 3;
@@ -1263,7 +1267,7 @@ connecteduser_parse_received_websocket_stream (struct 
ConnectedUser*cu,
                   * This is useful for debugging with an IDE.
                   */
                   char user_index[32];
-                  itoa ((int) from_user_id, user_index, 10);
+                  snprintf (user_index, 32, "%d", (int) cu->user_id);
                   size_t user_index_len = strlen (user_index);
                   size_t data_len = user_index_len + frame_len - j + 9;
                   char*data = (char*) malloc (data_len + 1);
@@ -1300,7 +1304,7 @@ connecteduser_parse_received_websocket_stream (struct 
ConnectedUser*cu,
                    * The difference is the prefix "private"
                   */
                   char user_index[32];
-                  itoa ((int) from_user_id, user_index, 10);
+                  snprintf (user_index, 32, "%d", (int) cu->user_id);
                   size_t user_index_len = strlen (user_index);
                   size_t data_len = user_index_len + frame_len - j + 9;
                   char*data = (char*) malloc (data_len + 1);
@@ -1538,14 +1542,10 @@ connecteduser_parse_received_websocket_stream (struct 
ConnectedUser*cu,
               char result_text[240];
               strcpy (result_text,
                       "ping|");
-              itoa ((int) cu->user_id,
-                    result_text + 5,
-                    10);
+              snprintf (result_text + 5, 235, "%d", (int) cu->user_id);
               strcat (result_text,
                       "|");
-              itoa (ping,
-                    result_text + strlen (result_text),
-                    10);
+              snprintf (result_text + strlen (result_text), 240 - strlen 
(result_text), "%d", (int) ping);
               chat_addmessage (0,
                                0,
                                result_text,
@@ -1627,9 +1627,7 @@ connecteduser_send_messages (void*cls)
           ++cu->ping_counter;
           strcpy (cu->ping_message,
                   "libmicrohttpdchatserverpingdata");
-          itoa (cu->ping_counter,
-                cu->ping_message + 31,
-                10);
+          snprintf (cu->ping_message + 31, 97, "%d", (int) cu->ping_counter);
           cu->ping_message_len = strlen (cu->ping_message);
           char*frame_data = NULL;
           size_t frame_len = 0;
@@ -1750,7 +1748,7 @@ connecteduser_receive_messages (void *cls)
   {
     char user_name[32];
     strcpy (user_name, "User");
-    itoa ((int) cu->user_id, user_name + 4, 10);
+    snprintf (user_name + 4, 28, "%d", (int) cu->user_id);
     cu->user_name_len = strlen (user_name);
     cu->user_name = malloc (cu->user_name_len + 1);
     if (NULL == cu->user_name)
@@ -1831,7 +1829,7 @@ connecteduser_receive_messages (void *cls)
           for (size_t i = 0; i < user_count; ++i)
           {
             char user_index[32];
-            itoa ((int) users[i]->user_id, user_index, 10);
+            snprintf (user_index, 32, "%d", (int) users[i]->user_id);
             size_t user_index_len = strlen (user_index);
             struct UserInit iu;
             iu.user_init_len = user_index_len + users[i]->user_name_len + 10;
@@ -1891,15 +1889,16 @@ connecteduser_receive_messages (void *cls)
                               "  /ping <user> - sends a ping to the specified 
user\n" \
                               "  /name <name> - changes your name to the 
specified name\n" \
                               "  /disconnect - disconnects your websocket\n\n" 
\
-                              "All messages, which does not start a slash, are 
regular messages, which will be sent to selected user.\n\n" \
+                              "All messages, which does not start with a 
slash, " \
+                              "are regular messages and will be sent to the 
selected user.\n\n" \
                               "Have fun!";
-    int r = MHD_websocket_encode_text (cu->ws,
-                                       welcome_msg,
-                                       strlen (welcome_msg),
-                                       MHD_WEBSOCKET_FRAGMENTATION_NONE,
-                                       &frame_data,
-                                       &frame_len,
-                                       NULL);
+    MHD_websocket_encode_text (cu->ws,
+                               welcome_msg,
+                               strlen (welcome_msg),
+                               MHD_WEBSOCKET_FRAGMENTATION_NONE,
+                               &frame_data,
+                               &frame_len,
+                               NULL);
     send_all (cu,
               frame_data,
               frame_len);
@@ -2132,7 +2131,7 @@ upgrade_handler (void *cls,
  * @param ptr A pointer for request specific data
  * @return MHD_YES on success or MHD_NO on error.
  */
-static int
+static enum MHD_Result
 access_handler (void *cls,
                 struct MHD_Connection *connection,
                 const char *url,
@@ -2185,118 +2184,88 @@ access_handler (void *cls,
      * Furthermore it must be a HTTP/1.1 or higher GET request.
      * See: https://tools.ietf.org/html/rfc6455#section-4.2.1
      *
-     * To ease this example we skip the following checks:
-     * - Whether the HTTP version is 1.1 or newer
-     * - Whether Connection is Upgrade, because this header may
-     *   contain multiple values.
-     * - The requested Host (because we don't know)
+     * To make this example portable we skip the Host check
      */
 
+    char is_valid = 1;
+    const char* value = NULL;
+    char sec_websocket_accept[29];
+
     /* check whether an websocket upgrade is requested */
-    const char*value = MHD_lookup_connection_value (connection,
-                                                    MHD_HEADER_KIND,
-                                                    MHD_HTTP_HEADER_UPGRADE);
-    if ((0 == value) || (0 != stricmp (value, "websocket")))
+    if (0 != MHD_websocket_check_http_version (version))
     {
-      struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
-                                                                        
PAGE_INVALID_WEBSOCKET_REQUEST),
-                                                                      
PAGE_INVALID_WEBSOCKET_REQUEST,
-                                                                      
MHD_RESPMEM_PERSISTENT);
-      ret = MHD_queue_response (connection,
-                                MHD_HTTP_BAD_REQUEST,
-                                response);
-      MHD_destroy_response (response);
-      return ret;
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_CONNECTION);
+    if (0 != MHD_websocket_check_connection_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_UPGRADE);
+    if (0 != MHD_websocket_check_upgrade_header (value))
+    {
+      is_valid = 0;
     }
-
-    /* check the protocol version */
     value = MHD_lookup_connection_value (connection,
                                          MHD_HEADER_KIND,
                                          
MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
-    if ((0 == value) || (0 != stricmp (value, "13")))
+    if (0 != MHD_websocket_check_version_header (value))
     {
-      struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
-                                                                        
PAGE_INVALID_WEBSOCKET_REQUEST),
-                                                                      
PAGE_INVALID_WEBSOCKET_REQUEST,
-                                                                      
MHD_RESPMEM_PERSISTENT);
-      ret = MHD_queue_response (connection,
-                                MHD_HTTP_BAD_REQUEST,
-                                response);
-      MHD_destroy_response (response);
-      return ret;
+      is_valid = 0;
     }
-
-    /* read the websocket key (required for the response) */
-    value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
                                          MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
-    if (0 == value)
+    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
     {
-      struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
-                                                                        
PAGE_INVALID_WEBSOCKET_REQUEST),
-                                                                      
PAGE_INVALID_WEBSOCKET_REQUEST,
-                                                                      
MHD_RESPMEM_PERSISTENT);
-      ret = MHD_queue_response (connection,
-                                MHD_HTTP_BAD_REQUEST,
-                                response);
-      MHD_destroy_response (response);
-      return ret;
+      is_valid = 0;
     }
 
-    /* generate the response accept header */
-    char sec_websocket_accept[29];
-    if (0 != MHD_websocket_create_accept (value, sec_websocket_accept))
+    if (1 == is_valid)
     {
-      struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
-                                                                        
PAGE_INVALID_WEBSOCKET_REQUEST),
-                                                                      
PAGE_INVALID_WEBSOCKET_REQUEST,
-                                                                      
MHD_RESPMEM_PERSISTENT);
+      /* create the response for upgrade */
+      response = MHD_create_response_for_upgrade (&upgrade_handler,
+                                                  NULL);
+
+      /**
+       * For the response we need at least the following headers:
+       * 1. "Connection: Upgrade"
+       * 2. "Upgrade: websocket"
+       * 3. "Sec-WebSocket-Accept: <base64value>"
+       * The value for Sec-WebSocket-Accept can be generated with 
MHD_websocket_create_accept_header.
+       * It requires the value of the Sec-WebSocket-Key header of the request.
+       * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
+       */
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_CONNECTION,
+                               "Upgrade");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_UPGRADE,
+                               "websocket");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+                               sec_websocket_accept);
       ret = MHD_queue_response (connection,
-                                MHD_HTTP_BAD_REQUEST,
+                                MHD_HTTP_SWITCHING_PROTOCOLS,
                                 response);
       MHD_destroy_response (response);
-      return ret;
     }
-
-    /* only for this example: don't accept incoming connection when we are 
already shutting down */
-    if (0 != disconnect_all)
+    else
     {
+      /* return error page */
       struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
                                                                         
PAGE_INVALID_WEBSOCKET_REQUEST),
                                                                       
PAGE_INVALID_WEBSOCKET_REQUEST,
                                                                       
MHD_RESPMEM_PERSISTENT);
       ret = MHD_queue_response (connection,
-                                MHD_HTTP_SERVICE_UNAVAILABLE,
+                                MHD_HTTP_BAD_REQUEST,
                                 response);
       MHD_destroy_response (response);
-      return ret;
     }
-
-    /* create the response for upgrade */
-    response = MHD_create_response_for_upgrade (&upgrade_handler,
-                                                NULL);
-
-    /**
-     * For the response we need at least the following headers:
-     * 1. "Connection: Upgrade"
-     * 2. "Upgrade: websocket"
-     * 3. "Sec-WebSocket-Accept: <base64value>"
-     * The value for Sec-WebSocket-Accept can be generated with 
MHD_websocket_create_accept.
-     * It requires the value of the Sec-WebSocket-Key header of the request.
-     * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
-     */
-    MHD_add_response_header (response,
-                             MHD_HTTP_HEADER_CONNECTION,
-                             "Upgrade");
-    MHD_add_response_header (response,
-                             MHD_HTTP_HEADER_UPGRADE,
-                             "websocket");
-    MHD_add_response_header (response,
-                             MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
-                             sec_websocket_accept);
-    ret = MHD_queue_response (connection,
-                              MHD_HTTP_SWITCHING_PROTOCOLS,
-                              response);
-    MHD_destroy_response (response);
   }
   else
   {
@@ -2353,7 +2322,7 @@ main (int argc,
                         MHD_OPTION_END);
 #endif
 
-  if (d == NULL)
+  if (NULL == d)
     return 1;
   (void) getc (stdin);
 
diff --git a/src/include/microhttpd_ws.h b/src/include/microhttpd_ws.h
index bfbd550a..f19c140d 100644
--- a/src/include/microhttpd_ws.h
+++ b/src/include/microhttpd_ws.h
@@ -51,29 +51,27 @@ struct MHD_WebSocketStream;
 enum MHD_WEBSOCKET_FLAG
 {
   /**
-   * The websocket is used by the server (default).
+   * The websocket stream is initialized in server mode (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
+   * This flag 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).
+   * The websocket stream is initialized in client mode.
+   * You will usually never use that mode in combination with libmicrohttpd,
+   * because libmicrohttpd provides a server and not a client.
+   * In client mode 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 implementation 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
+   * If you use this mode, you must always call #MHD_websocket_stream_init2()
+   * instead of #MHD_websocket_stream_init(), because you need
+   * to pass a random number generator callback function for masking.
+   * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER
    */
   MHD_WEBSOCKET_FLAG_CLIENT = 1,
   /**
-   * You don't want to get fragmented data while decoding.
+   * You don't want to get fragmented data while decoding (default).
    * Fragmented frames will be internally put together until
    * they are complete.
    * Whether or not data is fragmented is decided
@@ -85,11 +83,11 @@ enum MHD_WEBSOCKET_FLAG
    * 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).
+   * UTF-8 sequence (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
+   * This cannot be used together with #MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
    */
   MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS = 2,
   /**
@@ -97,7 +95,7 @@ enum MHD_WEBSOCKET_FLAG
    * 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
+   * `payload` and `payload_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
@@ -140,7 +138,7 @@ enum MHD_WEBSOCKET_FRAGMENTATION
    * 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.
+   * After this frame, you may send all types of frames again.
    */
   MHD_WEBSOCKET_FRAGMENTATION_LAST = 3
 };
@@ -157,18 +155,19 @@ 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.
+   * For other functions this means simply a success.
    */
   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).
+   * The parameters `payload` and `payload_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).
+   * The parameters `payload` and `payload_len` are filled with
+   * the decoded binary data (if any).
    */
   MHD_WEBSOCKET_STATUS_BINARY_FRAME = 0x2,
   /**
@@ -176,12 +175,13 @@ enum MHD_WEBSOCKET_STATUS
    * 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 parameters `payload` and `payload_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.
+   * You can call #MHD_websocket_split_close_reason() to split that
+   * close reason.
    */
   MHD_WEBSOCKET_STATUS_CLOSE_FRAME = 0x8,
   /**
@@ -189,7 +189,7 @@ enum MHD_WEBSOCKET_STATUS
    * 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 parameters `payload` and `payload_len` are filled with
    * the binary ping data (if any).
    */
   MHD_WEBSOCKET_STATUS_PING_FRAME = 0x9,
@@ -199,57 +199,79 @@ enum MHD_WEBSOCKET_STATUS
    * 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 parameters `payload` and `payload_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).
+   * The parameters `payload` and `payload_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_STATUS_TEXT_FIRST_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).
+   * The parameters `payload` and `payload_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_STATUS_BINARY_FIRST_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,
+   * #MHD_websocket_decode() has decoded the next text frame fragment.
+   * The parameters `payload` and `payload_len` are filled with
+   * the decoded text (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT, but it appears
+   * only after the first and before 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_NEXT_FRAGMENT = 0x21,
+  /**
+   * #MHD_websocket_decode() has decoded the next binary frame fragment.
+   * The parameters `payload` and `payload_len` are filled with
+   * the decoded binary data (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT, but it appears
+   * only after the first and before 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_NEXT_FRAGMENT = 0x22,
+  /**
+   * #MHD_websocket_decode() has decoded the last text frame fragment.
+   * The parameters `payload` and `payload_len` are filled with
+   * the decoded text (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_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 = 0x41,
+  /**
+   * #MHD_websocket_decode() has decoded the last binary frame fragment.
+   * The parameters `payload` and `payload_len` are filled with
+   * the decoded binary data (if any).
+   * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_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 = 0x42,
   /**
    * 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.
+   * You may 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).
    */
@@ -259,10 +281,12 @@ enum MHD_WEBSOCKET_STATUS
    * 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.
+   * You may 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
+   * if you call #MDM_websocket_decode() again after
    * has been invalidated.
+   * You can call #MHD_websocket_stream_is_valid() at any time
+   * to check whether a stream is invalid or not.
    */
   MHD_WEBSOCKET_STATUS_STREAM_BROKEN = -2,
   /**
@@ -271,8 +295,8 @@ enum MHD_WEBSOCKET_STATUS
    * 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_stream_init() or #MHD_websocket_stream_init2() to
+   * avoid this and close the websocket instead.
    */
   MHD_WEBSOCKET_STATUS_MEMORY_ERROR = -3,
   /**
@@ -286,28 +310,39 @@ enum MHD_WEBSOCKET_STATUS
    * 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.
+   * You may 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).
+   * (i. e. more than 4 GiB 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.
+   * An UTF-8 sequence 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.
+   * You may 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
+  MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR = -6,
+  /**
+   * A check routine for the HTTP headers came to the conclusion that
+   * the header value isn't valid for a websocket handshake request.
+   * This value can only be returned from the following functions:
+   * * #MHD_websocket_check_http_version()
+   * * #MHD_websocket_check_connection_header()
+   * * #MHD_websocket_check_upgrade_header()
+   * * #MHD_websocket_check_version_header()
+   * * #MHD_websocket_create_accept_header()
+   */
+  MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER = -7
 };
 
 /**
@@ -332,22 +367,22 @@ 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.
+   * This value cannot be 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
+   * You close the websocket because it 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
+   * You close the websocket because you are shutting down the server or
    * something similar.
    */
   MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001,
   /**
-   * You close the websocket because you a protocol error occurred
+   * You close the websocket because a protocol error occurred
    * during decoding (i. e. invalid byte data).
    */
   MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR = 1002,
@@ -371,7 +406,8 @@ enum MHD_WEBSOCKET_CLOSEREASON
    */
   MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED = 1008,
   /**
-   * You close the websocket because you received a frame which is too big to 
process.
+   * 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().
    */
@@ -497,61 +533,170 @@ enum MHD_WEBSOCKET_VALIDITY
   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.
+ * This callback function is used internally by many websocket functions
+ * for allocating data.
+ * By default `malloc()` is used.
+ * You can use your own allocation function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `malloc()` callback of
+ * a websocket stream with #MHD_websocket_malloc().
  *
- * @param len new size
+ * @param buf_len buffer size in bytes
  * @return allocated memory
  * @ingroup websocket
  */
 typedef void*
-(*MHD_WebSocketMallocCallback) (size_t len);
+(*MHD_WebSocketMallocCallback) (size_t buf_len);
 /**
- * This method is called by many websocket
+ * This callback function is used internally 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.
+ * By default `realloc()` is used.
+ * You can use your own reallocation function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `realloc()` callback of
+ * a websocket stream with #MHD_websocket_realloc().
  *
- * @param cls closure
- * @param len new size
+ * @param buf buffer
+ * @param new_buf_len new buffer size in bytes
  * @return reallocated memory
  * @ingroup websocket
  */
 typedef void*
-(*MHD_WebSocketReallocCallback) (void *cls, size_t len);
+(*MHD_WebSocketReallocCallback) (void *buf, size_t new_buf_len);
 /**
- * This method is called by many websocket
+ * This callback function is used internally 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.
+ * By default `free()` is used.
+ * You can use your own free function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `free()` callback of
+ * a websocket stream with #MHD_websocket_free().
  *
- * @param cls closure
+ * @param buf buffer
  * @ingroup websocket
  */
 typedef void
-(*MHD_WebSocketFreeCallback) (void *cls);
+(*MHD_WebSocketFreeCallback) (void *buf);
+/**
+ * This callback function is used for generating random numbers
+ * for masking payload data in client mode.
+ * If you use websockets in server mode with libmicrohttpd then
+ * you don't need a random number generator, because
+ * the server doesn't mask its outgoing messageses.
+ * However if you wish to use a websocket stream in client mode,
+ * you must pass this callback function to #MHD_websocket_stream_init2().
+ *
+ * @param cls closure specified in #MHD_websocket_stream_init2()
+ * @param buf buffer to fill with random values
+ * @param buf_len size of buffer in bytes
+ * @return The number of generated random bytes.
+ *         Should usually equal to buf_len.
+ * @ingroup websocket
+ */
+typedef size_t
+(*MHD_WebSocketRandomNumberGenerator) (void *cls, void* buf, size_t buf_len);
+
+/**
+ * Checks the HTTP version of the incoming request.
+ * Websocket requests are only allowed for HTTP/1.1 or above.
+ *
+ * @param http_version The value of the 'version' parameter of your
+ *                     access_handler callback
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         0 means the HTTP version is correct for a websocket request,
+ *         a value less than zero means that the HTTP version isn't
+ *         valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_http_version (const char* http_version);
+
+/**
+ * Checks the value of the 'Connection' HTTP request header.
+ * Websocket requests require the token 'Upgrade' in
+ * the 'Connection' HTTP request header.
+ *
+ * @param connection_header The value of the 'Connection' request header.
+ *                          You can get this request header value by passing
+ *                          #MHD_HTTP_HEADER_CONNECTION to
+ *                          #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         0 means the 'Connection' request header is correct
+ *         for a websocket request,
+ *         a value less than zero means that the 'Connection' header isn't
+ *         valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_connection_header (const char* connection_header);
+
+/**
+ * Checks the value of the 'Upgrade' HTTP request header.
+ * Websocket requests require the value 'websocket' in
+ * the 'Upgrade' HTTP request header.
+ *
+ * @param upgrade_header The value of the 'Upgrade' request header.
+ *                       You can get this request header value by passing
+ *                       #MHD_HTTP_HEADER_UPGRADE to
+ *                       #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         0 means the 'Upgrade' request header is correct
+ *         for a websocket request,
+ *         a value less than zero means that the 'Upgrade' header isn't
+ *         valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_upgrade_header (const char* upgrade_header);
 
 /**
- * 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.
+ * Checks the value of the 'Sec-WebSocket-Version' HTTP request header.
+ * Websocket requests require the value '13'
+ * in the 'Sec-WebSocket-Version' HTTP request header.
  *
- * @param sec_websocket_key The value of the 'Sec-WebSocket-Key' request header
+ * @param version_header The value of the 'Sec-WebSocket-Version'
+ *                       request header.
+ *                       You can get this request header value by passing
+ *                       #MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION to
+ *                       #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ *         0 means the 'Sec-WebSocket-Version' request header is correct
+ *         for a websocket request,
+ *         a value less than zero means that the 'Sec-WebSocket-Version'
+ *         header isn't valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_version_header (const char* version_header);
+
+/**
+ * Creates the response value for the 'Sec-WebSocket-Key' HTTP request header.
+ * The generated value must be sent to the client
+ * as 'Sec-WebSocket-Accept' HTTP response header.
+ *
+ * @param sec_websocket_key The value of the 'Sec-WebSocket-Key'
+ *                          request header.
+ *                          You can get this request header value by passing
+ *                          #MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY to
+ *                          #MHD_lookup_connection_value().
  * @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.
+ *                                  will contain the response value plus
+ *                                  a terminating NUL on success.
  * @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);
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_create_accept_header (const char* sec_websocket_key,
+                                    char* sec_websocket_accept);
 
 /**
  * Creates a new websocket stream, used for decoding/encoding.
@@ -559,13 +704,13 @@ MHD_websocket_create_accept (const char*sec_websocket_key,
  * @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
+ * @param max_payload_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_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
                            int flags,
                            size_t max_payload_size);
@@ -573,26 +718,42 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
 /**
  * Creates a new websocket stream, used for decoding/encoding,
  * but with custom memory functions for malloc, realloc and free.
+ * Also a random number generator can be specified for client mode.
  *
  * @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
+ * @param max_payload_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'.
+ * @param callback_malloc  The callback function for `malloc()`.
+ * @param callback_realloc The callback function for `realloc()`.
+ * @param callback_free    The callback function for `free()`.
+ * @param cls_rng          A closure for the random number generator callback.
+ *                         This is only required when
+ *                         MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
+ *                         The given value is passed to
+ *                         the random number generator.
+ *                         May be NULL if not needed.
+ *                         Should be NULL when you are
+ *                         not using MHD_WEBSOCKET_FLAG_CLIENT.
+ * @param callback_rng     A callback function for a
+ *                         secure random number generator.
+ *                         This is only required when
+ *                         MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
+ *                         Should be NULL otherwise.
  * @return A value of `enum MHD_WEBSOCKET_STATUS`.
  *         Typically 0 on success or less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 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);
+                            MHD_WebSocketFreeCallback callback_free,
+                            void* cls_rng,
+                            MHD_WebSocketRandomNumberGenerator callback_rng);
 
 /**
  * Frees a websocket stream
@@ -602,7 +763,7 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
  *         Typically 0 on success or less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_free (struct MHD_WebSocketStream*ws);
 
 /**
@@ -615,7 +776,7 @@ MHD_websocket_stream_free (struct MHD_WebSocketStream*ws);
  *         Typically 0 on success or less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws);
 
 /**
@@ -627,11 +788,11 @@ MHD_websocket_stream_invalidate (struct 
MHD_WebSocketStream*ws);
  * @return A value of `enum MHD_WEBSOCKET_VALIDITY`.
  * @ingroup websocket
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
 MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
 
 /**
- * Decodes a byte sequence via this websocket stream.
+ * Decodes a byte sequence for a websocket stream.
  * Decoding is done until either a frame is complete or
  * the end of the byte sequence is reached.
  *
@@ -644,11 +805,11 @@ MHD_websocket_stream_is_valid (struct 
MHD_WebSocketStream*ws);
  *                                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.
+ *                                to the next call of this function.
+ * @param[out] payload Pointer to a variable, which receives a buffer
+ *                     with the decoded payload data.
  *                     If no decoded data is available this is NULL.
- *                     When this variable is not NULL then
+ *                     When the returned value is not NULL then
  *                     the buffer contains always @a payload_len bytes plus
  *                     one terminating NUL character.
  *                     The caller must free this buffer
@@ -657,11 +818,11 @@ MHD_websocket_stream_is_valid (struct 
MHD_WebSocketStream*ws);
  *                     #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
+ *                     (function 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
+ *                     upon creation of the 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.
@@ -670,58 +831,58 @@ MHD_websocket_stream_is_valid (struct 
MHD_WebSocketStream*ws);
  * @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.
+ *         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,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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);
+                      size_t* streambuf_read_len,
+                      char** payload,
+                      size_t* payload_len);
 
 /**
- * Splits the payload of of a decoded close frame.
+ * Splits the payload of a decoded close frame.
  *
  * @param payload The payload of the close frame.
- *                This parameter may be NULL if @a payload_len is 0.
+ *                This parameter may only 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.
+ *                         This parameter is optional and may 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.
+ *                         This parameter is optional and may 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.
+ *                             This parameter is optional and may 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,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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);
+                                  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.
+ *                     This may 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 specify the fragmentation behavior.
@@ -735,11 +896,11 @@ MHD_websocket_split_close_reason (const char*payload,
  *                   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.
+ *                       should 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.
+ *                       `enum MHD_WEBSOCKET_UTF8STEP` is for this value.
  *                       If you start a new fragment using
  *                       MHD_WEBSOCKET_FRAGMENTATION_NONE or
  *                       MHD_WEBSOCKET_FRAGMENTATION_FIRST the value
@@ -751,17 +912,17 @@ MHD_websocket_split_close_reason (const char*payload,
  *         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,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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);
+                           char** frame,
+                           size_t* frame_len,
+                           int* utf8_step);
 
 /**
- * Encodes a binary data into websocket binary frame.
+ * Encodes binary data into websocket binary frame.
  *
  * @param ws The websocket stream.
  * @param payload The binary data to send.
@@ -786,13 +947,13 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
  *         or a value less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
-MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
-                             const char*payload,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_encode_binary (struct MHD_WebSocketStream* ws,
+                             const char* payload,
                              size_t payload_len,
                              int fragmentation,
-                             char**frame,
-                             size_t*frame_len);
+                             char** frame,
+                             size_t* frame_len);
 
 /**
  * Encodes a websocket ping frame
@@ -815,18 +976,18 @@ MHD_websocket_encode_binary (struct 
MHD_WebSocketStream*ws,
  *         or a value less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
-MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
-                           const char*payload,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_encode_ping (struct MHD_WebSocketStream* ws,
+                           const char* payload,
                            size_t payload_len,
-                           char**frame,
-                           size_t*frame_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
+ * @param payload The binary pong payload data, which should be
  *                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.
@@ -848,12 +1009,12 @@ MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
  *         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);
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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
@@ -890,58 +1051,35 @@ MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
  *         or a value less than 0 on errors.
  * @ingroup websocket
  */
-_MHD_EXTERN int
-MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_encode_close (struct MHD_WebSocketStream* ws,
                             unsigned short reason_code,
-                            const char*reason_utf8,
+                            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);
+                            char** frame,
+                            size_t* frame_len);
 
 /**
  * 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
+ * @param buf_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);
+MHD_websocket_malloc (struct MHD_WebSocketStream* ws,
+                      size_t buf_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
+ * @param buf The previously allocated memory or NULL
+ * @param new_buf_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
@@ -949,16 +1087,16 @@ MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
  * @ingroup websocket
  */
 _MHD_EXTERN void*
-MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
-                       void*cls,
-                       size_t len);
+MHD_websocket_realloc (struct MHD_WebSocketStream* ws,
+                       void* buf,
+                       size_t new_buf_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
+ * @param buf The previously allocated memory or NULL
  *
  * @return A value of `enum MHD_WEBSOCKET_STATUS`.
  *         This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
@@ -966,8 +1104,8 @@ MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
  * @ingroup websocket
  */
 _MHD_EXTERN int
-MHD_websocket_free (struct MHD_WebSocketStream*ws,
-                    void*cls);
+MHD_websocket_free (struct MHD_WebSocketStream* ws,
+                    void* buf);
 
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
diff --git a/src/microhttpd_ws/mhd_websocket.c 
b/src/microhttpd_ws/mhd_websocket.c
index ea1a5bda..da787f7d 100644
--- a/src/microhttpd_ws/mhd_websocket.c
+++ b/src/microhttpd_ws/mhd_websocket.c
@@ -36,6 +36,10 @@ struct MHD_WebSocketStream
   MHD_WebSocketReallocCallback realloc;
   /* The function pointer to free for payload (can be used to use different 
memory management) */
   MHD_WebSocketFreeCallback free;
+  /* A closure for the random number generator (only used for client mode; 
usually not required) */
+  void* cls_rng;
+  /* The random number generator (only used for client mode; usually not 
required) */
+  MHD_WebSocketRandomNumberGenerator rng;
   /* 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. */
@@ -122,13 +126,6 @@ enum MHD_WebSocket_UTF8Result
   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,
@@ -142,12 +139,12 @@ MHD_websocket_check_utf8 (const char*buf,
                           int*utf8_step,
                           size_t*buf_offset);
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
                                       char**payload,
                                       size_t*payload_len);
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
                                        char**payload,
                                        size_t*payload_len);
@@ -158,7 +155,7 @@ static char
 MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
                                     size_t payload_len);
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
                            const char*payload,
                            size_t payload_len,
@@ -167,7 +164,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
                            size_t*frame_len,
                            char opcode);
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
                                 const char*payload,
                                 size_t payload_len,
@@ -176,35 +173,338 @@ MHD_websocket_encode_ping_pong (struct 
MHD_WebSocketStream*ws,
                                 char opcode);
 
 static uint32_t
-MHD_websocket_generate_mask ();
+MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws);
+
+static uint16_t
+MHD_htons (uint16_t value);
+
+static uint64_t
+MHD_htonll (uint64_t value);
+
+
+/**
+ * Checks whether the HTTP version is 1.1 or above.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_http_version (const char* http_version)
+{
+  /* validate parameters */
+  if (NULL == http_version)
+  {
+    /* Like with the other check routines, */
+    /* NULL is threated as "value not given" and not as parameter error */
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+
+  /* Check whether the version has a valid format */
+  /* RFC 1945 3.1: The format must be "HTTP/x.x" where x is */
+  /* any digit and must appear at least once */
+  if ('H' != http_version[0] ||
+      'T' != http_version[1] ||
+      'T' != http_version[2] ||
+      'P' != http_version[3] ||
+      '/' != http_version[4])
+  {
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+
+  /* Find the major and minor part of the version */
+  /* RFC 1945 3.1: Both numbers must be threated as separate integers. */
+  /* Leading zeros must be ignored and both integers may have multiple digits 
*/
+  const char* major = NULL;
+  const char* dot   = NULL;
+  size_t i = 5;
+  for (;;)
+  {
+    char c = http_version[i];
+    if ('0' <= c && '9' >= c)
+    {
+      if (NULL == major ||
+          (http_version + i == major + 1 && '0' == *major) )
+      {
+        major = http_version + i;
+      }
+      ++i;
+    }
+    else if ('.' == http_version[i])
+    {
+      dot = http_version + i;
+      ++i;
+      break;
+    }
+    else
+    {
+      return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+    }
+  }
+  const char* minor = NULL;
+  const char* end   = NULL;
+  for (;;)
+  {
+    char c = http_version[i];
+    if ('0' <= c && '9' >= c)
+    {
+      if (NULL == minor ||
+          (http_version + i == minor + 1 && '0' == *minor) )
+      {
+        minor = http_version + i;
+      }
+      ++i;
+    }
+    else if (0 == c)
+    {
+      end = http_version + i;
+      break;
+    }
+    else
+    {
+      return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+    }
+  }
+  if (NULL == major || NULL == dot || NULL == minor || NULL == end)
+  {
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+  if (2 <= dot - major || '2' <= *major ||
+      ('1' == *major && (2 <= end - minor || '1' <= *minor)) )
+  {
+    return MHD_WEBSOCKET_STATUS_OK;
+  }
+
+  return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Connection" request header has the 'Upgrade' token.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_connection_header (const char* connection_header)
+{
+  /* validate parameters */
+  if (NULL == connection_header)
+  {
+    /* To be compatible with the return value */
+    /* of MHD_lookup_connection_value, */
+    /* NULL is threated as "value not given" and not as parameter error */
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+
+  /* Check whether the Connection includes an Upgrade token */
+  /* RFC 7230 6.1: Multiple tokens may appear. */
+  /* RFC 7230 3.2.6: Tokens are comma separated */
+  const char* token_start = NULL;
+  const char* token_end   = NULL;
+  for(size_t i = 0; ; ++i)
+  {
+    char c = connection_header[i];
+
+    /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
+    /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
+    /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
+    /* DIGIT / ALPHA */
+    if ('!' == c || '#' == c || '$' == c || '%' == c ||
+        '&' == c || '\'' == c || '*' == c ||
+        '+' == c || '-' == c || '.' == c || '^' == c ||
+        '_' == c || '`' == c || '|' == c || '~' == c ||
+        ('0' <= c && '9' >= c) ||
+        ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c) )
+    {
+      /* This is a valid token character */
+      if (NULL == token_start)
+      {
+        token_start = connection_header + i;
+      }
+      token_end = connection_header + i + 1;
+    }
+    else if (' ' == c || '\t' == c)
+    {
+      /* White-spaces around tokens will be ignored */
+    }
+    else if (',' == c || 0 == c)
+    {
+      /* Check the token (case-insensitive) */
+      if (NULL != token_start)
+      {
+        if ( 7 == (token_end - token_start) )
+        {
+          if ( ('U' == token_start[0] || 'u' == token_start[0]) &&
+               ('P' == token_start[1] || 'p' == token_start[1]) &&
+               ('G' == token_start[2] || 'g' == token_start[2]) &&
+               ('R' == token_start[3] || 'r' == token_start[3]) &&
+               ('A' == token_start[4] || 'a' == token_start[4]) &&
+               ('D' == token_start[5] || 'd' == token_start[5]) &&
+               ('E' == token_start[6] || 'e' == token_start[6]) )
+          {
+            /* The token equals to "Upgrade" */
+            return MHD_WEBSOCKET_STATUS_OK;
+          }
+        }
+      }
+      if (0 == c)
+      {
+        break;
+      }
+      token_start = NULL;
+      token_end   = NULL;
+    }
+    else
+    {
+      /* RFC 7230 3.2.6: Other characters are not allowed */
+      return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+    }
+  }
+  return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Upgrade" request header has the "websocket" keyword.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_upgrade_header (const char* upgrade_header)
+{
+  /* validate parameters */
+  if (NULL == upgrade_header)
+  {
+    /* To be compatible with the return value */
+    /* of MHD_lookup_connection_value, */
+    /* NULL is threated as "value not given" and not as parameter error */
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+
+  /* Check whether the Connection includes an Upgrade token */
+  /* RFC 7230 6.1: Multiple tokens may appear. */
+  /* RFC 7230 3.2.6: Tokens are comma separated */
+  const char* keyword_start = NULL;
+  const char* keyword_end   = NULL;
+  for(size_t i = 0; ; ++i)
+  {
+    char c = upgrade_header[i];
+
+    /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
+    /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
+    /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
+    /* DIGIT / ALPHA */
+    /* We also allow "/" here as the sub-delimiter for the protocol version */
+    if ('!' == c || '#' == c || '$' == c || '%' == c ||
+        '&' == c || '\'' == c || '*' == c ||
+        '+' == c || '-' == c || '.' == c || '^' == c ||
+        '_' == c || '`' == c || '|' == c || '~' == c ||
+        '/' == c ||
+        ('0' <= c && '9' >= c) ||
+        ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c) )
+    {
+      /* This is a valid token character */
+      if (NULL == keyword_start)
+      {
+        keyword_start = upgrade_header + i;
+      }
+      keyword_end = upgrade_header + i + 1;
+    }
+    else if (' ' == c || '\t' == c)
+    {
+      /* White-spaces around tokens will be ignored */
+    }
+    else if (',' == c || 0 == c)
+    {
+      /* Check the token (case-insensitive) */
+      if (NULL != keyword_start)
+      {
+        if ( 9 == (keyword_end - keyword_start) )
+        {
+          if ( ('W' == keyword_start[0] || 'w' == keyword_start[0]) &&
+               ('E' == keyword_start[1] || 'e' == keyword_start[1]) &&
+               ('B' == keyword_start[2] || 'b' == keyword_start[2]) &&
+               ('S' == keyword_start[3] || 's' == keyword_start[3]) &&
+               ('O' == keyword_start[4] || 'o' == keyword_start[4]) &&
+               ('C' == keyword_start[5] || 'c' == keyword_start[5]) &&
+               ('K' == keyword_start[6] || 'k' == keyword_start[6]) &&
+               ('E' == keyword_start[7] || 'e' == keyword_start[7]) &&
+               ('T' == keyword_start[8] || 't' == keyword_start[8]) )
+          {
+            /* The keyword equals to "websocket" */
+            return MHD_WEBSOCKET_STATUS_OK;
+          }
+        }
+      }
+      if (0 == c)
+      {
+        break;
+      }
+      keyword_start = NULL;
+      keyword_end   = NULL;
+    }
+    else
+    {
+      /* RFC 7230 3.2.6: Other characters are not allowed */
+      return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+    }
+  }
+  return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Sec-WebSocket-Version" request header
+ * equals to "13"
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_version_header (const char* version_header)
+{
+  /* validate parameters */
+  if (NULL == version_header)
+  {
+    /* To be compatible with the return value */
+    /* of MHD_lookup_connection_value, */
+    /* NULL is threated as "value not given" and not as parameter error */
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
+
+  if ('1' == version_header[0] &&
+      '3' == version_header[1] &&
+      0   == version_header[2])
+  {
+    /* The version equals to "13" */
+    return MHD_WEBSOCKET_STATUS_OK;
+  }
+  return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
 
 /**
  * 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)
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_create_accept_header (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) )
+  if (NULL == sec_websocket_accept)
   {
     return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
   }
+  if (NULL == sec_websocket_key)
+  {
+    /* NULL is not a parameter error, */
+    /* because MHD_lookup_connection_value returns NULL */
+    /* if the header wasn't found */
+    return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+  }
 
   /* 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);
+  MHD_SHA1_init (&ctx);
+  MHD_SHA1_update (&ctx, (const uint8_t*) sec_websocket_key, length);
+  MHD_SHA1_update (&ctx, (const uint8_t*) suffix, 36);
+  MHD_SHA1_finish (&ctx, (uint8_t*) sha1);
 
   /* base64 encode that SHA1 hash */
   /* (simple algorithm here; SHA1 has always 20 bytes, */
@@ -234,7 +534,7 @@ MHD_websocket_create_accept (const char*sec_websocket_key,
 /**
  * Initializes a new websocket stream
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
                            int flags,
                            size_t max_payload_size)
@@ -244,7 +544,9 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
                                      max_payload_size,
                                      malloc,
                                      realloc,
-                                     free);
+                                     free,
+                                     NULL,
+                                     NULL);
 }
 
 
@@ -252,13 +554,15 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
  * Initializes a new websocket stream with
  * additional parameters for allocation functions
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 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)
+                            MHD_WebSocketFreeCallback callback_free,
+                            void* cls_rng,
+                            MHD_WebSocketRandomNumberGenerator callback_rng)
 {
   /* initialize output variables for errors cases */
   if (NULL != ws)
@@ -270,7 +574,9 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
       ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) ||
       (NULL == callback_malloc) ||
       (NULL == callback_realloc) ||
-      (NULL == callback_free) )
+      (NULL == callback_free) ||
+      ((0 != (flags & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+      (NULL == callback_rng)))
   {
     return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
   }
@@ -288,6 +594,8 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
   ws_->malloc   = callback_malloc;
   ws_->realloc  = callback_realloc;
   ws_->free     = callback_free;
+  ws_->cls_rng  = cls_rng;
+  ws_->rng      = callback_rng;
   ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID;
 
   /* return stream */
@@ -300,7 +608,7 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
 /**
  * Frees a previously allocated websocket stream
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
 {
   /* validate parameters */
@@ -323,7 +631,7 @@ MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
 /**
  * Invalidates a websocket stream (no more decoding possible)
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
 {
   /* validate parameters */
@@ -340,7 +648,7 @@ MHD_websocket_stream_invalidate (struct 
MHD_WebSocketStream*ws)
 /**
  * Returns whether a websocket stream is valid
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
 MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
 {
   /* validate parameters */
@@ -354,7 +662,7 @@ MHD_websocket_stream_is_valid (struct 
MHD_WebSocketStream*ws)
 /**
  * Decodes incoming data to a websocket frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_decode (struct MHD_WebSocketStream*ws,
                       const char*streambuf,
                       size_t streambuf_len,
@@ -372,7 +680,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
 
   /* validate parameters */
   if ((NULL == ws) ||
-      (NULL == streambuf) && (0 != streambuf_len) ||
+      ((NULL == streambuf) && (0 != streambuf_len)) ||
       (NULL == streambuf_read_len) ||
       (NULL == payload) ||
       (NULL == payload_len) )
@@ -657,8 +965,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
         else
         {
           size_t size = (size_t) frame_len;
-          if ((SIZE_MAX < size) || ws->max_payload_size &&
-              (ws->max_payload_size < size) )
+          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;
@@ -713,8 +1021,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
     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]));
+        size_t size = (size_t) MHD_htons (*((uint16_t*) &ws->frame_header 
[2]));
         if (125 >= size)
         {
           /* RFC 6455 5.2 Payload length: The minimal number of bytes */
@@ -733,8 +1040,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
           *streambuf_read_len = current;
           return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
         }
-        if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size 
<
-                                                          size) )
+        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 */
@@ -771,7 +1078,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
     case MHD_WebSocket_DecodeStep_Length8of8:
       {
         ws->frame_header [ws->frame_header_size++] = streambuf [current++];
-        uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2]));
+        uint64_t size = MHD_htonll (*((uint64_t*) &ws->frame_header [2]));
         if (0x7fffffffffffffff < size)
         {
           /* RFC 6455 5.2 frame-payload-length-63: The length may */
@@ -809,8 +1116,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
           *streambuf_read_len = current;
           return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
         }
-        if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size 
<
-                                                          size) )
+        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 */
@@ -893,13 +1200,13 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
                                                        & 0x03));
           current += bytes_to_take;
           ws->payload_index += bytes_to_take;
-          if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
+          if (((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
                ws->decode_step) &&
-              (MHD_WebSocket_Opcode_Text == ws->data_type) ||
-              (MHD_WebSocket_DecodeStep_PayloadOfControlFrame ==
+              (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) )
+              (2 < ws->payload_index)) )
           {
             /* RFC 6455 8.1: We need to check the UTF-8 validity */
             int utf8_step;
@@ -1016,7 +1323,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
 }
 
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
                                       char**payload,
                                       size_t*payload_len)
@@ -1119,13 +1426,15 @@ MHD_websocket_decode_header_complete (struct 
MHD_WebSocketStream*ws,
 }
 
 
-static int
+static enum MHD_WEBSOCKET_STATUS
 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;
+  char is_continue = MHD_WebSocket_Opcode_Continuation ==
+                      (ws->frame_header [0] & 0x0F);
+  char is_fin      = ws->frame_header [0] & 0x80;
   if (0 != is_fin)
   {
     /* the frame is complete */
@@ -1134,9 +1443,9 @@ MHD_websocket_decode_payload_complete (struct 
MHD_WebSocketStream*ws,
       /* 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)))
+          (0 != is_continue))
       {
-        data_type |= 0x20;   /* mark as last fragment */
+        data_type |= 0x40;   /* mark as last fragment */
       }
       *payload     = ws->data_payload;
       *payload_len = ws->data_payload_size;
@@ -1170,7 +1479,7 @@ MHD_websocket_decode_payload_complete (struct 
MHD_WebSocketStream*ws,
     {
       /* the last UTF-8 sequence is incomplete, so we keep the start of
       that and only return the part before */
-      size_t given_utf8;
+      size_t given_utf8 = 0;
       switch (ws->data_utf8_step)
       {
       /* one byte given */
@@ -1220,7 +1529,10 @@ MHD_websocket_decode_payload_complete (struct 
MHD_WebSocketStream*ws,
       ws->decode_step       = MHD_WebSocket_DecodeStep_Start;
       ws->payload_index     = 0;
       ws->frame_header_size = 0;
-      return ws->data_type | 0x10;    /* mark as fragment */
+      if (0 != is_continue)
+        return ws->data_type | 0x20;    /* mark as middle fragment */
+      else
+        return ws->data_type | 0x10;    /* mark as first fragment */
     }
     else
     {
@@ -1233,7 +1545,10 @@ MHD_websocket_decode_payload_complete (struct 
MHD_WebSocketStream*ws,
       ws->decode_step        = MHD_WebSocket_DecodeStep_Start;
       ws->payload_index      = 0;
       ws->frame_header_size  = 0;
-      return ws->data_type | 0x10;    /* mark as fragment */
+      if (0 != is_continue)
+        return ws->data_type | 0x20;    /* mark as middle fragment */
+      else
+        return ws->data_type | 0x10;    /* mark as first fragment */
     }
   }
   else
@@ -1251,7 +1566,7 @@ MHD_websocket_decode_payload_complete (struct 
MHD_WebSocketStream*ws,
 /**
  * Splits the received close reason
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_split_close_reason (const char*payload,
                                   size_t payload_len,
                                   unsigned short*reason_code,
@@ -1283,7 +1598,7 @@ MHD_websocket_split_close_reason (const char*payload,
   else
   {
     if (NULL != reason_code)
-      *reason_code = htons (*((unsigned short*) payload));
+      *reason_code = MHD_htons (*((uint16_t*) payload));
   }
 
   /* decode reason text */
@@ -1309,7 +1624,7 @@ MHD_websocket_split_close_reason (const char*payload,
 /**
  * Encodes a text into a websocket text frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
                            const char*payload_utf8,
                            size_t payload_utf8_len,
@@ -1333,13 +1648,13 @@ MHD_websocket_encode_text (struct 
MHD_WebSocketStream*ws,
 
   /* validate parameters */
   if ((NULL == ws) ||
-      (0 != payload_utf8_len) && (NULL == payload_utf8) ||
+      ((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) )
+      ((MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) &&
+      (NULL == utf8_step)) )
   {
     return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
   }
@@ -1356,8 +1671,8 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
                                               utf8_step,
                                               NULL);
   if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) ||
-      (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
-      (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) )
+      ((MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
+      (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation)) )
   {
     return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
   }
@@ -1376,7 +1691,7 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
 /**
  * Encodes binary data into a websocket binary frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
                              const char*payload,
                              size_t payload_len,
@@ -1392,7 +1707,7 @@ MHD_websocket_encode_binary (struct 
MHD_WebSocketStream*ws,
 
   /* validate parameters */
   if ((NULL == ws) ||
-      (0 != payload_len) && (NULL == payload) ||
+      ((0 != payload_len) && (NULL == payload)) ||
       (NULL == frame) ||
       (NULL == frame_len) ||
       (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
@@ -1420,7 +1735,7 @@ MHD_websocket_encode_binary (struct 
MHD_WebSocketStream*ws,
 /**
  * Internal function for encoding text/binary data into a websocket frame
  */
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
                            const char*payload,
                            size_t payload_len,
@@ -1433,7 +1748,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
   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;
-  uint32_t mask       = 0 != is_masked ? MHD_websocket_generate_mask () : 0;
+  uint32_t mask       = 0 != is_masked ? MHD_websocket_generate_mask (ws) : 0;
 
   /* allocate memory */
   char*result = ws->malloc (total_len + 1);
@@ -1468,13 +1783,13 @@ MHD_websocket_encode_data (struct 
MHD_WebSocketStream*ws,
   else if (65536 > payload_len)
   {
     *(result++) = is_masked | 126;
-    *((unsigned short *) result) = htons ((unsigned short) payload_len);
+    *((uint16_t *) result) = MHD_htons ((uint16_t) payload_len);
     result += 2;
   }
   else
   {
     *(result++) = is_masked | 127;
-    *((uint64_t *) result) = htonll ((uint64_t) payload_len);
+    *((uint64_t *) result) = MHD_htonll ((uint64_t) payload_len);
     result += 8;
 
   }
@@ -1505,7 +1820,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
 /**
  * Encodes a websocket ping frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
                            const char*payload,
                            size_t payload_len,
@@ -1525,7 +1840,7 @@ MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
 /**
  * Encodes a websocket pong frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
                            const char*payload,
                            size_t payload_len,
@@ -1545,7 +1860,7 @@ MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
 /**
  * Internal function for encoding ping/pong frames
  */
-static int
+static enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
                                 const char*payload,
                                 size_t payload_len,
@@ -1561,7 +1876,7 @@ MHD_websocket_encode_ping_pong (struct 
MHD_WebSocketStream*ws,
 
   /* validate the parameters */
   if ((NULL == ws) ||
-      (0 != payload_len) && (NULL == payload) ||
+      ((0 != payload_len) && (NULL == payload)) ||
       (NULL == frame) ||
       (NULL == frame_len) )
   {
@@ -1576,7 +1891,7 @@ MHD_websocket_encode_ping_pong (struct 
MHD_WebSocketStream*ws,
   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;
-  uint32_t mask       = is_masked != 0 ? MHD_websocket_generate_mask () : 0;
+  uint32_t mask       = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
 
   /* allocate memory */
   char*result = ws->malloc (total_len + 1);
@@ -1618,7 +1933,7 @@ MHD_websocket_encode_ping_pong (struct 
MHD_WebSocketStream*ws,
 /**
  * Encodes a websocket close frame
  */
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
 MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
                             unsigned short reason_code,
                             const char*reason_utf8,
@@ -1634,13 +1949,13 @@ MHD_websocket_encode_close (struct 
MHD_WebSocketStream*ws,
 
   /* validate the parameters */
   if ((NULL == ws) ||
-      (0 != reason_utf8_len) && (NULL == reason_utf8) ||
+      ((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) )
+      ((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;
   }
@@ -1668,7 +1983,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
                          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;
-  uint32_t mask       = is_masked != 0 ? MHD_websocket_generate_mask () : 0;
+  uint32_t mask       = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
 
   /* allocate memory */
   char*result = ws->malloc (total_len + 1);
@@ -1697,7 +2012,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
   if (0 != reason_code)
   {
     /* close reason code */
-    unsigned short reason_code_nb = htons (reason_code);
+    uint16_t reason_code_nb = MHD_htons (reason_code);
     MHD_websocket_copy_payload (result,
                                 (const char*) &reason_code_nb,
                                 2,
@@ -1815,8 +2130,8 @@ MHD_websocket_check_utf8 (const char*buf,
         /* 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) )
+      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;
@@ -1990,31 +2305,33 @@ MHD_websocket_check_utf8 (const char*buf,
 }
 
 
-/**
- * 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 uint32_t
-MHD_websocket_generate_mask ()
+MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws)
 {
   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);
+  if (NULL != ws->rng)
+  {
+    size_t offset = 0;
+    while (offset < 4)
+    {
+      size_t encoded = ws->rng (ws->cls_rng,
+                                mask_ + offset,
+                                4 - offset);
+      offset += encoded;
+    }
+  }
+  else
+  {
+    /* this case should never happen */
+    mask_ [0] = 0;
+    mask_ [1] = 0;
+    mask_ [2] = 0;
+    mask_ [3] = 0;
+  }
 
   return *((uint32_t *) mask_);
 }
@@ -2024,15 +2341,15 @@ MHD_websocket_generate_mask ()
  * Calls the malloc function associated with the websocket steam
  */
 _MHD_EXTERN void*
-MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
-                      size_t len)
+MHD_websocket_malloc (struct MHD_WebSocketStream* ws,
+                      size_t buf_len)
 {
   if (NULL == ws)
   {
     return NULL;
   }
 
-  return ws->malloc (len);
+  return ws->malloc (buf_len);
 }
 
 
@@ -2040,16 +2357,16 @@ MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
  * Calls the realloc function associated with the websocket steam
  */
 _MHD_EXTERN void*
-MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
-                       void*cls,
-                       size_t len)
+MHD_websocket_realloc (struct MHD_WebSocketStream* ws,
+                       void* buf,
+                       size_t new_buf_len)
 {
   if (NULL == ws)
   {
     return NULL;
   }
 
-  return ws->realloc (cls, len);
+  return ws->realloc (buf, new_buf_len);
 }
 
 
@@ -2057,15 +2374,67 @@ MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
  * Calls the free function associated with the websocket steam
  */
 _MHD_EXTERN int
-MHD_websocket_free (struct MHD_WebSocketStream*ws,
-                    void*cls)
+MHD_websocket_free (struct MHD_WebSocketStream* ws,
+                    void* buf)
 {
   if (NULL == ws)
   {
     return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
   }
 
-  ws->free (cls);
+  ws->free (buf);
 
   return MHD_WEBSOCKET_STATUS_OK;
 }
+
+/**
+ * Converts a 16 bit value into network byte order (MSB first)
+ * in dependence of the host system
+ */
+static uint16_t
+MHD_htons (uint16_t value)
+{
+  uint16_t endian = 0x0001;
+
+  if (((char *) &endian)[0] == 0x01)
+  {
+    /* least significant byte first */
+    ((char *) &endian)[0] = ((char *) &value)[1];
+    ((char *) &endian)[1] = ((char *) &value)[0];
+    return endian;
+  }
+  else
+  {
+    /* most significant byte first */
+    return value;
+  }
+}
+
+/**
+ * Converts a 64 bit value into network byte order (MSB first)
+ * in dependence of the host system
+ */
+static uint64_t
+MHD_htonll (uint64_t value)
+{
+  uint64_t endian = 0x0000000000000001;
+
+  if (((char *) &endian)[0] == 0x01)
+  {
+    /* least significant byte first */
+    ((char *) &endian)[0] = ((char *) &value)[7];
+    ((char *) &endian)[1] = ((char *) &value)[6];
+    ((char *) &endian)[2] = ((char *) &value)[5];
+    ((char *) &endian)[3] = ((char *) &value)[4];
+    ((char *) &endian)[4] = ((char *) &value)[3];
+    ((char *) &endian)[5] = ((char *) &value)[2];
+    ((char *) &endian)[6] = ((char *) &value)[1];
+    ((char *) &endian)[7] = ((char *) &value)[0];
+    return endian;
+  }
+  else
+  {
+    /* most significant byte first */
+    return value;
+  }
+}
diff --git a/src/microhttpd_ws/sha1.c b/src/microhttpd_ws/sha1.c
index 910c1bdb..9888cbfe 100644
--- a/src/microhttpd_ws/sha1.c
+++ b/src/microhttpd_ws/sha1.c
@@ -1,420 +1,378 @@
-/* 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
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+     libmicrohttpd 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, see <http://www.gnu.org/licenses/>.
 */
 
-/*#include <config.h>*/
+/**
+ * @file microhttpd/sha1.c
+ * @brief  Calculation of SHA-1 digest as defined in FIPS PUB 180-4 (2015)
+ * @author Karlson2k (Evgeny Grin)
+ */
 
 #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.  */
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif /* HAVE_MEMORY_H */
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ */
 void
-sha1_init_ctx (struct sha1_ctx *ctx)
+MHD_SHA1_init (void *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;
+  struct sha1_ctx *const ctx = ctx_;
+  /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.1 */
+  /* Just some "magic" numbers defined by standard */
+  ctx->H[0] = UINT32_C (0x67452301);
+  ctx->H[1] = UINT32_C (0xefcdab89);
+  ctx->H[2] = UINT32_C (0x98badcfe);
+  ctx->H[3] = UINT32_C (0x10325476);
+  ctx->H[4] = UINT32_C (0xc3d2e1f0);
+
+  /* Initialise number of bytes. */
+  ctx->count = 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)
+/**
+ * Base of SHA-1 transformation.
+ * Gets full 512 bits / 64 bytes block of data and updates hash values;
+ * @param H     hash values
+ * @param data  data, must be exactly 64 bytes long
+ */
+static void
+sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH],
+                const uint8_t data[SHA1_BLOCK_SIZE])
 {
-  ((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;
+  /* Working variables,
+     see FIPS PUB 180-4 paragraph 6.1.3 */
+  uint32_t a = H[0];
+  uint32_t b = H[1];
+  uint32_t c = H[2];
+  uint32_t d = H[3];
+  uint32_t e = H[4];
+
+  /* Data buffer, used as cyclic buffer.
+     See FIPS PUB 180-4 paragraphs 5.2.1, 6.1.3 */
+  uint32_t W[16];
+
+  /* 'Ch' and 'Maj' macro functions are defined with
+     widely-used optimization.
+     See FIPS PUB 180-4 formulae 4.1. */
+#define Ch(x,y,z)     ( (z) ^ ((x) & ((y) ^ (z))) )
+#define Maj(x,y,z)    ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
+  /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z)  ( ( (x) & (y) ) ^ ( ~(x) & (z) ) )          */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+#define Par(x,y,z)    ( (x) ^ (y) ^ (z) )
+
+  /* Single step of SHA-1 computation,
+     see FIPS PUB 180-4 paragraph 6.1.3 step 3.
+   * Note: instead of reassigning all working variables on each step,
+           variables are rotated for each step:
+             SHA1STEP32 (a, b, c, d, e, func, K00, W[0]);
+             SHA1STEP32 (e, a, b, c, d, func, K00, W[1]);
+           so current 'vC' will be used as 'vD' on the next step,
+           current 'vE' will be used as 'vA' on the next step.
+   * Note: 'wt' must be used exactly one time in this macro as it change other 
data as well
+           every time when used. */
+
+#define SHA1STEP32(vA,vB,vC,vD,vE,ft,kt,wt) do {                         \
+    (vE) += _MHD_ROTL32 ((vA), 5) + ft ((vB), (vC), (vD)) + (kt) + (wt); \
+    (vB) = _MHD_ROTL32 ((vB), 30); } while (0)
+
+  /* Get value of W(t) from input data buffer,
+     See FIPS PUB 180-4 paragraph 6.1.3.
+     Input data must be read in big-endian bytes order,
+     see FIPS PUB 180-4 paragraph 3.1.2. */
+#define GET_W_FROM_DATA(buf,t) \
+  _MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD)
+
+#ifndef _MHD_GET_32BIT_BE_UNALIGNED
+  if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
+  {
+    /* Copy the unaligned input data to the aligned buffer */
+    memcpy (W, data, SHA1_BLOCK_SIZE);
+    /* The W[] buffer itself will be used as the source of the data,
+     * but data will be reloaded in correct bytes order during
+     * the next steps */
+    data = (uint8_t*) W;
+  }
+#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
+
+/* SHA-1 values of Kt for t=0..19, see FIPS PUB 180-4 paragraph 4.2.1. */
+#define K00      UINT32_C(0x5a827999)
+/* SHA-1 values of Kt for t=20..39, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K20      UINT32_C(0x6ed9eba1)
+/* SHA-1 values of Kt for t=40..59, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K40      UINT32_C(0x8f1bbcdc)
+/* SHA-1 values of Kt for t=60..79, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K60      UINT32_C(0xca62c1d6)
+
+  /* During first 16 steps, before making any calculations on each step,
+     the W element is read from input data buffer as big-endian value and
+     stored in array of W elements. */
+  /* Note: instead of using K constants as array, all K values are specified
+     individually for each step. */
+  SHA1STEP32 (a, b, c, d, e, Ch, K00, W[0] = GET_W_FROM_DATA (data, 0));
+  SHA1STEP32 (e, a, b, c, d, Ch, K00, W[1] = GET_W_FROM_DATA (data, 1));
+  SHA1STEP32 (d, e, a, b, c, Ch, K00, W[2] = GET_W_FROM_DATA (data, 2));
+  SHA1STEP32 (c, d, e, a, b, Ch, K00, W[3] = GET_W_FROM_DATA (data, 3));
+  SHA1STEP32 (b, c, d, e, a, Ch, K00, W[4] = GET_W_FROM_DATA (data, 4));
+  SHA1STEP32 (a, b, c, d, e, Ch, K00, W[5] = GET_W_FROM_DATA (data, 5));
+  SHA1STEP32 (e, a, b, c, d, Ch, K00, W[6] = GET_W_FROM_DATA (data, 6));
+  SHA1STEP32 (d, e, a, b, c, Ch, K00, W[7] = GET_W_FROM_DATA (data, 7));
+  SHA1STEP32 (c, d, e, a, b, Ch, K00, W[8] = GET_W_FROM_DATA (data, 8));
+  SHA1STEP32 (b, c, d, e, a, Ch, K00, W[9] = GET_W_FROM_DATA (data, 9));
+  SHA1STEP32 (a, b, c, d, e, Ch, K00, W[10] = GET_W_FROM_DATA (data, 10));
+  SHA1STEP32 (e, a, b, c, d, Ch, K00, W[11] = GET_W_FROM_DATA (data, 11));
+  SHA1STEP32 (d, e, a, b, c, Ch, K00, W[12] = GET_W_FROM_DATA (data, 12));
+  SHA1STEP32 (c, d, e, a, b, Ch, K00, W[13] = GET_W_FROM_DATA (data, 13));
+  SHA1STEP32 (b, c, d, e, a, Ch, K00, W[14] = GET_W_FROM_DATA (data, 14));
+  SHA1STEP32 (a, b, c, d, e, Ch, K00, W[15] = GET_W_FROM_DATA (data, 15));
+
+  /* 'W' generation and assignment for 16 <= t <= 79.
+     See FIPS PUB 180-4 paragraph 6.1.3.
+     As only last 16 'W' are used in calculations, it is possible to
+     use 16 elements array of W as cyclic buffer. */
+#define Wgen(w,t) _MHD_ROTL32((w)[(t + 13) & 0xf] ^ (w)[(t + 8) & 0xf] \
+                              ^ (w)[(t + 2) & 0xf] ^ (w)[t & 0xf], 1)
+
+  /* During last 60 steps, before making any calculations on each step,
+     W element is generated from W elements of cyclic buffer and generated 
value
+     stored back in cyclic buffer. */
+  /* Note: instead of using K constants as array, all K values are specified
+     individually for each step, see FIPS PUB 180-4 paragraph 4.2.1. */
+  SHA1STEP32 (e, a, b, c, d, Ch, K00, W[16 & 0xf] = Wgen (W, 16));
+  SHA1STEP32 (d, e, a, b, c, Ch, K00, W[17 & 0xf] = Wgen (W, 17));
+  SHA1STEP32 (c, d, e, a, b, Ch, K00, W[18 & 0xf] = Wgen (W, 18));
+  SHA1STEP32 (b, c, d, e, a, Ch, K00, W[19 & 0xf] = Wgen (W, 19));
+  SHA1STEP32 (a, b, c, d, e, Par, K20, W[20 & 0xf] = Wgen (W, 20));
+  SHA1STEP32 (e, a, b, c, d, Par, K20, W[21 & 0xf] = Wgen (W, 21));
+  SHA1STEP32 (d, e, a, b, c, Par, K20, W[22 & 0xf] = Wgen (W, 22));
+  SHA1STEP32 (c, d, e, a, b, Par, K20, W[23 & 0xf] = Wgen (W, 23));
+  SHA1STEP32 (b, c, d, e, a, Par, K20, W[24 & 0xf] = Wgen (W, 24));
+  SHA1STEP32 (a, b, c, d, e, Par, K20, W[25 & 0xf] = Wgen (W, 25));
+  SHA1STEP32 (e, a, b, c, d, Par, K20, W[26 & 0xf] = Wgen (W, 26));
+  SHA1STEP32 (d, e, a, b, c, Par, K20, W[27 & 0xf] = Wgen (W, 27));
+  SHA1STEP32 (c, d, e, a, b, Par, K20, W[28 & 0xf] = Wgen (W, 28));
+  SHA1STEP32 (b, c, d, e, a, Par, K20, W[29 & 0xf] = Wgen (W, 29));
+  SHA1STEP32 (a, b, c, d, e, Par, K20, W[30 & 0xf] = Wgen (W, 30));
+  SHA1STEP32 (e, a, b, c, d, Par, K20, W[31 & 0xf] = Wgen (W, 31));
+  SHA1STEP32 (d, e, a, b, c, Par, K20, W[32 & 0xf] = Wgen (W, 32));
+  SHA1STEP32 (c, d, e, a, b, Par, K20, W[33 & 0xf] = Wgen (W, 33));
+  SHA1STEP32 (b, c, d, e, a, Par, K20, W[34 & 0xf] = Wgen (W, 34));
+  SHA1STEP32 (a, b, c, d, e, Par, K20, W[35 & 0xf] = Wgen (W, 35));
+  SHA1STEP32 (e, a, b, c, d, Par, K20, W[36 & 0xf] = Wgen (W, 36));
+  SHA1STEP32 (d, e, a, b, c, Par, K20, W[37 & 0xf] = Wgen (W, 37));
+  SHA1STEP32 (c, d, e, a, b, Par, K20, W[38 & 0xf] = Wgen (W, 38));
+  SHA1STEP32 (b, c, d, e, a, Par, K20, W[39 & 0xf] = Wgen (W, 39));
+  SHA1STEP32 (a, b, c, d, e, Maj, K40, W[40 & 0xf] = Wgen (W, 40));
+  SHA1STEP32 (e, a, b, c, d, Maj, K40, W[41 & 0xf] = Wgen (W, 41));
+  SHA1STEP32 (d, e, a, b, c, Maj, K40, W[42 & 0xf] = Wgen (W, 42));
+  SHA1STEP32 (c, d, e, a, b, Maj, K40, W[43 & 0xf] = Wgen (W, 43));
+  SHA1STEP32 (b, c, d, e, a, Maj, K40, W[44 & 0xf] = Wgen (W, 44));
+  SHA1STEP32 (a, b, c, d, e, Maj, K40, W[45 & 0xf] = Wgen (W, 45));
+  SHA1STEP32 (e, a, b, c, d, Maj, K40, W[46 & 0xf] = Wgen (W, 46));
+  SHA1STEP32 (d, e, a, b, c, Maj, K40, W[47 & 0xf] = Wgen (W, 47));
+  SHA1STEP32 (c, d, e, a, b, Maj, K40, W[48 & 0xf] = Wgen (W, 48));
+  SHA1STEP32 (b, c, d, e, a, Maj, K40, W[49 & 0xf] = Wgen (W, 49));
+  SHA1STEP32 (a, b, c, d, e, Maj, K40, W[50 & 0xf] = Wgen (W, 50));
+  SHA1STEP32 (e, a, b, c, d, Maj, K40, W[51 & 0xf] = Wgen (W, 51));
+  SHA1STEP32 (d, e, a, b, c, Maj, K40, W[52 & 0xf] = Wgen (W, 52));
+  SHA1STEP32 (c, d, e, a, b, Maj, K40, W[53 & 0xf] = Wgen (W, 53));
+  SHA1STEP32 (b, c, d, e, a, Maj, K40, W[54 & 0xf] = Wgen (W, 54));
+  SHA1STEP32 (a, b, c, d, e, Maj, K40, W[55 & 0xf] = Wgen (W, 55));
+  SHA1STEP32 (e, a, b, c, d, Maj, K40, W[56 & 0xf] = Wgen (W, 56));
+  SHA1STEP32 (d, e, a, b, c, Maj, K40, W[57 & 0xf] = Wgen (W, 57));
+  SHA1STEP32 (c, d, e, a, b, Maj, K40, W[58 & 0xf] = Wgen (W, 58));
+  SHA1STEP32 (b, c, d, e, a, Maj, K40, W[59 & 0xf] = Wgen (W, 59));
+  SHA1STEP32 (a, b, c, d, e, Par, K60, W[60 & 0xf] = Wgen (W, 60));
+  SHA1STEP32 (e, a, b, c, d, Par, K60, W[61 & 0xf] = Wgen (W, 61));
+  SHA1STEP32 (d, e, a, b, c, Par, K60, W[62 & 0xf] = Wgen (W, 62));
+  SHA1STEP32 (c, d, e, a, b, Par, K60, W[63 & 0xf] = Wgen (W, 63));
+  SHA1STEP32 (b, c, d, e, a, Par, K60, W[64 & 0xf] = Wgen (W, 64));
+  SHA1STEP32 (a, b, c, d, e, Par, K60, W[65 & 0xf] = Wgen (W, 65));
+  SHA1STEP32 (e, a, b, c, d, Par, K60, W[66 & 0xf] = Wgen (W, 66));
+  SHA1STEP32 (d, e, a, b, c, Par, K60, W[67 & 0xf] = Wgen (W, 67));
+  SHA1STEP32 (c, d, e, a, b, Par, K60, W[68 & 0xf] = Wgen (W, 68));
+  SHA1STEP32 (b, c, d, e, a, Par, K60, W[69 & 0xf] = Wgen (W, 69));
+  SHA1STEP32 (a, b, c, d, e, Par, K60, W[70 & 0xf] = Wgen (W, 70));
+  SHA1STEP32 (e, a, b, c, d, Par, K60, W[71 & 0xf] = Wgen (W, 71));
+  SHA1STEP32 (d, e, a, b, c, Par, K60, W[72 & 0xf] = Wgen (W, 72));
+  SHA1STEP32 (c, d, e, a, b, Par, K60, W[73 & 0xf] = Wgen (W, 73));
+  SHA1STEP32 (b, c, d, e, a, Par, K60, W[74 & 0xf] = Wgen (W, 74));
+  SHA1STEP32 (a, b, c, d, e, Par, K60, W[75 & 0xf] = Wgen (W, 75));
+  SHA1STEP32 (e, a, b, c, d, Par, K60, W[76 & 0xf] = Wgen (W, 76));
+  SHA1STEP32 (d, e, a, b, c, Par, K60, W[77 & 0xf] = Wgen (W, 77));
+  SHA1STEP32 (c, d, e, a, b, Par, K60, W[78 & 0xf] = Wgen (W, 78));
+  SHA1STEP32 (b, c, d, e, a, Par, K60, W[79 & 0xf] = Wgen (W, 79));
+
+  /* Compute intermediate hash.
+     See FIPS PUB 180-4 paragraph 6.1.3 step 4. */
+  H[0] += a;
+  H[1] += b;
+  H[2] += c;
+  H[3] += d;
+  H[4] += e;
 }
 
 
-/* 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)
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+                 const uint8_t *data,
+                 size_t length)
 {
-  /* 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);
-}
+  struct sha1_ctx *const ctx = ctx_;
+  unsigned bytes_have; /**< Number of bytes in buffer */
 
+  mhd_assert ((data != NULL) || (length == 0));
 
-/* 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;
+  if (0 == length)
+    return; /* Do nothing */
 
-  /* Initialize the computation context.  */
-  sha1_init_ctx (&ctx);
+  /* Note: (count & (SHA1_BLOCK_SIZE-1))
+           equal (count % SHA1_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+  ctx->count += length;
 
-  /* Iterate over full file contents.  */
-  while (1)
+  if (0 != bytes_have)
   {
-    /* 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;
+    unsigned bytes_left = SHA1_BLOCK_SIZE - bytes_have;
+    if (length >= bytes_left)
+    {     /* Combine new data with the data in the buffer and
+             process the full block. */
+      memcpy (ctx->buffer + bytes_have,
+              data,
+              bytes_left);
+      data += bytes_left;
+      length -= bytes_left;
+      sha1_transform (ctx->H, ctx->buffer);
+      bytes_have = 0;
     }
-
-    /* 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);
+  while (SHA1_BLOCK_SIZE <= length)
+  {   /* Process any full blocks of new data directly,
+         without copying to the buffer. */
+    sha1_transform (ctx->H, data);
+    data += SHA1_BLOCK_SIZE;
+    length -= SHA1_BLOCK_SIZE;
+  }
 
-  /* Construct result in desired memory.  */
-  sha1_finish_ctx (&ctx, resblock);
-  return 0;
+  if (0 != length)
+  {   /* Copy incomplete block of new data (if any)
+         to the buffer. */
+    memcpy (ctx->buffer + bytes_have, data, length);
+  }
 }
 
 
-/* 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);
-}
-
+/**
+ * Size of "length" padding addition in bytes.
+ * See FIPS PUB 180-4 paragraph 5.1.1.
+ */
+#define SHA1_SIZE_OF_LEN_ADD (64 / 8)
 
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
 void
-sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx)
+MHD_SHA1_finish (void *ctx_,
+                 uint8_t digest[SHA1_DIGEST_SIZE])
 {
-  /* 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;
+  struct sha1_ctx *const ctx = ctx_;
+  uint64_t num_bits;   /**< Number of processed bits */
+  unsigned bytes_have; /**< Number of bytes in buffer */
+
+  num_bits = ctx->count << 3;
+  /* Note: (count & (SHA1_BLOCK_SIZE-1))
+           equals (count % SHA1_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+
+  /* Input data must be padded with bit "1" and with length of data in bits.
+     See FIPS PUB 180-4 paragraph 5.1.1. */
+  /* Data is always processed in form of bytes (not by individual bits),
+     therefore position of first padding bit in byte is always predefined 
(0x80). */
+  /* Buffer always have space at least for one byte (as full buffers are
+     processed immediately). */
+  ctx->buffer[bytes_have++] = 0x80;
+
+  if (SHA1_BLOCK_SIZE - bytes_have < SHA1_SIZE_OF_LEN_ADD)
+  {   /* No space in current block to put total length of message.
+         Pad current block with zeros and process it. */
+    if (SHA1_BLOCK_SIZE > bytes_have)
+      memset (ctx->buffer + bytes_have, 0, SHA1_BLOCK_SIZE - bytes_have);
+    /* Process full block. */
+    sha1_transform (ctx->H, ctx->buffer);
+    /* Start new block. */
+    bytes_have = 0;
   }
 
-  /* Process available complete blocks.  */
-  if (len >= 64)
+  /* Pad the rest of the buffer with zeros. */
+  memset (ctx->buffer + bytes_have, 0,
+          SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD - bytes_have);
+  /* Put the number of bits in the processed message as a big-endian value. */
+  _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD,
+                          num_bits);
+  /* Process the full final block. */
+  sha1_transform (ctx->H, ctx->buffer);
+
+  /* Put final hash/digest in BE mode */
+#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
+  if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
   {
-#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;
-    }
+    uint32_t alig_dgst[_SHA1_DIGEST_LENGTH];
+    _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+    _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+    _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+    _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+    _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+    /* Copy result to unaligned destination address */
+    memcpy (digest, alig_dgst, SHA1_DIGEST_SIZE);
   }
-
-  /* Move remaining bytes in internal buffer.  */
-  if (len > 0)
+  else
+#else  /* _MHD_PUT_32BIT_BE_UNALIGNED */
+  if (1)
+#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
   {
-    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;
+    _MHD_PUT_32BIT_BE (digest + 0 * SHA1_BYTES_IN_WORD, ctx->H[0]);
+    _MHD_PUT_32BIT_BE (digest + 1 * SHA1_BYTES_IN_WORD, ctx->H[1]);
+    _MHD_PUT_32BIT_BE (digest + 2 * SHA1_BYTES_IN_WORD, ctx->H[2]);
+    _MHD_PUT_32BIT_BE (digest + 3 * SHA1_BYTES_IN_WORD, ctx->H[3]);
+    _MHD_PUT_32BIT_BE (digest + 4 * SHA1_BYTES_IN_WORD, ctx->H[4]);
   }
-}
-
-
-/* --- 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;
-  }
+  /* Erase potentially sensitive data. */
+  memset (ctx, 0, sizeof(struct sha1_ctx));
 }
diff --git a/src/microhttpd_ws/sha1.h b/src/microhttpd_ws/sha1.h
index be0d190b..851a4429 100644
--- a/src/microhttpd_ws/sha1.h
+++ b/src/microhttpd_ws/sha1.h
@@ -1,145 +1,110 @@
-/* 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)
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+     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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/sha1.h
+ * @brief  Calculation of SHA-1 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA1_H
+#define MHD_SHA1_H 1
+
+#include "mhd_options.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.  */
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>  /* for size_t */
+#endif /* HAVE_STDDEF_H */
+
+/**
+ *  SHA-1 digest is kept internally as 5 32-bit words.
+ */
+#define _SHA1_DIGEST_LENGTH 5
+
+/**
+ * Number of bits in single SHA-1 word
+ */
+#define SHA1_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-1 word
+ */
+#define SHA1_BYTES_IN_WORD (SHA1_WORD_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-1 digest in bytes
+ */
+#define SHA1_DIGEST_SIZE (_SHA1_DIGEST_LENGTH * SHA1_BYTES_IN_WORD)
+
+/**
+ * Size of SHA-1 digest string in chars including termination NUL
+ */
+#define SHA1_DIGEST_STRING_SIZE ((SHA1_DIGEST_SIZE) * 2 + 1)
+
+/**
+ * Size of single processing block in bits
+ */
+#define SHA1_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of single processing block in bytes
+ */
+#define SHA1_BLOCK_SIZE (SHA1_BLOCK_SIZE_BITS / 8)
+
+
 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];
+  uint32_t H[_SHA1_DIGEST_LENGTH];    /**< Intermediate hash value / digest at 
end of calculation */
+  uint8_t buffer[SHA1_BLOCK_SIZE];    /**< SHA256 input data buffer */
+  uint64_t count;                     /**< number of bytes, mod 2^64 */
 };
 
-
-/* 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
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx must be a `struct sha1_ctx *`
+ */
+void
+MHD_SHA1_init (void *ctx_);
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+                 const uint8_t *data,
+                 size_t length);
+
+
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
+void
+MHD_SHA1_finish (void *ctx_,
+                 uint8_t digest[SHA1_DIGEST_SIZE]);
+
+#endif /* MHD_SHA1_H */
diff --git a/src/microhttpd_ws/test_websocket.c 
b/src/microhttpd_ws/test_websocket.c
index 0034eb4c..29a4661a 100644
--- a/src/microhttpd_ws/test_websocket.c
+++ b/src/microhttpd_ws/test_websocket.c
@@ -27,8 +27,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <time.h>
 
+#if SIZE_MAX >= 0x100000000
+  #define ENABLE_64BIT_TESTS 1
+#endif
+
 int disable_alloc = 0;
 size_t open_allocs = 0;
 
@@ -73,6 +78,20 @@ test_free (void*buf)
   free (buf);
 }
 
+/**
+ * Custom `rng()` function used for client mode tests
+ */
+static size_t
+test_rng (void*cls, void*buf, size_t buf_len)
+{
+  for (size_t i = 0; i < buf_len; ++i)
+  {
+    ((char*) buf) [i] = (char) (rand () % 0xFF);
+  }
+
+  return buf_len;
+}
+
 
 /**
  * Helper function which allocates a big amount of data
@@ -126,7 +145,14 @@ test_decode_single (unsigned int test_line,
   int ret = MHD_WEBSOCKET_STATUS_OK;
 
   /* initialize stream */
-  ret = MHD_websocket_stream_init (&ws, flags, max_payload_size);
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    flags,
+                                    max_payload_size,
+                                    malloc,
+                                    realloc,
+                                    free,
+                                    NULL,
+                                    test_rng);
   if (MHD_WEBSOCKET_STATUS_OK != ret)
   {
     fprintf (stderr,
@@ -267,7 +293,7 @@ test_decode_single (unsigned int test_line,
 
 /**
  * Test procedure for `MHD_websocket_stream_init()` and
- * `MHD_websocket_stream_init()2`
+ * `MHD_websocket_stream_init2()`
  */
 int
 test_inits ()
@@ -281,15 +307,19 @@ test_inits ()
     All valid flags
   
------------------------------------------------------------------------------
   */
-  /* Regular test: all valid flags for init */
+  /* Regular test: all valid flags for init (only the even ones work) */
   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) )
+    if (((0 == (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+        ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+        (NULL == ws))) ||
+        ((0 != (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+        ((MHD_WEBSOCKET_STATUS_OK == ret) ||
+        (NULL != ws))))
     {
       fprintf (stderr,
                "Init test failed in line %u for flags %d.\n",
@@ -312,7 +342,9 @@ test_inits ()
                                       0,
                                       test_malloc,
                                       test_realloc,
-                                      test_free);
+                                      test_free,
+                                      NULL,
+                                      test_rng);
     if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
         (NULL == ws) )
     {
@@ -361,7 +393,9 @@ test_inits ()
                                       0,
                                       test_malloc,
                                       test_realloc,
-                                      test_free);
+                                      test_free,
+                                      NULL,
+                                      NULL);
     if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
         (NULL != ws) )
     {
@@ -410,7 +444,9 @@ test_inits ()
                                     0,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
       (NULL == ws) )
   {
@@ -451,7 +487,9 @@ test_inits ()
                                     1,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
       (NULL == ws) )
   {
@@ -492,7 +530,9 @@ test_inits ()
                                     1000,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
       (NULL == ws) )
   {
@@ -506,6 +546,7 @@ test_inits ()
     MHD_websocket_stream_free (ws);
     ws = NULL;
   }
+#ifdef ENABLE_64BIT_TESTS
   /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init */
   ws = NULL;
   ret = MHD_websocket_stream_init (&ws,
@@ -533,7 +574,9 @@ test_inits ()
                                     (uint64_t) 0x7FFFFFFFFFFFFFFF,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
       (NULL == ws) )
   {
@@ -574,7 +617,9 @@ test_inits ()
                                     (uint64_t) 0x8000000000000000,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
       (NULL != ws) )
   {
@@ -588,6 +633,7 @@ test_inits ()
     MHD_websocket_stream_free (ws);
     ws = NULL;
   }
+#endif
 
   /*
   
------------------------------------------------------------------------------
@@ -621,7 +667,9 @@ test_inits ()
                                     0,
                                     test_malloc,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
       (NULL != ws) )
   {
@@ -643,7 +691,9 @@ test_inits ()
                                     0,
                                     NULL,
                                     test_realloc,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
       (NULL != ws) )
   {
@@ -665,7 +715,9 @@ test_inits ()
                                     0,
                                     test_malloc,
                                     NULL,
-                                    test_free);
+                                    test_free,
+                                    NULL,
+                                    NULL);
   if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
       (NULL != ws) )
   {
@@ -687,6 +739,8 @@ test_inits ()
                                     0,
                                     test_malloc,
                                     test_realloc,
+                                    NULL,
+                                    NULL,
                                     NULL);
   if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
       (NULL != ws) )
@@ -701,13 +755,109 @@ test_inits ()
     MHD_websocket_stream_free (ws);
     ws = NULL;
   }
+  /* Regular test: rng given for server mode (will be ignored) */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free,
+                                    NULL,
+                                    test_rng);
+  if ((MHD_WEBSOCKET_STATUS_OK != 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;
+  }
+  /* Regular test: cls_rng given for server mode (will be ignored) */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_SERVER
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free,
+                                    (void*) 12345,
+                                    test_rng);
+  if ((MHD_WEBSOCKET_STATUS_OK != 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;
+  }
+  /* Regular test: rng given for client mode */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_CLIENT
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free,
+                                    NULL,
+                                    test_rng);
+  if ((MHD_WEBSOCKET_STATUS_OK != 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: rng not given for client mode */
+  ws = NULL;
+  ret = MHD_websocket_stream_init2 (&ws,
+                                    MHD_WEBSOCKET_FLAG_CLIENT
+                                    | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+                                    0,
+                                    test_malloc,
+                                    test_realloc,
+                                    test_free,
+                                    NULL,
+                                    NULL);
+  if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+      (NULL != ws) )
+  {
+    fprintf (stderr,
+             "Init test failed in line %u %u.\n",
+             (unsigned int) __LINE__, ret);
+    ++failed;
+  }
+  if (NULL != ws)
+  {
+    MHD_websocket_stream_free (ws);
+    ws = NULL;
+  }
 
   return failed != 0 ? 0x01 : 0x00;
 }
 
 
 /**
- * Test procedure for `MHD_websocket_create_accept()`
+ * Test procedure for `MHD_websocket_create_accept_header()`
  */
 int
 test_accept ()
@@ -723,8 +873,8 @@ test_accept ()
   */
   /* Regular test: Test case from RFC6455 4.2.2 */
   memset (accept_key, 0, 29);
-  ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==",
-                                     accept_key);
+  ret = MHD_websocket_create_accept_header ("dGhlIHNhbXBsZSBub25jZQ==",
+                                            accept_key);
   if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
       (0 != memcmp (accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", 29)))
   {
@@ -741,9 +891,9 @@ test_accept ()
   */
   /* 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)
+  ret = MHD_websocket_create_accept_header (NULL,
+                                            accept_key);
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
   {
     fprintf (stderr,
              "Accept test failed in line %u.\n",
@@ -752,8 +902,8 @@ test_accept ()
   }
   /* Fail test: missing accept variable */
   memset (accept_key, 0, 29);
-  ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==",
-                                     NULL);
+  ret = MHD_websocket_create_accept_header ("dGhlIHNhbXBsZSBub25jZQ==",
+                                            NULL);
   if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret)
   {
     fprintf (stderr,
@@ -1038,7 +1188,7 @@ test_decodes ()
                                 12,
                                 NULL,
                                 0,
-                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 6);
   /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 2nd call */
@@ -1080,7 +1230,7 @@ test_decodes ()
                                 18,
                                 "\x01\x02\x03",
                                 3,
-                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 9);
   /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 2nd call */
@@ -1097,6 +1247,62 @@ test_decodes ()
                                 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
                                 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\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+                                36,
+                                "\x01\x02\x03",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_FIRST_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\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+                                36,
+                                "\x04\x05\x06",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                18);
+  /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 3rd call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                3,
+                                0,
+                                
"\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+                                36,
+                                "\x07\x08\x09",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                27);
+  /* Regular test: Fragmented binary frame without payload, fragments to the 
caller, 4th call */
+  failed += test_decode_single (__LINE__,
+                                MHD_WEBSOCKET_FLAG_SERVER
+                                | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+                                0,
+                                4,
+                                0,
+                                
"\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+                                36,
+                                "\x0A\x0B\x0C",
+                                3,
+                                MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                36);
   /* Regular test: Binary frame with bytes which look like invalid UTF-8 
character */
   failed += test_decode_single (__LINE__,
                                 MHD_WEBSOCKET_FLAG_SERVER
@@ -1167,7 +1373,7 @@ test_decodes ()
                                 17,
                                 "H\xC3",
                                 2,
-                                MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 8);
   /* Regular test: Fragmented binary frame with bytes which look like valid 
UTF-8 sequence,
@@ -1802,7 +2008,7 @@ test_decodes ()
                                 17,
                                 "Hel",
                                 3,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 9);
   /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, second call */
@@ -1833,6 +2039,48 @@ test_decodes ()
                                 MHD_WEBSOCKET_STATUS_OK,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 17);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, 1st 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+                                23,
+                                "Hel",
+                                3,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                9);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, 2nd 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+                                23,
+                                "l",
+                                1,
+                                MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                16);
+  /* Regular test: Fragmented, masked text frame, we are the server and want 
fragments, 3rd 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+                                23,
+                                "o",
+                                1,
+                                MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+                                MHD_WEBSOCKET_VALIDITY_VALID,
+                                23);
 
 
   /*
@@ -2255,6 +2503,7 @@ test_decodes ()
     free (buf2);
     buf2 = NULL;
   }
+#ifdef ENABLE_64BIT_TESTS
   /* Edge test (success): Maximum allowed length (here is only the header 
checked) */
   failed += test_decode_single (__LINE__,
                                 MHD_WEBSOCKET_FLAG_SERVER
@@ -2269,6 +2518,23 @@ test_decodes ()
                                 MHD_WEBSOCKET_STATUS_OK,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 10);
+#else
+  /* Edge test (fail): Maximum allowed length
+     (the size is allowed, but the system cannot handle this amount of memory) 
*/
+  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_MAXIMUM_SIZE_EXCEEDED,
+                                MHD_WEBSOCKET_VALIDITY_INVALID,
+                                10);
+#endif
   /* Edge test (fail): Too big payload length */
   failed += test_decode_single (__LINE__,
                                 MHD_WEBSOCKET_FLAG_SERVER
@@ -2447,7 +2713,7 @@ test_decodes ()
                                 17,
                                 "Hel",
                                 3,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 9);
   /* Edge test (success): Fragmented frames with the sum of payload greater 
than
@@ -3247,7 +3513,7 @@ test_decodes ()
                                 28,
                                 "This is my n",
                                 12,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 19);
   /* Regular test: UTF-8 sequence between fragments, fragmentation for the 
caller, 2nd call */
@@ -3276,7 +3542,7 @@ test_decodes ()
                                 14,
                                 NULL,
                                 0,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 7);
   /* Edge test (success): UTF-8 sequence between fragments, but nothing 
before, fragmentation for the caller, 2nd call */
@@ -3664,7 +3930,7 @@ test_decodes ()
                                 35,
                                 "This ",
                                 5,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 11);
   /* Regular test: Fragmented text frame mixed with one ping frame, the caller 
wants fragments (2nd call) */
@@ -3734,7 +4000,7 @@ test_decodes ()
                                 36,
                                 "This ",
                                 5,
-                                MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT,
+                                MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
                                 MHD_WEBSOCKET_VALIDITY_VALID,
                                 11);
   /* Fail test: Fragmented text frame mixed with one non-fragmented binary 
frame; the caller wants fragments; 2nd call */
@@ -4106,7 +4372,7 @@ test_decodes ()
                                   &streambuf_read_len,
                                   &payload,
                                   &payload_len);
-      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) ||
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT != ret) ||
           (3 != payload_len) ||
           (NULL == payload) ||
           (0 != memcmp ("Hel", payload, 3 + 1)))
@@ -4155,7 +4421,7 @@ test_decodes ()
                                   &streambuf_read_len,
                                   &payload,
                                   &payload_len);
-      if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) ||
+      if ((MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT != ret) ||
           (2 != payload_len) ||
           (NULL == payload) ||
           (0 != memcmp ("He", payload, 2 + 1)))
@@ -4195,6 +4461,8 @@ test_decodes ()
         MHD_websocket_free (ws, payload);
         payload = NULL;
       }
+
+      MHD_websocket_stream_free (ws);
     }
     else
     {
@@ -4439,7 +4707,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       size_t streambuf_read_len = 0;
       char*payload = NULL;
@@ -4484,7 +4754,9 @@ test_decodes ()
                                                                  0,
                                                                  test_malloc,
                                                                  test_realloc,
-                                                                 test_free))
+                                                                 test_free,
+                                                                 NULL,
+                                                                 NULL))
       {
         /* Failure test: No memory allocation after fragmented frame */
         disable_alloc = 0;
@@ -4623,7 +4895,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       ret = MHD_websocket_decode (ws,
                                   "\x81\x85\x00\x00\x00\x00Hel",
@@ -4674,7 +4948,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       ret = MHD_websocket_decode (ws,
                                   "\x88\x85\x00\x00\x00\x00Hel",
@@ -4725,7 +5001,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       ret = MHD_websocket_decode (ws,
                                   "\x01\x85\x00\x00\x00\x00Hello",
@@ -4775,7 +5053,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       ret = MHD_websocket_decode (ws,
                                   "\x01\x85\x00\x00\x00\x00Hello",
@@ -4841,7 +5121,9 @@ test_decodes ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       ret = MHD_websocket_decode (ws,
                                   "\x01\x85\x00\x00\x00\x00Hello",
@@ -4929,9 +5211,14 @@ test_encodes_text ()
   size_t frame_len = 0;
   int utf8_step = 0;
 
-  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
-                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
-                                                            0))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
+                                                             
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     fprintf (stderr,
              "No encode text tests possible due to failed stream init in line 
%u\n",
@@ -5742,6 +6029,7 @@ test_encodes_text ()
     free (buf2);
     buf2 = NULL;
   }
+#ifdef ENABLE_64BIT_TESTS
   /* Fail test: frame_len is greater than 0x7FFFFFFFFFFFFFFF
      (this is the maximum allowed payload size) */
   frame_len = 0;
@@ -5766,6 +6054,7 @@ test_encodes_text ()
     MHD_websocket_free (wss, frame);
     frame = NULL;
   }
+#endif
 
   /*
   
------------------------------------------------------------------------------
@@ -6188,7 +6477,9 @@ test_encodes_text ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       /* Fail test: allocation while no memory available */
       disable_alloc = 1;
@@ -6276,9 +6567,14 @@ test_encodes_binary ()
   char*frame = NULL;
   size_t frame_len = 0;
 
-  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
-                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
-                                                            0))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
+                                                             
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     fprintf (stderr,
              "No encode binary tests possible due to failed stream init in 
line %u\n",
@@ -6688,6 +6984,7 @@ test_encodes_binary ()
     free (buf2);
     buf2 = NULL;
   }
+#ifdef ENABLE_64BIT_TESTS
   /* Fail test: `frame_len` is greater than 0x7FFFFFFFFFFFFFFF
      (this is the maximum allowed payload size) */
   frame_len = 0;
@@ -6711,6 +7008,7 @@ test_encodes_binary ()
     MHD_websocket_free (wss, frame);
     frame = NULL;
   }
+#endif
 
   /*
   
------------------------------------------------------------------------------
@@ -6882,7 +7180,9 @@ test_encodes_binary ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       /* Fail test: allocation while no memory available */
       disable_alloc = 1;
@@ -6968,18 +7268,28 @@ test_encodes_close ()
   char*frame = NULL;
   size_t frame_len = 0;
 
-  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
-                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
-                                                            0))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
+                                                             
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     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))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wss,
+                                                             
MHD_WEBSOCKET_FLAG_SERVER,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     fprintf (stderr,
              "No encode close tests possible due to failed stream init in line 
%u\n",
@@ -7623,7 +7933,9 @@ test_encodes_close ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       /* Fail test: allocation while no memory available */
       disable_alloc = 1;
@@ -7709,9 +8021,14 @@ test_encodes_ping ()
   char*frame = NULL;
   size_t frame_len = 0;
 
-  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
-                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
-                                                            0))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
+                                                             
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     fprintf (stderr,
              "No encode ping tests possible due to failed stream init in line 
%u\n",
@@ -8156,7 +8473,9 @@ test_encodes_ping ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       /* Fail test: allocation while no memory available */
       disable_alloc = 1;
@@ -8240,9 +8559,14 @@ test_encodes_pong ()
   char*frame = NULL;
   size_t frame_len = 0;
 
-  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc,
-                                                            
MHD_WEBSOCKET_FLAG_CLIENT,
-                                                            0))
+  if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
+                                                             
MHD_WEBSOCKET_FLAG_CLIENT,
+                                                             0,
+                                                             malloc,
+                                                             realloc,
+                                                             free,
+                                                             NULL,
+                                                             test_rng))
   {
     fprintf (stderr,
              "No encode pong tests possible due to failed stream init in line 
%u\n",
@@ -8687,7 +9011,9 @@ test_encodes_pong ()
                                                                0,
                                                                test_malloc,
                                                                test_realloc,
-                                                               test_free))
+                                                               test_free,
+                                                               NULL,
+                                                               NULL))
     {
       /* Fail test: allocation while no memory available */
       disable_alloc = 1;
@@ -8955,6 +9281,793 @@ test_split_close_reason ()
 }
 
 
+/**
+ * Test procedure for `MHD_websocket_check_http_version()`
+ */
+int
+test_check_http_version ()
+{
+  int failed = 0;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    Version check with valid HTTP version syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: HTTP/1.1 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.1");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: HTTP/1.2 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.2");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: HTTP/1.10 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.10");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: HTTP/2.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/2.0");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: HTTP/3.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/3.0");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: HTTP/1.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.0");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: HTTP/0.9 */
+  ret = MHD_websocket_check_http_version ("HTTP/0.9");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Version check edge cases
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): HTTP/123.45 */
+  ret = MHD_websocket_check_http_version ("HTTP/123.45");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/1.45 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.45");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/01.1 */
+  ret = MHD_websocket_check_http_version ("HTTP/01.1");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/0001.1 */
+  ret = MHD_websocket_check_http_version ("HTTP/0001.1");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/1.01 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.01");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/1.0001 */
+  ret = MHD_websocket_check_http_version ("HTTP/1.0001");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/0001.0001 */
+  ret = MHD_websocket_check_http_version ("HTTP/0001.0001");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/2.000 */
+  ret = MHD_websocket_check_http_version ("HTTP/2.000");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): HTTP/0.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/0.0");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): HTTP/00.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/00.0");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): HTTP/00.0 */
+  ret = MHD_websocket_check_http_version ("HTTP/0.00");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Invalid version syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: (empty string) */
+  ret = MHD_websocket_check_http_version ("");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: http/1.1 */
+  ret = MHD_websocket_check_http_version ("http/1.1");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: "HTTP / 1.1" */
+  ret = MHD_websocket_check_http_version ("HTTP / 1.1");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: NULL as version */
+  ret = MHD_websocket_check_http_version (NULL);
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_http_version test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x200 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_connection_header()`
+ */
+int
+test_check_connection_header ()
+{
+  int failed = 0;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    Check with valid Connection header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: Upgrade */
+  ret = MHD_websocket_check_connection_header ("Upgrade");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Regular test: keep-alive, Upgrade */
+  ret = MHD_websocket_check_connection_header ("keep-alive, Upgrade");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: keep-alive */
+  ret = MHD_websocket_check_connection_header ("keep-alive");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: close */
+  ret = MHD_websocket_check_connection_header ("close");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Connection check edge cases
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): keep-alive,Upgrade */
+  ret = MHD_websocket_check_connection_header ("keep-alive,Upgrade");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Upgrade, keep-alive */
+  ret = MHD_websocket_check_connection_header ("Upgrade, keep-alive");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Upgrade,keep-alive */
+  ret = MHD_websocket_check_connection_header ("Upgrade,keep-alive");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Transfer-Encoding,Upgrade,keep-alive */
+  ret = MHD_websocket_check_connection_header 
("Transfer-Encoding,Upgrade,keep-alive");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Transfer-Encoding  ,  Upgrade  ,  keep-alive */
+  ret = MHD_websocket_check_connection_header ("Transfer-Encoding  ,  Upgrade  
,  keep-alive");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): upgrade */
+  ret = MHD_websocket_check_connection_header ("upgrade");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): UPGRADE */
+  ret = MHD_websocket_check_connection_header ("UPGRADE");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): All allowed token characters, then upgrade token */
+  ret = MHD_websocket_check_connection_header 
("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,Upgrade");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Different, allowed whitespaces */
+  ret = MHD_websocket_check_connection_header (" \tUpgrade \t");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_connection_header ("\rUpgrade");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_connection_header ("\nUpgrade");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_connection_header ("\vUpgrade");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_connection_header ("\fUpgrade");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Invalid header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: (empty string) */
+  ret = MHD_websocket_check_connection_header ("");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: (Disallowed) multiple word token with the term "Upgrade" in it 
*/
+  ret = MHD_websocket_check_connection_header ("Upgrade or Downgrade");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: Invalid characters */
+  ret = MHD_websocket_check_connection_header ("\"Upgrade\"");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: NULL as connection */
+  ret = MHD_websocket_check_connection_header (NULL);
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_connection_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x400 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_upgrade_header()`
+ */
+int
+test_check_upgrade_header ()
+{
+  int failed = 0;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    Check with valid Upgrade header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: websocket */
+  ret = MHD_websocket_check_upgrade_header ("websocket");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: HTTP/2.0 */
+  ret = MHD_websocket_check_upgrade_header ("HTTP/2.0");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Upgrade check edge cases
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (success): websocket,HTTP/2.0 */
+  ret = MHD_websocket_check_upgrade_header ("websocket,HTTP/2.0");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): websocket ,HTTP/2.0 */
+  ret = MHD_websocket_check_upgrade_header (" websocket ,HTTP/2.0");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): HTTP/2.0, websocket */
+  ret = MHD_websocket_check_upgrade_header ("HTTP/2.0, websocket ");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): websocket/13 */
+  ret = MHD_websocket_check_upgrade_header ("websocket/13");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): WeBsOcKeT */
+  ret = MHD_websocket_check_upgrade_header ("WeBsOcKeT");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): WEBSOCKET */
+  ret = MHD_websocket_check_upgrade_header ("WEBSOCKET");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): All allowed token characters plus /, then websocket 
keyowrd */
+  ret = MHD_websocket_check_upgrade_header 
("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/,websocket");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (success): Different, allowed whitespaces */
+  ret = MHD_websocket_check_upgrade_header (" \twebsocket \t");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_upgrade_header ("\rwebsocket");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_upgrade_header ("\nwebsocket");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_upgrade_header ("\vwebsocket");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): Different, disallowed whitespaces */
+  ret = MHD_websocket_check_upgrade_header ("\fwebsocket");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Invalid header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: (empty string) */
+  ret = MHD_websocket_check_upgrade_header ("");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: (Disallowed) multiple word token with the term "websocket" in 
it */
+  ret = MHD_websocket_check_upgrade_header ("websocket or something");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: Invalid characters */
+  ret = MHD_websocket_check_upgrade_header ("\"websocket\"");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: NULL as upgrade */
+  ret = MHD_websocket_check_upgrade_header (NULL);
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_upgrade_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x800 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_version_header()`
+ */
+int
+test_check_version_header ()
+{
+  int failed = 0;
+  int ret;
+
+  /*
+  
------------------------------------------------------------------------------
+    Check with valid Upgrade header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Regular test: 13 */
+  ret = MHD_websocket_check_version_header ("13");
+  if (MHD_WEBSOCKET_STATUS_OK != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Version check edge cases
+  
------------------------------------------------------------------------------
+  */
+  /* Edge test (fail): 14 */
+  ret = MHD_websocket_check_version_header ("14");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): 12 */
+  ret = MHD_websocket_check_version_header ("12");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): 0 */
+  ret = MHD_websocket_check_version_header ("1");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): 1 */
+  ret = MHD_websocket_check_version_header ("1");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): 130 */
+  ret = MHD_websocket_check_version_header ("130");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Edge test (fail): " 13" */
+  ret = MHD_websocket_check_version_header (" 13");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Invalid header syntax
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: (empty string) */
+  ret = MHD_websocket_check_version_header ("");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+  /* Fail test: Invalid characters */
+  ret = MHD_websocket_check_version_header ("abc");
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  /*
+  
------------------------------------------------------------------------------
+    Missing parameters
+  
------------------------------------------------------------------------------
+  */
+  /* Fail test: NULL as version */
+  ret = MHD_websocket_check_version_header (NULL);
+  if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+  {
+    fprintf (stderr,
+             "check_version_header test failed in line %u.\n",
+             (unsigned int) __LINE__);
+    ++failed;
+  }
+
+  return failed != 0 ? 0x1000 : 0x00;
+}
+
+
 int
 main (int argc, char *const *argv)
 {
@@ -8962,7 +10075,7 @@ main (int argc, char *const *argv)
   (void) argc; (void) argv;  /* Unused. Silent compiler warning. */
 
   /* seed random number generator */
-  MHD_websocket_srand ((unsigned long) time (NULL));
+  srand ((unsigned long) time (NULL));
 
   /* perform tests */
   errorCount += test_inits ();
@@ -8974,6 +10087,10 @@ main (int argc, char *const *argv)
   errorCount += test_encodes_ping ();
   errorCount += test_encodes_pong ();
   errorCount += test_split_close_reason ();
+  errorCount += test_check_http_version ();
+  errorCount += test_check_connection_header ();
+  errorCount += test_check_upgrade_header ();
+  errorCount += test_check_version_header ();
 
   /* output result */
   if (errorCount != 0)
diff --git a/src/microhttpd_ws/test_websocket_browser.c 
b/src/microhttpd_ws/test_websocket_browser.c
new file mode 100644
index 00000000..dfbcd116
--- /dev/null
+++ b/src/microhttpd_ws/test_websocket_browser.c
@@ -0,0 +1,563 @@
+/*
+     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_browser.c
+ * @brief  Testcase for WebSocket decoding/encoding with external browser
+ * @author David Gausmann
+ */
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock2.h>
+#endif
+#include "microhttpd.h"
+#include "microhttpd_ws.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
+#include <errno.h>
+
+#define PORT 80
+
+#define PAGE \
+  "<!DOCTYPE html>\n" \
+  "<html>\n" \
+  "<head>\n" \
+  "<meta charset=\"UTF-8\">\n" \
+  "<title>Websocket External Test with Webbrowser</title>\n" \
+  "<script>\n" \
+  "\n" \
+  "let current_mode  = 0;\n" \
+  "let current_step  = 0;\n" \
+  "let sent_payload  = null;\n" \
+  "let charset       = 
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_!@%&/\\\\';\n" 
\
+  "let step_to_bytes = [ 0, 1, 2, 3, 122, 123, 124, 125, 126, 127, 128, 32766, 
32767, 32768, 65534, 65535, 65536, 65537, 1048576, 10485760 ];\n" \
+  "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
+    "  + '://' +\n" \
+  "          window.location.host + '/websocket';\n" \
+  "let socket = null;\n" \
+  "\n" \
+  "window.onload = function (event) {\n" \
+  "  if (!window.WebSocket) {\n" \
+  "    document.write ('ERROR: The WebSocket class is not supported by your 
browser.<br>');\n" \
+  "  }\n" \
+  "  if (!window.fetch) {\n" \
+  "    document.write ('ERROR: The fetch-API is not supported by your 
browser.<br>');\n" \
+  "  }\n" \
+  "  document.write ('Starting tests.<br>');\n" \
+  "  runTest ();\n" \
+  "}\n" \
+  "\n" \
+  "function runTest () {\n" \
+  "  switch (current_mode) {\n" \
+  "  case 0:\n" \
+  "    document.write ('TEXT');\n" \
+  "    break;\n" \
+  "  case 1:\n" \
+  "    document.write ('BINARY');\n" \
+  "    break;\n" \
+  "  }\n" \
+  "  document.write (', ' + step_to_bytes[current_step] + ' Bytes: ');\n" \
+  "  socket = new WebSocket(url);\n" \
+  "  socket.binaryType = 'arraybuffer';\n" \
+  "  socket.onopen = function (event) {\n" \
+  "    switch (current_mode) {\n" \
+  "    case 0:\n" \
+  "      sent_payload = randomText (step_to_bytes[current_step]);\n" \
+  "      socket.send (sent_payload);\n" \
+  "      break;\n" \
+  "    case 1:\n" \
+  "      sent_payload = randomBinary (step_to_bytes[current_step]);\n" \
+  "      socket.send (sent_payload);\n" \
+  "      break;\n" \
+  "    }\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onclose = function (event) {\n" \
+  "    socket.onmessage = null;\n" \
+  "    socket.onclose   = null;\n" \
+  "    socket.onerror   = null;\n" \
+  "    document.write ('CLOSED unexpectedly.<br>');\n" \
+  "    notifyError ();\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onerror = function (event) {\n" \
+  "    socket.onmessage = null;\n" \
+  "    socket.onclose   = null;\n" \
+  "    socket.onerror   = null;\n" \
+  "    document.write ('ERROR.<br>');\n" \
+  "    notifyError ();\n" \
+  "  }\n" \
+  "\n" \
+  "  socket.onmessage = async function (event) {\n" \
+  "    if (compareData (event.data, sent_payload)) {\n" \
+  "      document.write ('SUCCESS.<br>');\n" \
+  "      socket.onmessage = null;\n" \
+  "      socket.onclose   = null;\n" \
+  "      socket.onerror   = null;\n" \
+  "      socket.close();\n" \
+  "      socket = null;\n" \
+  "      if (step_to_bytes.length <= ++current_step) {\n" \
+  "        current_step = 0;\n" \
+  "        if (1 < ++current_mode) {\n" \
+  "          document.write ('FINISHED ALL TESTS.<br>');\n" \
+  "          return;\n" \
+  "        }\n" \
+  "      }\n" \
+  "      runTest ();\n" \
+  "    }" \
+  "  }\n" \
+  "}\n" \
+  "\n" \
+  "function compareData (data, data2) {\n" \
+  "  if (typeof (data) === 'string' && typeof (data2) === 'string') {\n" \
+  "    return (data === data2); \n" \
+  "  } \n" \
+  "  else if ((data instanceof ArrayBuffer) && (data2 instanceof ArrayBuffer)) 
{\n" \
+  "    let view1 = new Uint8Array (data);\n" \
+  "    let view2 = new Uint8Array (data2);\n" \
+  "    if (view1.length != view2.length)\n" \
+  "      return false;\n" \
+  "    for (let i = 0; i < view1.length; ++i) {\n" \
+  "      if (view1[i] !== view2[i])\n" \
+  "        return false;\n" \
+  "    }\n" \
+  "    return true;\n" \
+  "  }\n" \
+  "  else\n" \
+  "  {\n" \
+  "    return false;\n" \
+  "  }\n" \
+  "}\n" \
+  "\n" \
+  "function randomText (length) {\n" \
+  "  let result = new Array (length);\n" \
+  "  for (let i = 0; i < length; ++i)\n" \
+  "    result [i] = charset [~~(Math.random () * charset.length)];\n" \
+  "  return result.join ('');\n" \
+  "}\n" \
+  "\n" \
+  "function randomBinary (length) {\n" \
+  "  let buffer = new ArrayBuffer (length);\n" \
+  "  let view   = new Uint8Array (buffer);\n" \
+  "  for (let i = 0; i < length; ++i)\n" \
+  "    view [i] = ~~(Math.random () * 256);\n" \
+  "  return buffer;\n" \
+  "}\n" \
+  "\n" \
+  "function notifyError () {\n" \
+  "  fetch('error/' + (0 == current_mode ? 'text' : 'binary') + '/' + 
step_to_bytes[current_step]);\n" \
+  "}\n" \
+  "\n" \
+  "</script>\n" \
+  "</head>\n" \
+  "<body>\n" \
+  "</body>\n" \
+  "</html>"
+
+#define PAGE_NOT_FOUND \
+  "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+  "Invalid WebSocket request!"
+
+static void
+send_all (MHD_socket fd,
+          const char *buf,
+          size_t len);
+static void
+make_blocking (MHD_socket fd);
+
+static void
+upgrade_handler (void *cls,
+                 struct MHD_Connection *connection,
+                 void *con_cls,
+                 const char *extra_in,
+                 size_t extra_in_size,
+                 MHD_socket fd,
+                 struct MHD_UpgradeResponseHandle *urh)
+{
+  /* make the socket blocking (operating-system-dependent code) */
+  make_blocking (fd);
+
+  /* create a websocket stream for this connection */
+  struct MHD_WebSocketStream* ws;
+  int result = MHD_websocket_stream_init (&ws,
+                                          0,
+                                          0);
+  if (0 != result)
+  {
+    /* Couldn't create the websocket stream.
+     * So we close the socket and leave
+     */
+    MHD_upgrade_action (urh,
+                        MHD_UPGRADE_ACTION_CLOSE);
+    return;
+  }
+
+  /* Let's wait for incoming data */
+  const size_t buf_len = 256;
+  char buf[buf_len];
+  ssize_t got;
+  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+  {
+    got = recv (fd,
+                buf,
+                sizeof (buf),
+                0);
+    if (0 >= got)
+    {
+      /* the TCP/IP socket has been closed */
+      fprintf (stderr,
+               "Error (The socket has been closed unexpectedly)\n");
+      break;
+    }
+
+    /* parse the entire received data */
+    size_t buf_offset = 0;
+    while (buf_offset < (size_t) got)
+    {
+      size_t new_offset  = 0;
+      char *payload_data = NULL;
+      size_t payload_len = 0;
+      char *frame_data   = NULL;
+      size_t frame_len   = 0;
+      int status = MHD_websocket_decode (ws,
+                                         buf + buf_offset,
+                                         ((size_t) got) - buf_offset,
+                                         &new_offset,
+                                         &payload_data,
+                                         &payload_len);
+      if (0 > status)
+      {
+        /* an error occurred and the connection must be closed */
+        printf ("Decoding failed: status=%d, passed=%u\n", status, ((size_t) 
got) - buf_offset);
+        if (NULL != payload_data)
+        {
+          MHD_websocket_free (ws, payload_data);
+        }
+        break;
+      }
+      else
+      {
+        buf_offset += new_offset;
+        if (0 < status)
+        {
+          /* the frame is complete */
+          printf ("Decoding succeeded: type=%d, passed=%u, parsed=%u, 
payload_len=%d\n", status, ((size_t) got) - buf_offset, new_offset, 
payload_len);
+          switch (status)
+          {
+          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+          case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
+            /* The client has sent some data. */
+            if (NULL != payload_data || 0 == payload_len)
+            {
+              /* Send the received data back to the client */
+              if (MHD_WEBSOCKET_STATUS_TEXT_FRAME == status)
+              {
+                result = MHD_websocket_encode_text (ws,
+                                                    payload_data,
+                                                    payload_len,
+                                                    0,
+                                                    &frame_data,
+                                                    &frame_len,
+                                                    NULL);
+              }
+              else
+              {
+                result = MHD_websocket_encode_binary (ws,
+                                                    payload_data,
+                                                    payload_len,
+                                                    0,
+                                                    &frame_data,
+                                                    &frame_len);
+              }
+              if (0 == result)
+              {
+                send_all (fd,
+                          frame_data,
+                          frame_len);
+              }
+            }
+            else
+            {
+              /* should never happen */
+              fprintf (stderr,
+                       "Error (Empty buffer with payload_len != 0)\n");
+            }
+            break;
+
+          default:
+            /* Other frame types are ignored
+             * in this test script.
+             */
+            break;
+          }
+        }
+        if (NULL != payload_data)
+        {
+          MHD_websocket_free (ws, payload_data);
+        }
+        if (NULL != frame_data)
+        {
+          MHD_websocket_free (ws, frame_data);
+        }
+      }
+    }
+  }
+
+  /* free the websocket stream */
+  MHD_websocket_stream_free (ws);
+
+  /* close the socket when it is not needed anymore */
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CLOSE);
+}
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+          const char *buf,
+          size_t len)
+{
+  ssize_t ret;
+  size_t off;
+
+  for (off = 0; off < len; off += ret)
+  {
+    ret = send (fd,
+                &buf[off],
+                (int) (len - off),
+                0);
+    if (0 > ret)
+    {
+      if (EAGAIN == errno)
+      {
+        ret = 0;
+        continue;
+      }
+      break;
+    }
+    if (0 == ret)
+      break;
+  }
+}
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#ifndef _WIN32
+  int flags;
+
+  flags = fcntl (fd, F_GETFL);
+  if (-1 == flags)
+    return;
+  if ((flags & ~O_NONBLOCK) != flags)
+    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+      abort ();
+#else
+  unsigned long flags = 0;
+
+  ioctlsocket (fd, FIONBIO, &flags);
+#endif
+}
+
+static enum MHD_Result
+access_handler (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  (void) cls;               /* Unused. Silent compiler warning. */
+  (void) upload_data;       /* Unused. Silent compiler warning. */
+  (void) upload_data_size;  /* Unused. Silent compiler warning. */
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+  {
+    /* do never respond on first call */
+    *ptr = &aptr;
+    return MHD_YES;
+  }
+  *ptr = NULL;                  /* reset when done */
+
+  if (0 == strcmp (url, "/"))
+  {
+    /* Default page for visiting the server */
+    struct MHD_Response *response = MHD_create_response_from_buffer (
+                                      strlen (PAGE),
+                                      PAGE,
+                                      MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_OK,
+                              response);
+    MHD_destroy_response (response);
+  }
+  else if (0 == strncmp (url, "/error/", 7))
+  {
+    /* Report error */
+    fprintf (stderr, "Error in test (%s)\n", url + 7);
+
+    struct MHD_Response *response = MHD_create_response_from_buffer (
+                                      0,
+                                      "",
+                                      MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_OK,
+                              response);
+    MHD_destroy_response (response);
+  }
+  else if (0 == strcmp (url, "/websocket"))
+  {
+    char is_valid = 1;
+    const char* value = NULL;
+    char sec_websocket_accept[29];
+
+    if (0 != MHD_websocket_check_http_version (version))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_CONNECTION);
+    if (0 != MHD_websocket_check_connection_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_UPGRADE);
+    if (0 != MHD_websocket_check_upgrade_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         
MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+    if (0 != MHD_websocket_check_version_header (value))
+    {
+      is_valid = 0;
+    }
+    value = MHD_lookup_connection_value (connection,
+                                         MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+    {
+      is_valid = 0;
+    }
+
+    if (1 == is_valid)
+    {
+      /* upgrade the connection */
+      response = MHD_create_response_for_upgrade (&upgrade_handler,
+                                                  NULL);
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_CONNECTION,
+                               "Upgrade");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_UPGRADE,
+                               "websocket");
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+                               sec_websocket_accept);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_SWITCHING_PROTOCOLS,
+                                response);
+      MHD_destroy_response (response);
+    }
+    else
+    {
+      /* return error page */
+      struct MHD_Response*response = MHD_create_response_from_buffer (
+                                       strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+                                       PAGE_INVALID_WEBSOCKET_REQUEST,
+                                       MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_BAD_REQUEST,
+                                response);
+      MHD_destroy_response (response);
+    }
+  }
+  else
+  {
+    struct MHD_Response*response = MHD_create_response_from_buffer (
+                                     strlen (PAGE_NOT_FOUND),
+                                     PAGE_NOT_FOUND,
+                                     MHD_RESPMEM_PERSISTENT);
+    ret = MHD_queue_response (connection,
+                              MHD_HTTP_NOT_FOUND,
+                              response);
+    MHD_destroy_response (response);
+  }
+
+  return ret;
+}
+
+int
+main (int argc,
+      char *const *argv)
+{
+  (void) argc;               /* Unused. Silent compiler warning. */
+  (void) argv;               /* Unused. Silent compiler warning. */
+  struct MHD_Daemon *daemon;
+
+  daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
+                             MHD_USE_THREAD_PER_CONNECTION |
+                             MHD_ALLOW_UPGRADE |
+                             MHD_USE_ERROR_LOG,
+                             PORT, NULL, NULL,
+                             &access_handler, NULL,
+                             MHD_OPTION_END);
+
+  if (NULL == daemon)
+  {
+    fprintf (stderr, "Error (Couldn't start daemon for testing)\n");
+    return 1;
+  }
+  printf("The server is listening now.\n");
+  printf("Access the server now with a websocket-capable webbrowser.\n\n");
+  printf("Press return to close.\n");
+
+  (void) getc (stdin);
+
+  MHD_stop_daemon (daemon);
+
+  return 0;
+}
+

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