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