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