gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated: Added example for how to provide


From: gnunet
Subject: [libmicrohttpd] branch master updated: Added example for how to provide a tiny threaded websocket server. (#5501)
Date: Tue, 07 Apr 2020 20:07:44 +0200

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

silvioprog pushed a commit to branch master
in repository libmicrohttpd.

The following commit(s) were added to refs/heads/master by this push:
     new 68200a84 Added example for how to provide a tiny threaded websocket 
server. (#5501)
68200a84 is described below

commit 68200a8426b0b0cd56bae2f737c4ceea1e10d95a
Author: silvioprog <address@hidden>
AuthorDate: Tue Apr 7 15:00:56 2020 -0300

    Added example for how to provide a tiny threaded websocket server. (#5501)
---
 ChangeLog                                 |   3 +
 src/examples/.gitignore                   |   1 +
 src/examples/Makefile.am                  |  11 +-
 src/examples/websocket_threaded_example.c | 878 ++++++++++++++++++++++++++++++
 4 files changed, 892 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 14ad04a6..36c5bdbf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+Tue 07 Apr 2020 02:58:39 PM BRT
+    Fixed #5501 (Added example for how to provide a tiny threaded websocket 
server). -SC
+
 Tue 31 Mar 2020 02:36:40 PM BRT
     Fixed #6142 (applied several spelling fixes). -DKG/-SC
 
diff --git a/src/examples/.gitignore b/src/examples/.gitignore
index 7fa3e9b8..7a64ff40 100644
--- a/src/examples/.gitignore
+++ b/src/examples/.gitignore
@@ -32,6 +32,7 @@
 /https_echo_client_example.c
 /authorization_example
 upgrade_example
+websocket_threaded_example
 /timeout
 http_chunked_compression
 http_compression
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am
index 5dcee4d2..1f0a65af 100644
--- a/src/examples/Makefile.am
+++ b/src/examples/Makefile.am
@@ -67,7 +67,8 @@ endif
 if HAVE_POSIX_THREADS
 if ENABLE_UPGRADE
 noinst_PROGRAMS += \
- upgrade_example
+ upgrade_example \
+ websocket_threaded_example
 endif
 endif
 
@@ -104,6 +105,14 @@ upgrade_example_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   $(PTHREAD_LIBS)
 
+websocket_threaded_example_SOURCES = \
+ websocket_threaded_example.c
+websocket_threaded_example_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+websocket_threaded_example_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS)
+
 timeout_SOURCES = \
  timeout.c
 timeout_LDADD = \
diff --git a/src/examples/websocket_threaded_example.c 
b/src/examples/websocket_threaded_example.c
new file mode 100644
index 00000000..21028901
--- /dev/null
+++ b/src/examples/websocket_threaded_example.c
@@ -0,0 +1,878 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2020 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+   USA
+*/
+
+/**
+ * @file websocket_threaded_example.c
+ * @brief example for how to provide a tiny threaded websocket server
+ * @author Silvio Clecio (silvioprog)
+ */
+
+#include "platform.h"
+#include <pthread.h>
+#include <microhttpd.h>
+
+#define CHAT_PAGE                                                             \
+  "<html>\n"                                                                  \
+  "<head>\n"                                                                  \
+  "<title>WebSocket chat</title>\n"                                           \
+  "<script>\n"                                                                \
+  "document.addEventListener('DOMContentLoaded', function() {\n"              \
+  "  const ws = new WebSocket('ws://localhost:%d');\n"                        \
+  "  const btn = document.getElementById('send');\n"                          \
+  "  const msg = document.getElementById('msg');\n"                           \
+  "  const log = document.getElementById('log');\n"                           \
+  "  ws.onopen = function() {\n"                                              \
+  "    log.value += 'Connected\\n';\n"                                        \
+  "  };\n"                                                                    \
+  "  ws.onclose = function() {\n"                                             \
+  "    log.value += 'Disconnected\\n';\n"                                     \
+  "  };\n"                                                                    \
+  "  ws.onmessage = function(ev) {\n"                                         \
+  "    log.value += ev.data + '\\n';\n"                                       \
+  "  };\n"                                                                    \
+  "  btn.onclick = function() {\n"                                            \
+  "    log.value += '<You>: ' + msg.value + '\\n';\n"                         \
+  "    ws.send(msg.value);\n"                                                 \
+  "  };\n"                                                                    \
+  "  msg.onkeyup = function(ev) {\n"                                          \
+  "    if (ev.keyCode === 13) {\n"                                            \
+  "      ev.preventDefault();\n"                                              \
+  "      btn.click();\n"                                                      \
+  "      msg.value = '';\n"                                                   \
+  "    }\n"                                                                   \
+  "  };\n"                                                                    \
+  "});\n"                                                                     \
+  "</script>\n"                                                               \
+  "</head>\n"                                                                 \
+  "<body>\n"                                                                  \
+  "<input type='text' id='msg' autofocus/>\n"                                 \
+  "<input type='button' id='send' value='Send' /><br /><br />\n"              \
+  "<textarea id='log' rows='20' cols='28'></textarea>\n"                      \
+  "</body>\n"                                                                 \
+  "</html>"
+#define BAD_REQUEST_PAGE                                                      \
+  "<html>\n"                                                                  \
+  "<head>\n"                                                                  \
+  "<title>WebSocket chat</title>\n"                                           \
+  "</head>\n"                                                                 \
+  "<body>\n"                                                                  \
+  "Bad Request\n"                                                             \
+  "</body>\n"                                                                 \
+  "</html>\n"
+#define UPGRADE_REQUIRED_PAGE                                                 \
+  "<html>\n"                                                                  \
+  "<head>\n"                                                                  \
+  "<title>WebSocket chat</title>\n"                                           \
+  "</head>\n"                                                                 \
+  "<body>\n"                                                                  \
+  "Upgrade required\n"                                                        \
+  "</body>\n"                                                                 \
+  "</html>\n"
+
+#define WS_SEC_WEBSOCKET_VERSION "13"
+#define WS_UPGRADE_VALUE "websocket"
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+#define WS_GUID_LEN 36
+#define WS_KEY_LEN 24
+#define WS_KEY_GUI_LEN ((WS_KEY_LEN) + (WS_GUID_LEN))
+#define WS_FIN 128
+#define WS_OPCODE_TEXT_FRAME 1
+#define WS_OPCODE_CON_CLOSE_FRAME 8
+
+#define MAX_CLIENTS 10
+
+static int CLIENT_SOCKS[MAX_CLIENTS];
+
+static pthread_mutex_t MUTEX = PTHREAD_MUTEX_INITIALIZER;
+
+struct WsData
+{
+  struct MHD_UpgradeResponseHandle *urh;
+  MHD_socket sock;
+};
+
+/********** begin SHA-1 **********/
+
+#define SHA1HashSize 20
+
+#define SHA1CircularShift(bits, word)                                         \
+  (((word) << (bits)) | ((word) >> (32 - (bits))))
+
+enum SHA1_RESULT
+{
+  SHA1_RESULT_SUCCESS = 0,
+  SHA1_RESULT_NULL = 1,
+  SHA1_RESULT_STATE_ERROR = 2
+};
+
+struct SHA1Context
+{
+  uint32_t intermediate_hash[SHA1HashSize / 4];
+  uint32_t length_low;
+  uint32_t length_high;
+  int_least16_t message_block_index;
+  uint8_t message_block[64];
+  int computed;
+  int corrupted;
+};
+
+static void
+SHA1ProcessMessageBlock (struct SHA1Context *context)
+{
+  const uint32_t K[] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
+  int i;
+  uint32_t temp;
+  uint32_t W[80];
+  uint32_t A, B, C, D, E;
+  for (i = 0; i < 16; i++)
+  {
+    W[i] = context->message_block[i * 4] << 24;
+    W[i] |= context->message_block[i * 4 + 1] << 16;
+    W[i] |= context->message_block[i * 4 + 2] << 8;
+    W[i] |= context->message_block[i * 4 + 3];
+  }
+  for (i = 16; i < 80; i++)
+  {
+    W[i]
+      = SHA1CircularShift (1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]);
+  }
+  A = context->intermediate_hash[0];
+  B = context->intermediate_hash[1];
+  C = context->intermediate_hash[2];
+  D = context->intermediate_hash[3];
+  E = context->intermediate_hash[4];
+  for (i = 0; i < 20; i++)
+  {
+    temp = SHA1CircularShift (5, A) + ((B & C) | ((~B) & D)) + E + W[i]
+           + K[0];
+    E = D;
+    D = C;
+    C = SHA1CircularShift (30, B);
+    B = A;
+    A = temp;
+  }
+  for (i = 20; i < 40; i++)
+  {
+    temp = SHA1CircularShift (5, A) + (B ^ C ^ D) + E + W[i] + K[1];
+    E = D;
+    D = C;
+    C = SHA1CircularShift (30, B);
+    B = A;
+    A = temp;
+  }
+  for (i = 40; i < 60; i++)
+  {
+    temp = SHA1CircularShift (5, A) + ((B & C) | (B & D) | (C & D)) + E
+           + W[i] + K[2];
+    E = D;
+    D = C;
+    C = SHA1CircularShift (30, B);
+    B = A;
+    A = temp;
+  }
+  for (i = 60; i < 80; i++)
+  {
+    temp = SHA1CircularShift (5, A) + (B ^ C ^ D) + E + W[i] + K[3];
+    E = D;
+    D = C;
+    C = SHA1CircularShift (30, B);
+    B = A;
+    A = temp;
+  }
+  context->intermediate_hash[0] += A;
+  context->intermediate_hash[1] += B;
+  context->intermediate_hash[2] += C;
+  context->intermediate_hash[3] += D;
+  context->intermediate_hash[4] += E;
+  context->message_block_index = 0;
+}
+
+
+static void
+SHA1PadMessage (struct SHA1Context *context)
+{
+  if (context->message_block_index > 55)
+  {
+    context->message_block[context->message_block_index++] = 0x80;
+    while (context->message_block_index < 64)
+    {
+      context->message_block[context->message_block_index++] = 0;
+    }
+    SHA1ProcessMessageBlock (context);
+    while (context->message_block_index < 56)
+    {
+      context->message_block[context->message_block_index++] = 0;
+    }
+  }
+  else
+  {
+    context->message_block[context->message_block_index++] = 0x80;
+    while (context->message_block_index < 56)
+    {
+      context->message_block[context->message_block_index++] = 0;
+    }
+  }
+  context->message_block[56] = context->length_high >> 24;
+  context->message_block[57] = context->length_high >> 16;
+  context->message_block[58] = context->length_high >> 8;
+  context->message_block[59] = context->length_high;
+  context->message_block[60] = context->length_low >> 24;
+  context->message_block[61] = context->length_low >> 16;
+  context->message_block[62] = context->length_low >> 8;
+  context->message_block[63] = context->length_low;
+  SHA1ProcessMessageBlock (context);
+}
+
+
+static enum SHA1_RESULT
+SHA1Reset (struct SHA1Context *context)
+{
+  if (! context)
+  {
+    return SHA1_RESULT_NULL;
+  }
+  context->length_low = 0;
+  context->length_high = 0;
+  context->message_block_index = 0;
+  context->intermediate_hash[0] = 0x67452301;
+  context->intermediate_hash[1] = 0xEFCDAB89;
+  context->intermediate_hash[2] = 0x98BADCFE;
+  context->intermediate_hash[3] = 0x10325476;
+  context->intermediate_hash[4] = 0xC3D2E1F0;
+  context->computed = 0;
+  context->corrupted = 0;
+  return SHA1_RESULT_SUCCESS;
+}
+
+
+static enum SHA1_RESULT
+SHA1Result (struct SHA1Context *context, uint8_t Message_Digest[SHA1HashSize])
+{
+  int i;
+  if (! context || ! Message_Digest)
+  {
+    return SHA1_RESULT_NULL;
+  }
+  if (context->corrupted)
+  {
+    return context->corrupted;
+  }
+  if (! context->computed)
+  {
+    SHA1PadMessage (context);
+    for (i = 0; i < 64; ++i)
+    {
+      context->message_block[i] = 0;
+    }
+    context->length_low = 0;
+    context->length_high = 0;
+    context->computed = 1;
+  }
+  for (i = 0; i < SHA1HashSize; ++i)
+  {
+    Message_Digest[i]
+      = context->intermediate_hash[i >> 2] >> 8 * (3 - (i & 0x03));
+  }
+  return SHA1_RESULT_SUCCESS;
+}
+
+
+static enum SHA1_RESULT
+SHA1Input (struct SHA1Context *context, const uint8_t *message_array,
+           unsigned length)
+{
+  if (! length)
+  {
+    return SHA1_RESULT_SUCCESS;
+  }
+  if (! context || ! message_array)
+  {
+    return SHA1_RESULT_NULL;
+  }
+  if (context->computed)
+  {
+    context->corrupted = SHA1_RESULT_STATE_ERROR;
+    return SHA1_RESULT_STATE_ERROR;
+  }
+  if (context->corrupted)
+  {
+    return context->corrupted;
+  }
+  while (length-- && ! context->corrupted)
+  {
+    context->message_block[context->message_block_index++]
+      = (*message_array & 0xFF);
+    context->length_low += 8;
+    if (context->length_low == 0)
+    {
+      context->length_high++;
+      if (context->length_high == 0)
+      {
+        context->corrupted = 1;
+      }
+    }
+    if (context->message_block_index == 64)
+    {
+      SHA1ProcessMessageBlock (context);
+    }
+    message_array++;
+  }
+  return SHA1_RESULT_SUCCESS;
+}
+
+
+/********** end SHA-1 **********/
+
+/********** begin Base64 **********/
+
+static const unsigned char BASE64_TABLE[65]
+  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static unsigned char *
+BASE64Encode (const unsigned char *src, size_t len, size_t *out_len)
+{
+  const unsigned char *end;
+  const unsigned char *in;
+  unsigned char *out;
+  unsigned char *pos;
+  size_t olen;
+  int line_len;
+  olen = len * 4 / 3 + 4;
+  olen += olen / 72;
+  olen++;
+  if (olen < len)
+  {
+    return NULL;
+  }
+  out = malloc (olen);
+  if (NULL == out)
+  {
+    return NULL;
+  }
+  end = src + len;
+  in = src;
+  pos = out;
+  line_len = 0;
+  while (end - in >= 3)
+  {
+    *pos++ = BASE64_TABLE[in[0] >> 2];
+    *pos++ = BASE64_TABLE[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+    *pos++ = BASE64_TABLE[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+    *pos++ = BASE64_TABLE[in[2] & 0x3f];
+    in += 3;
+    line_len += 4;
+    if (line_len >= 72)
+    {
+      *pos++ = '\n';
+      line_len = 0;
+    }
+  }
+  if (end - in)
+  {
+    *pos++ = BASE64_TABLE[in[0] >> 2];
+    if (end - in == 1)
+    {
+      *pos++ = BASE64_TABLE[(in[0] & 0x03) << 4];
+      *pos++ = '=';
+    }
+    else
+    {
+      *pos++ = BASE64_TABLE[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+      *pos++ = BASE64_TABLE[(in[1] & 0x0f) << 2];
+    }
+    *pos++ = '=';
+    line_len += 4;
+  }
+  if (line_len)
+  {
+    *pos++ = '\n';
+  }
+  *pos = '\0';
+  if (out_len)
+  {
+    *out_len = pos - out;
+  }
+  return out;
+}
+
+
+/********** end Base64 **********/
+
+static int
+is_websocket_request (struct MHD_Connection *con, const char *upg_header,
+                      const char *con_header)
+{
+  return (upg_header != NULL) && (con_header != NULL)
+         && (0 == strcmp (upg_header, WS_UPGRADE_VALUE))
+         && (NULL != strstr (con_header, "Upgrade"))
+         ? MHD_YES
+         : MHD_NO;
+}
+
+
+static int
+send_chat_page (struct MHD_Connection *con, uint16_t port)
+{
+  struct MHD_Response *res;
+  char page[1024];
+  size_t page_len;
+  int ret;
+  page_len = sprintf (page, CHAT_PAGE, port);
+  res = MHD_create_response_from_buffer (page_len, (void *) page,
+                                         MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (con, MHD_HTTP_OK, res);
+  MHD_destroy_response (res);
+  return ret;
+}
+
+
+static int
+send_bad_request (struct MHD_Connection *con)
+{
+  struct MHD_Response *res;
+  int ret;
+  res = MHD_create_response_from_buffer (strlen (BAD_REQUEST_PAGE),
+                                         (void *) BAD_REQUEST_PAGE,
+                                         MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (con, MHD_HTTP_BAD_REQUEST, res);
+  MHD_destroy_response (res);
+  return ret;
+}
+
+
+static int
+send_upgrade_required (struct MHD_Connection *con)
+{
+  struct MHD_Response *res;
+  int ret;
+  res = MHD_create_response_from_buffer (strlen (UPGRADE_REQUIRED_PAGE),
+                                         (void *) UPGRADE_REQUIRED_PAGE,
+                                         MHD_RESPMEM_PERSISTENT);
+  MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION,
+                           WS_SEC_WEBSOCKET_VERSION);
+  ret = MHD_queue_response (con, MHD_HTTP_UPGRADE_REQUIRED, res);
+  MHD_destroy_response (res);
+  return ret;
+}
+
+
+static int
+ws_get_accept_value (char *key, unsigned char **val)
+{
+  struct SHA1Context ctx;
+  unsigned char hash[SHA1HashSize];
+  char *str;
+  if (NULL == key)
+  {
+    return MHD_NO;
+  }
+  str = malloc (WS_KEY_LEN + WS_GUID_LEN + 1);
+  if (NULL == str)
+  {
+    return MHD_NO;
+  }
+  strcpy (str, key);
+  strcat (str, WS_GUID);
+  SHA1Reset (&ctx);
+  SHA1Input (&ctx, (const uint8_t *) str, WS_KEY_GUI_LEN);
+  SHA1Result (&ctx, hash);
+  free (str);
+  *val = BASE64Encode (hash, SHA1HashSize, NULL);
+  if (NULL == *val)
+  {
+    return MHD_NO;
+  }
+  *(*val + strlen ((const char *) *val) - 1) = '\0';
+  return MHD_YES;
+}
+
+
+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 = 1;
+  ioctlsocket (fd, FIONBIO, &flags);
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+static size_t
+send_all (MHD_socket sock, const char *buf, size_t len)
+{
+  ssize_t ret;
+  size_t off;
+  for (off = 0; off < len; off += ret)
+  {
+    ret = send (sock, &buf[off], len - off, 0);
+    if (0 > ret)
+    {
+      if (EAGAIN == errno)
+      {
+        ret = 0;
+        continue;
+      }
+      break;
+    }
+    if (0 == ret)
+    {
+      break;
+    }
+  }
+  return off;
+}
+
+
+static int
+ws_send_frame (int sock, const char *msg, size_t length)
+{
+  unsigned char *response;
+  unsigned char frame[10];
+  uint8_t idx_first_rdata;
+  int idx_response;
+  int output;
+  int isock;
+  int i;
+  frame[0] = (WS_FIN | WS_OPCODE_TEXT_FRAME);
+  if (length <= 125)
+  {
+    frame[1] = length & 0x7F;
+    idx_first_rdata = 2;
+  }
+  else if ((length >= 126) && (length <= 0xFFFF))
+  {
+    frame[1] = 126;
+    frame[2] = (length >> 8) & 0xFF;
+    frame[3] = length & 0xFF;
+    idx_first_rdata = 4;
+  }
+  else
+  {
+    frame[1] = 127;
+    frame[2] = (unsigned char) ((length >> 56) & 0xFF);
+    frame[3] = (unsigned char) ((length >> 48) & 0xFF);
+    frame[4] = (unsigned char) ((length >> 40) & 0xFF);
+    frame[5] = (unsigned char) ((length >> 32) & 0xFF);
+    frame[6] = (unsigned char) ((length >> 24) & 0xFF);
+    frame[7] = (unsigned char) ((length >> 16) & 0xFF);
+    frame[8] = (unsigned char) ((length >> 8) & 0xFF);
+    frame[9] = (unsigned char) (length & 0xFF);
+    idx_first_rdata = 10;
+  }
+  idx_response = 0;
+  response = malloc (idx_first_rdata + length + 1);
+  if (NULL == response)
+  {
+    return -1;
+  }
+  for (i = 0; i < idx_first_rdata; i++)
+  {
+    response[i] = frame[i];
+    idx_response++;
+  }
+  for (i = 0; i < length; i++)
+  {
+    response[idx_response] = msg[i];
+    idx_response++;
+  }
+  response[idx_response] = '\0';
+  output = 0;
+  pthread_mutex_lock (&MUTEX);
+  for (i = 0; i < MAX_CLIENTS; i++)
+  {
+    isock = CLIENT_SOCKS[i];
+    if ((isock > -1) && (isock != sock))
+    {
+      output += send_all (isock, response, idx_response);
+    }
+  }
+  pthread_mutex_unlock (&MUTEX);
+  free (response);
+  return output;
+}
+
+
+static unsigned char *
+ws_receive_frame (unsigned char *frame, ssize_t *length, int *type)
+{
+  unsigned char *msg;
+  uint8_t masks[4];
+  uint8_t mask;
+  uint8_t flength;
+  uint8_t idx_first_mask;
+  uint8_t idx_first_data;
+  ssize_t data_length;
+  int i;
+  int j;
+  msg = NULL;
+  if (frame[0] == (WS_FIN | WS_OPCODE_TEXT_FRAME))
+  {
+    *type = WS_OPCODE_TEXT_FRAME;
+    idx_first_mask = 2;
+    mask = frame[1];
+    flength = mask & 0x7F;
+    if (flength == 126)
+    {
+      idx_first_mask = 4;
+    }
+    else if (flength == 127)
+    {
+      idx_first_mask = 10;
+    }
+    idx_first_data = idx_first_mask + 4;
+    data_length = *length - idx_first_data;
+    masks[0] = frame[idx_first_mask + 0];
+    masks[1] = frame[idx_first_mask + 1];
+    masks[2] = frame[idx_first_mask + 2];
+    masks[3] = frame[idx_first_mask + 3];
+    msg = malloc (data_length + 1);
+    if (NULL != msg)
+    {
+      for (i = idx_first_data, j = 0; i < *length; i++, j++)
+      {
+        msg[j] = frame[i] ^ masks[j % 4];
+      }
+      *length = data_length;
+      msg[j] = '\0';
+    }
+  }
+  else if (frame[0] == (WS_FIN | WS_OPCODE_CON_CLOSE_FRAME))
+  {
+    *type = WS_OPCODE_CON_CLOSE_FRAME;
+  }
+  else
+  {
+    *type = frame[0] & 0x0F;
+  }
+  return msg;
+}
+
+
+static void *
+run_usock (void *cls)
+{
+  struct WsData *ws = cls;
+  struct MHD_UpgradeResponseHandle *urh = ws->urh;
+  unsigned char *msg;
+  unsigned char *text;
+  unsigned char client[20];
+  char buf[2048];
+  ssize_t got;
+  size_t size;
+  int type;
+  int sent;
+  int i;
+  make_blocking (ws->sock);
+  while (1)
+  {
+    got = recv (ws->sock, buf, sizeof (buf), 0);
+    if (0 >= got)
+    {
+      break;
+    }
+    msg = ws_receive_frame (buf, &got, &type);
+    if (NULL == msg)
+    {
+      break;
+    }
+    if (type == WS_OPCODE_TEXT_FRAME)
+    {
+      size = sprintf (client, "User#%d: ", ws->sock);
+      size += got;
+      text = malloc (size);
+      if (NULL != buf)
+      {
+        sprintf (text, "%s%s", client, msg);
+        sent = ws_send_frame (ws->sock, text, size);
+      }
+      else
+      {
+        sent = -1;
+      }
+      free (text);
+      free (msg);
+      if (-1 == sent)
+      {
+        break;
+      }
+    }
+    else
+    {
+      if (type == WS_OPCODE_CON_CLOSE_FRAME)
+      {
+        free (msg);
+        pthread_mutex_lock (&MUTEX);
+        for (i = 0; i < MAX_CLIENTS; i++)
+        {
+          if (CLIENT_SOCKS[i] == ws->sock)
+          {
+            CLIENT_SOCKS[i] = -1;
+            break;
+          }
+        }
+        pthread_mutex_unlock (&MUTEX);
+        break;
+      }
+    }
+  }
+  free (ws);
+  MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE);
+  return NULL;
+}
+
+
+static int
+uh_cb (void *cls, struct MHD_Connection *con, void *con_cls,
+       const char *extra_in, size_t extra_in_size, MHD_socket sock,
+       struct MHD_UpgradeResponseHandle *urh)
+{
+  struct WsData *ws;
+  pthread_t pt;
+  int sock_overflow;
+  int i;
+  (void) cls;
+  (void) con;
+  (void) con_cls;
+  sock_overflow = MHD_YES;
+  ws = malloc (sizeof (struct WsData));
+  if (NULL == ws)
+    abort ();
+  memset (ws, 0, sizeof (struct WsData));
+  ws->sock = sock;
+  ws->urh = urh;
+  pthread_mutex_lock (&MUTEX);
+  for (i = 0; i < MAX_CLIENTS; i++)
+  {
+    if (-1 == CLIENT_SOCKS[i])
+    {
+      CLIENT_SOCKS[i] = ws->sock;
+      sock_overflow = MHD_NO;
+      break;
+    }
+  }
+  if (sock_overflow)
+  {
+    free (ws);
+    MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE);
+    return;
+  }
+  pthread_mutex_unlock (&MUTEX);
+  if (0 != pthread_create (&pt, NULL, &run_usock, ws))
+    abort ();
+  /* Note that by detaching like this we make it impossible to ensure
+     a clean shutdown, as the we stop the daemon even if a worker thread
+     is still running. Alas, this is a simple example... */
+  pthread_detach (pt);
+}
+
+
+static int
+ahc_cb (void *cls, struct MHD_Connection *con, const char *url,
+        const char *method, const char *version, const char *upload_data,
+        size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *res;
+  const char *upg_header;
+  const char *con_header;
+  const char *ws_version_header;
+  const char *ws_key_header;
+  char ws_ac_header[60];
+  char *ws_ac_value;
+  int ret;
+  (void) url;
+  (void) upload_data;
+  (void) upload_data_size;
+  if (NULL == *ptr)
+  {
+    *ptr = (void *) 1;
+    return MHD_YES;
+  }
+  *ptr = NULL;
+  upg_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND,
+                                            MHD_HTTP_HEADER_UPGRADE);
+  con_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND,
+                                            MHD_HTTP_HEADER_CONNECTION);
+  if (MHD_NO == is_websocket_request (con, upg_header, con_header))
+  {
+    return send_chat_page (con, *(uint16_t *) cls);
+  }
+  if ((0 != strcmp (method, MHD_HTTP_METHOD_GET))
+      || (0 != strcmp (version, MHD_HTTP_VERSION_1_1)))
+  {
+    return send_bad_request (con);
+  }
+  ws_version_header = MHD_lookup_connection_value (
+    con, MHD_HEADER_KIND, MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+  if ((NULL == ws_version_header)
+      || (0 != strcmp (ws_version_header, WS_SEC_WEBSOCKET_VERSION)))
+  {
+    return send_upgrade_required (con);
+  }
+  ws_key_header = MHD_lookup_connection_value (
+    con, MHD_HEADER_KIND, MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+  if ((NULL == ws_key_header) || (strlen (ws_key_header) != 24))
+  {
+    return send_bad_request (con);
+  }
+  ret = ws_get_accept_value (ws_key_header, &ws_ac_value);
+  if (MHD_NO == ret)
+  {
+    return ret;
+  }
+  res = MHD_create_response_for_upgrade (&uh_cb, NULL);
+  MHD_add_response_header (res, MHD_HTTP_HEADER_UPGRADE, WS_UPGRADE_VALUE);
+  MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+                           ws_ac_value);
+  free (ws_ac_value);
+  ret = MHD_queue_response (con, MHD_HTTP_SWITCHING_PROTOCOLS, res);
+  MHD_destroy_response (res);
+  return ret;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  uint16_t port;
+  if (argc != 2)
+  {
+    printf ("%s PORT\n", argv[0]);
+    return 1;
+  }
+  port = atoi (argv[1]);
+  d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO_INTERNAL_THREAD
+                        | MHD_USE_ERROR_LOG,
+                        port, NULL, NULL, &ahc_cb, &port, MHD_OPTION_END);
+  if (NULL == d)
+    return 1;
+  memset (CLIENT_SOCKS, -1, sizeof (CLIENT_SOCKS));
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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