aboutsummaryrefslogtreecommitdiff
path: root/src/examples/websocket_threaded_example.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/examples/websocket_threaded_example.c')
-rw-r--r--src/examples/websocket_threaded_example.c878
1 files changed, 878 insertions, 0 deletions
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 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2020 Christian Grothoff (and other contributing authors)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18 USA
19*/
20
21/**
22 * @file websocket_threaded_example.c
23 * @brief example for how to provide a tiny threaded websocket server
24 * @author Silvio Clecio (silvioprog)
25 */
26
27#include "platform.h"
28#include <pthread.h>
29#include <microhttpd.h>
30
31#define CHAT_PAGE \
32 "<html>\n" \
33 "<head>\n" \
34 "<title>WebSocket chat</title>\n" \
35 "<script>\n" \
36 "document.addEventListener('DOMContentLoaded', function() {\n" \
37 " const ws = new WebSocket('ws://localhost:%d');\n" \
38 " const btn = document.getElementById('send');\n" \
39 " const msg = document.getElementById('msg');\n" \
40 " const log = document.getElementById('log');\n" \
41 " ws.onopen = function() {\n" \
42 " log.value += 'Connected\\n';\n" \
43 " };\n" \
44 " ws.onclose = function() {\n" \
45 " log.value += 'Disconnected\\n';\n" \
46 " };\n" \
47 " ws.onmessage = function(ev) {\n" \
48 " log.value += ev.data + '\\n';\n" \
49 " };\n" \
50 " btn.onclick = function() {\n" \
51 " log.value += '<You>: ' + msg.value + '\\n';\n" \
52 " ws.send(msg.value);\n" \
53 " };\n" \
54 " msg.onkeyup = function(ev) {\n" \
55 " if (ev.keyCode === 13) {\n" \
56 " ev.preventDefault();\n" \
57 " btn.click();\n" \
58 " msg.value = '';\n" \
59 " }\n" \
60 " };\n" \
61 "});\n" \
62 "</script>\n" \
63 "</head>\n" \
64 "<body>\n" \
65 "<input type='text' id='msg' autofocus/>\n" \
66 "<input type='button' id='send' value='Send' /><br /><br />\n" \
67 "<textarea id='log' rows='20' cols='28'></textarea>\n" \
68 "</body>\n" \
69 "</html>"
70#define BAD_REQUEST_PAGE \
71 "<html>\n" \
72 "<head>\n" \
73 "<title>WebSocket chat</title>\n" \
74 "</head>\n" \
75 "<body>\n" \
76 "Bad Request\n" \
77 "</body>\n" \
78 "</html>\n"
79#define UPGRADE_REQUIRED_PAGE \
80 "<html>\n" \
81 "<head>\n" \
82 "<title>WebSocket chat</title>\n" \
83 "</head>\n" \
84 "<body>\n" \
85 "Upgrade required\n" \
86 "</body>\n" \
87 "</html>\n"
88
89#define WS_SEC_WEBSOCKET_VERSION "13"
90#define WS_UPGRADE_VALUE "websocket"
91#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
92#define WS_GUID_LEN 36
93#define WS_KEY_LEN 24
94#define WS_KEY_GUI_LEN ((WS_KEY_LEN) + (WS_GUID_LEN))
95#define WS_FIN 128
96#define WS_OPCODE_TEXT_FRAME 1
97#define WS_OPCODE_CON_CLOSE_FRAME 8
98
99#define MAX_CLIENTS 10
100
101static int CLIENT_SOCKS[MAX_CLIENTS];
102
103static pthread_mutex_t MUTEX = PTHREAD_MUTEX_INITIALIZER;
104
105struct WsData
106{
107 struct MHD_UpgradeResponseHandle *urh;
108 MHD_socket sock;
109};
110
111/********** begin SHA-1 **********/
112
113#define SHA1HashSize 20
114
115#define SHA1CircularShift(bits, word) \
116 (((word) << (bits)) | ((word) >> (32 - (bits))))
117
118enum SHA1_RESULT
119{
120 SHA1_RESULT_SUCCESS = 0,
121 SHA1_RESULT_NULL = 1,
122 SHA1_RESULT_STATE_ERROR = 2
123};
124
125struct SHA1Context
126{
127 uint32_t intermediate_hash[SHA1HashSize / 4];
128 uint32_t length_low;
129 uint32_t length_high;
130 int_least16_t message_block_index;
131 uint8_t message_block[64];
132 int computed;
133 int corrupted;
134};
135
136static void
137SHA1ProcessMessageBlock (struct SHA1Context *context)
138{
139 const uint32_t K[] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
140 int i;
141 uint32_t temp;
142 uint32_t W[80];
143 uint32_t A, B, C, D, E;
144 for (i = 0; i < 16; i++)
145 {
146 W[i] = context->message_block[i * 4] << 24;
147 W[i] |= context->message_block[i * 4 + 1] << 16;
148 W[i] |= context->message_block[i * 4 + 2] << 8;
149 W[i] |= context->message_block[i * 4 + 3];
150 }
151 for (i = 16; i < 80; i++)
152 {
153 W[i]
154 = SHA1CircularShift (1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]);
155 }
156 A = context->intermediate_hash[0];
157 B = context->intermediate_hash[1];
158 C = context->intermediate_hash[2];
159 D = context->intermediate_hash[3];
160 E = context->intermediate_hash[4];
161 for (i = 0; i < 20; i++)
162 {
163 temp = SHA1CircularShift (5, A) + ((B & C) | ((~B) & D)) + E + W[i]
164 + K[0];
165 E = D;
166 D = C;
167 C = SHA1CircularShift (30, B);
168 B = A;
169 A = temp;
170 }
171 for (i = 20; i < 40; i++)
172 {
173 temp = SHA1CircularShift (5, A) + (B ^ C ^ D) + E + W[i] + K[1];
174 E = D;
175 D = C;
176 C = SHA1CircularShift (30, B);
177 B = A;
178 A = temp;
179 }
180 for (i = 40; i < 60; i++)
181 {
182 temp = SHA1CircularShift (5, A) + ((B & C) | (B & D) | (C & D)) + E
183 + W[i] + K[2];
184 E = D;
185 D = C;
186 C = SHA1CircularShift (30, B);
187 B = A;
188 A = temp;
189 }
190 for (i = 60; i < 80; i++)
191 {
192 temp = SHA1CircularShift (5, A) + (B ^ C ^ D) + E + W[i] + K[3];
193 E = D;
194 D = C;
195 C = SHA1CircularShift (30, B);
196 B = A;
197 A = temp;
198 }
199 context->intermediate_hash[0] += A;
200 context->intermediate_hash[1] += B;
201 context->intermediate_hash[2] += C;
202 context->intermediate_hash[3] += D;
203 context->intermediate_hash[4] += E;
204 context->message_block_index = 0;
205}
206
207
208static void
209SHA1PadMessage (struct SHA1Context *context)
210{
211 if (context->message_block_index > 55)
212 {
213 context->message_block[context->message_block_index++] = 0x80;
214 while (context->message_block_index < 64)
215 {
216 context->message_block[context->message_block_index++] = 0;
217 }
218 SHA1ProcessMessageBlock (context);
219 while (context->message_block_index < 56)
220 {
221 context->message_block[context->message_block_index++] = 0;
222 }
223 }
224 else
225 {
226 context->message_block[context->message_block_index++] = 0x80;
227 while (context->message_block_index < 56)
228 {
229 context->message_block[context->message_block_index++] = 0;
230 }
231 }
232 context->message_block[56] = context->length_high >> 24;
233 context->message_block[57] = context->length_high >> 16;
234 context->message_block[58] = context->length_high >> 8;
235 context->message_block[59] = context->length_high;
236 context->message_block[60] = context->length_low >> 24;
237 context->message_block[61] = context->length_low >> 16;
238 context->message_block[62] = context->length_low >> 8;
239 context->message_block[63] = context->length_low;
240 SHA1ProcessMessageBlock (context);
241}
242
243
244static enum SHA1_RESULT
245SHA1Reset (struct SHA1Context *context)
246{
247 if (! context)
248 {
249 return SHA1_RESULT_NULL;
250 }
251 context->length_low = 0;
252 context->length_high = 0;
253 context->message_block_index = 0;
254 context->intermediate_hash[0] = 0x67452301;
255 context->intermediate_hash[1] = 0xEFCDAB89;
256 context->intermediate_hash[2] = 0x98BADCFE;
257 context->intermediate_hash[3] = 0x10325476;
258 context->intermediate_hash[4] = 0xC3D2E1F0;
259 context->computed = 0;
260 context->corrupted = 0;
261 return SHA1_RESULT_SUCCESS;
262}
263
264
265static enum SHA1_RESULT
266SHA1Result (struct SHA1Context *context, uint8_t Message_Digest[SHA1HashSize])
267{
268 int i;
269 if (! context || ! Message_Digest)
270 {
271 return SHA1_RESULT_NULL;
272 }
273 if (context->corrupted)
274 {
275 return context->corrupted;
276 }
277 if (! context->computed)
278 {
279 SHA1PadMessage (context);
280 for (i = 0; i < 64; ++i)
281 {
282 context->message_block[i] = 0;
283 }
284 context->length_low = 0;
285 context->length_high = 0;
286 context->computed = 1;
287 }
288 for (i = 0; i < SHA1HashSize; ++i)
289 {
290 Message_Digest[i]
291 = context->intermediate_hash[i >> 2] >> 8 * (3 - (i & 0x03));
292 }
293 return SHA1_RESULT_SUCCESS;
294}
295
296
297static enum SHA1_RESULT
298SHA1Input (struct SHA1Context *context, const uint8_t *message_array,
299 unsigned length)
300{
301 if (! length)
302 {
303 return SHA1_RESULT_SUCCESS;
304 }
305 if (! context || ! message_array)
306 {
307 return SHA1_RESULT_NULL;
308 }
309 if (context->computed)
310 {
311 context->corrupted = SHA1_RESULT_STATE_ERROR;
312 return SHA1_RESULT_STATE_ERROR;
313 }
314 if (context->corrupted)
315 {
316 return context->corrupted;
317 }
318 while (length-- && ! context->corrupted)
319 {
320 context->message_block[context->message_block_index++]
321 = (*message_array & 0xFF);
322 context->length_low += 8;
323 if (context->length_low == 0)
324 {
325 context->length_high++;
326 if (context->length_high == 0)
327 {
328 context->corrupted = 1;
329 }
330 }
331 if (context->message_block_index == 64)
332 {
333 SHA1ProcessMessageBlock (context);
334 }
335 message_array++;
336 }
337 return SHA1_RESULT_SUCCESS;
338}
339
340
341/********** end SHA-1 **********/
342
343/********** begin Base64 **********/
344
345static const unsigned char BASE64_TABLE[65]
346 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
347
348static unsigned char *
349BASE64Encode (const unsigned char *src, size_t len, size_t *out_len)
350{
351 const unsigned char *end;
352 const unsigned char *in;
353 unsigned char *out;
354 unsigned char *pos;
355 size_t olen;
356 int line_len;
357 olen = len * 4 / 3 + 4;
358 olen += olen / 72;
359 olen++;
360 if (olen < len)
361 {
362 return NULL;
363 }
364 out = malloc (olen);
365 if (NULL == out)
366 {
367 return NULL;
368 }
369 end = src + len;
370 in = src;
371 pos = out;
372 line_len = 0;
373 while (end - in >= 3)
374 {
375 *pos++ = BASE64_TABLE[in[0] >> 2];
376 *pos++ = BASE64_TABLE[((in[0] & 0x03) << 4) | (in[1] >> 4)];
377 *pos++ = BASE64_TABLE[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
378 *pos++ = BASE64_TABLE[in[2] & 0x3f];
379 in += 3;
380 line_len += 4;
381 if (line_len >= 72)
382 {
383 *pos++ = '\n';
384 line_len = 0;
385 }
386 }
387 if (end - in)
388 {
389 *pos++ = BASE64_TABLE[in[0] >> 2];
390 if (end - in == 1)
391 {
392 *pos++ = BASE64_TABLE[(in[0] & 0x03) << 4];
393 *pos++ = '=';
394 }
395 else
396 {
397 *pos++ = BASE64_TABLE[((in[0] & 0x03) << 4) | (in[1] >> 4)];
398 *pos++ = BASE64_TABLE[(in[1] & 0x0f) << 2];
399 }
400 *pos++ = '=';
401 line_len += 4;
402 }
403 if (line_len)
404 {
405 *pos++ = '\n';
406 }
407 *pos = '\0';
408 if (out_len)
409 {
410 *out_len = pos - out;
411 }
412 return out;
413}
414
415
416/********** end Base64 **********/
417
418static int
419is_websocket_request (struct MHD_Connection *con, const char *upg_header,
420 const char *con_header)
421{
422 return (upg_header != NULL) && (con_header != NULL)
423 && (0 == strcmp (upg_header, WS_UPGRADE_VALUE))
424 && (NULL != strstr (con_header, "Upgrade"))
425 ? MHD_YES
426 : MHD_NO;
427}
428
429
430static int
431send_chat_page (struct MHD_Connection *con, uint16_t port)
432{
433 struct MHD_Response *res;
434 char page[1024];
435 size_t page_len;
436 int ret;
437 page_len = sprintf (page, CHAT_PAGE, port);
438 res = MHD_create_response_from_buffer (page_len, (void *) page,
439 MHD_RESPMEM_MUST_COPY);
440 ret = MHD_queue_response (con, MHD_HTTP_OK, res);
441 MHD_destroy_response (res);
442 return ret;
443}
444
445
446static int
447send_bad_request (struct MHD_Connection *con)
448{
449 struct MHD_Response *res;
450 int ret;
451 res = MHD_create_response_from_buffer (strlen (BAD_REQUEST_PAGE),
452 (void *) BAD_REQUEST_PAGE,
453 MHD_RESPMEM_PERSISTENT);
454 ret = MHD_queue_response (con, MHD_HTTP_BAD_REQUEST, res);
455 MHD_destroy_response (res);
456 return ret;
457}
458
459
460static int
461send_upgrade_required (struct MHD_Connection *con)
462{
463 struct MHD_Response *res;
464 int ret;
465 res = MHD_create_response_from_buffer (strlen (UPGRADE_REQUIRED_PAGE),
466 (void *) UPGRADE_REQUIRED_PAGE,
467 MHD_RESPMEM_PERSISTENT);
468 MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION,
469 WS_SEC_WEBSOCKET_VERSION);
470 ret = MHD_queue_response (con, MHD_HTTP_UPGRADE_REQUIRED, res);
471 MHD_destroy_response (res);
472 return ret;
473}
474
475
476static int
477ws_get_accept_value (char *key, unsigned char **val)
478{
479 struct SHA1Context ctx;
480 unsigned char hash[SHA1HashSize];
481 char *str;
482 if (NULL == key)
483 {
484 return MHD_NO;
485 }
486 str = malloc (WS_KEY_LEN + WS_GUID_LEN + 1);
487 if (NULL == str)
488 {
489 return MHD_NO;
490 }
491 strcpy (str, key);
492 strcat (str, WS_GUID);
493 SHA1Reset (&ctx);
494 SHA1Input (&ctx, (const uint8_t *) str, WS_KEY_GUI_LEN);
495 SHA1Result (&ctx, hash);
496 free (str);
497 *val = BASE64Encode (hash, SHA1HashSize, NULL);
498 if (NULL == *val)
499 {
500 return MHD_NO;
501 }
502 *(*val + strlen ((const char *) *val) - 1) = '\0';
503 return MHD_YES;
504}
505
506
507static void
508make_blocking (MHD_socket fd)
509{
510#if defined(MHD_POSIX_SOCKETS)
511 int flags;
512 flags = fcntl (fd, F_GETFL);
513 if (-1 == flags)
514 return;
515 if ((flags & ~O_NONBLOCK) != flags)
516 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
517 abort ();
518#elif defined(MHD_WINSOCK_SOCKETS)
519 unsigned long flags = 1;
520 ioctlsocket (fd, FIONBIO, &flags);
521#endif /* MHD_WINSOCK_SOCKETS */
522}
523
524
525static size_t
526send_all (MHD_socket sock, const char *buf, size_t len)
527{
528 ssize_t ret;
529 size_t off;
530 for (off = 0; off < len; off += ret)
531 {
532 ret = send (sock, &buf[off], len - off, 0);
533 if (0 > ret)
534 {
535 if (EAGAIN == errno)
536 {
537 ret = 0;
538 continue;
539 }
540 break;
541 }
542 if (0 == ret)
543 {
544 break;
545 }
546 }
547 return off;
548}
549
550
551static int
552ws_send_frame (int sock, const char *msg, size_t length)
553{
554 unsigned char *response;
555 unsigned char frame[10];
556 uint8_t idx_first_rdata;
557 int idx_response;
558 int output;
559 int isock;
560 int i;
561 frame[0] = (WS_FIN | WS_OPCODE_TEXT_FRAME);
562 if (length <= 125)
563 {
564 frame[1] = length & 0x7F;
565 idx_first_rdata = 2;
566 }
567 else if ((length >= 126) && (length <= 0xFFFF))
568 {
569 frame[1] = 126;
570 frame[2] = (length >> 8) & 0xFF;
571 frame[3] = length & 0xFF;
572 idx_first_rdata = 4;
573 }
574 else
575 {
576 frame[1] = 127;
577 frame[2] = (unsigned char) ((length >> 56) & 0xFF);
578 frame[3] = (unsigned char) ((length >> 48) & 0xFF);
579 frame[4] = (unsigned char) ((length >> 40) & 0xFF);
580 frame[5] = (unsigned char) ((length >> 32) & 0xFF);
581 frame[6] = (unsigned char) ((length >> 24) & 0xFF);
582 frame[7] = (unsigned char) ((length >> 16) & 0xFF);
583 frame[8] = (unsigned char) ((length >> 8) & 0xFF);
584 frame[9] = (unsigned char) (length & 0xFF);
585 idx_first_rdata = 10;
586 }
587 idx_response = 0;
588 response = malloc (idx_first_rdata + length + 1);
589 if (NULL == response)
590 {
591 return -1;
592 }
593 for (i = 0; i < idx_first_rdata; i++)
594 {
595 response[i] = frame[i];
596 idx_response++;
597 }
598 for (i = 0; i < length; i++)
599 {
600 response[idx_response] = msg[i];
601 idx_response++;
602 }
603 response[idx_response] = '\0';
604 output = 0;
605 pthread_mutex_lock (&MUTEX);
606 for (i = 0; i < MAX_CLIENTS; i++)
607 {
608 isock = CLIENT_SOCKS[i];
609 if ((isock > -1) && (isock != sock))
610 {
611 output += send_all (isock, response, idx_response);
612 }
613 }
614 pthread_mutex_unlock (&MUTEX);
615 free (response);
616 return output;
617}
618
619
620static unsigned char *
621ws_receive_frame (unsigned char *frame, ssize_t *length, int *type)
622{
623 unsigned char *msg;
624 uint8_t masks[4];
625 uint8_t mask;
626 uint8_t flength;
627 uint8_t idx_first_mask;
628 uint8_t idx_first_data;
629 ssize_t data_length;
630 int i;
631 int j;
632 msg = NULL;
633 if (frame[0] == (WS_FIN | WS_OPCODE_TEXT_FRAME))
634 {
635 *type = WS_OPCODE_TEXT_FRAME;
636 idx_first_mask = 2;
637 mask = frame[1];
638 flength = mask & 0x7F;
639 if (flength == 126)
640 {
641 idx_first_mask = 4;
642 }
643 else if (flength == 127)
644 {
645 idx_first_mask = 10;
646 }
647 idx_first_data = idx_first_mask + 4;
648 data_length = *length - idx_first_data;
649 masks[0] = frame[idx_first_mask + 0];
650 masks[1] = frame[idx_first_mask + 1];
651 masks[2] = frame[idx_first_mask + 2];
652 masks[3] = frame[idx_first_mask + 3];
653 msg = malloc (data_length + 1);
654 if (NULL != msg)
655 {
656 for (i = idx_first_data, j = 0; i < *length; i++, j++)
657 {
658 msg[j] = frame[i] ^ masks[j % 4];
659 }
660 *length = data_length;
661 msg[j] = '\0';
662 }
663 }
664 else if (frame[0] == (WS_FIN | WS_OPCODE_CON_CLOSE_FRAME))
665 {
666 *type = WS_OPCODE_CON_CLOSE_FRAME;
667 }
668 else
669 {
670 *type = frame[0] & 0x0F;
671 }
672 return msg;
673}
674
675
676static void *
677run_usock (void *cls)
678{
679 struct WsData *ws = cls;
680 struct MHD_UpgradeResponseHandle *urh = ws->urh;
681 unsigned char *msg;
682 unsigned char *text;
683 unsigned char client[20];
684 char buf[2048];
685 ssize_t got;
686 size_t size;
687 int type;
688 int sent;
689 int i;
690 make_blocking (ws->sock);
691 while (1)
692 {
693 got = recv (ws->sock, buf, sizeof (buf), 0);
694 if (0 >= got)
695 {
696 break;
697 }
698 msg = ws_receive_frame (buf, &got, &type);
699 if (NULL == msg)
700 {
701 break;
702 }
703 if (type == WS_OPCODE_TEXT_FRAME)
704 {
705 size = sprintf (client, "User#%d: ", ws->sock);
706 size += got;
707 text = malloc (size);
708 if (NULL != buf)
709 {
710 sprintf (text, "%s%s", client, msg);
711 sent = ws_send_frame (ws->sock, text, size);
712 }
713 else
714 {
715 sent = -1;
716 }
717 free (text);
718 free (msg);
719 if (-1 == sent)
720 {
721 break;
722 }
723 }
724 else
725 {
726 if (type == WS_OPCODE_CON_CLOSE_FRAME)
727 {
728 free (msg);
729 pthread_mutex_lock (&MUTEX);
730 for (i = 0; i < MAX_CLIENTS; i++)
731 {
732 if (CLIENT_SOCKS[i] == ws->sock)
733 {
734 CLIENT_SOCKS[i] = -1;
735 break;
736 }
737 }
738 pthread_mutex_unlock (&MUTEX);
739 break;
740 }
741 }
742 }
743 free (ws);
744 MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE);
745 return NULL;
746}
747
748
749static int
750uh_cb (void *cls, struct MHD_Connection *con, void *con_cls,
751 const char *extra_in, size_t extra_in_size, MHD_socket sock,
752 struct MHD_UpgradeResponseHandle *urh)
753{
754 struct WsData *ws;
755 pthread_t pt;
756 int sock_overflow;
757 int i;
758 (void) cls;
759 (void) con;
760 (void) con_cls;
761 sock_overflow = MHD_YES;
762 ws = malloc (sizeof (struct WsData));
763 if (NULL == ws)
764 abort ();
765 memset (ws, 0, sizeof (struct WsData));
766 ws->sock = sock;
767 ws->urh = urh;
768 pthread_mutex_lock (&MUTEX);
769 for (i = 0; i < MAX_CLIENTS; i++)
770 {
771 if (-1 == CLIENT_SOCKS[i])
772 {
773 CLIENT_SOCKS[i] = ws->sock;
774 sock_overflow = MHD_NO;
775 break;
776 }
777 }
778 if (sock_overflow)
779 {
780 free (ws);
781 MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE);
782 return;
783 }
784 pthread_mutex_unlock (&MUTEX);
785 if (0 != pthread_create (&pt, NULL, &run_usock, ws))
786 abort ();
787 /* Note that by detaching like this we make it impossible to ensure
788 a clean shutdown, as the we stop the daemon even if a worker thread
789 is still running. Alas, this is a simple example... */
790 pthread_detach (pt);
791}
792
793
794static int
795ahc_cb (void *cls, struct MHD_Connection *con, const char *url,
796 const char *method, const char *version, const char *upload_data,
797 size_t *upload_data_size, void **ptr)
798{
799 struct MHD_Response *res;
800 const char *upg_header;
801 const char *con_header;
802 const char *ws_version_header;
803 const char *ws_key_header;
804 char ws_ac_header[60];
805 char *ws_ac_value;
806 int ret;
807 (void) url;
808 (void) upload_data;
809 (void) upload_data_size;
810 if (NULL == *ptr)
811 {
812 *ptr = (void *) 1;
813 return MHD_YES;
814 }
815 *ptr = NULL;
816 upg_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND,
817 MHD_HTTP_HEADER_UPGRADE);
818 con_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND,
819 MHD_HTTP_HEADER_CONNECTION);
820 if (MHD_NO == is_websocket_request (con, upg_header, con_header))
821 {
822 return send_chat_page (con, *(uint16_t *) cls);
823 }
824 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET))
825 || (0 != strcmp (version, MHD_HTTP_VERSION_1_1)))
826 {
827 return send_bad_request (con);
828 }
829 ws_version_header = MHD_lookup_connection_value (
830 con, MHD_HEADER_KIND, MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
831 if ((NULL == ws_version_header)
832 || (0 != strcmp (ws_version_header, WS_SEC_WEBSOCKET_VERSION)))
833 {
834 return send_upgrade_required (con);
835 }
836 ws_key_header = MHD_lookup_connection_value (
837 con, MHD_HEADER_KIND, MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
838 if ((NULL == ws_key_header) || (strlen (ws_key_header) != 24))
839 {
840 return send_bad_request (con);
841 }
842 ret = ws_get_accept_value (ws_key_header, &ws_ac_value);
843 if (MHD_NO == ret)
844 {
845 return ret;
846 }
847 res = MHD_create_response_for_upgrade (&uh_cb, NULL);
848 MHD_add_response_header (res, MHD_HTTP_HEADER_UPGRADE, WS_UPGRADE_VALUE);
849 MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
850 ws_ac_value);
851 free (ws_ac_value);
852 ret = MHD_queue_response (con, MHD_HTTP_SWITCHING_PROTOCOLS, res);
853 MHD_destroy_response (res);
854 return ret;
855}
856
857
858int
859main (int argc, char *const *argv)
860{
861 struct MHD_Daemon *d;
862 uint16_t port;
863 if (argc != 2)
864 {
865 printf ("%s PORT\n", argv[0]);
866 return 1;
867 }
868 port = atoi (argv[1]);
869 d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO_INTERNAL_THREAD
870 | MHD_USE_ERROR_LOG,
871 port, NULL, NULL, &ahc_cb, &port, MHD_OPTION_END);
872 if (NULL == d)
873 return 1;
874 memset (CLIENT_SOCKS, -1, sizeof (CLIENT_SOCKS));
875 (void) getc (stdin);
876 MHD_stop_daemon (d);
877 return 0;
878}