From 68200a8426b0b0cd56bae2f737c4ceea1e10d95a Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 7 Apr 2020 15:00:56 -0300 Subject: 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(-) create mode 100644 src/examples/websocket_threaded_example.c 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 +#include + +#define CHAT_PAGE \ + "\n" \ + "\n" \ + "WebSocket chat\n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + "

\n" \ + "\n" \ + "\n" \ + "" +#define BAD_REQUEST_PAGE \ + "\n" \ + "\n" \ + "WebSocket chat\n" \ + "\n" \ + "\n" \ + "Bad Request\n" \ + "\n" \ + "\n" +#define UPGRADE_REQUIRED_PAGE \ + "\n" \ + "\n" \ + "WebSocket chat\n" \ + "\n" \ + "\n" \ + "Upgrade required\n" \ + "\n" \ + "\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; +} -- cgit v1.2.3