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