1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2021 David Gausmann (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  USA
18 */
19 /**
20  * @file websocket_chatserver_example.c
21  * @brief example for how to use websockets
22  * @author David Gausmann
23  *
24  * Access the HTTP server with your webbrowser.
25  * The webbrowser must support JavaScript and WebSockets.
26  * The websocket access will be initiated via the JavaScript on the website.
27  * You will get an example chat room, which uses websockets.
28  * For testing with multiple users, just start several instances of your webbrowser.
29  *
30  */
31 
32 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
33 #define _CRT_SECURE_NO_WARNINGS
34 #endif
35 #include "platform.h"
36 #include <microhttpd.h>
37 #include <microhttpd_ws.h>
38 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
39 /*
40   Workaround for Windows systems, because the NuGet version of pthreads is buggy.
41   This is a simple replacement. It doesn't offer all functions of pthread, but
42   has everything, what is required for this example.
43   See: https://github.com/coapp-packages/pthreads/issues/2
44 */
45 #include "pthread_windows.h"
46 
47 /*
48   On Windows we will use stricmp instead of strcasecmp (strcasecmp is undefined there).
49 */
50 #define strcasecmp stricmp
51 
52 #else
53 /*
54   On Unix systems we can use pthread.
55 */
56 #include <pthread.h>
57 #endif
58 
59 
60 /*
61  * Specify with this constant whether or not to use HTTPS.
62  * 0 means HTTP, 1 means HTTPS.
63  * Please note that you must enter a valid private key/certificate pair
64  * in the main procedure to running this example with HTTPS.
65  */
66 #define USE_HTTPS 0
67 
68 /**
69  * This is the main website.
70  * The HTML, CSS and JavaScript code is all in there.
71  */
72 #define PAGE \
73   "<!DOCTYPE html>" \
74   "<html>" \
75   "<head>" \
76   "<meta charset='UTF-8'>" \
77   "<title>libmicrohttpd websocket chatserver demo</title>" \
78   "<style>" \
79   "  html" \
80   "  {\n" \
81   "    font: 11pt sans-serif;\n" \
82   "  }\n" \
83   "  html, body" \
84   "  {\n" \
85   "    margin: 0;\n" \
86   "    width:  100vw;\n" \
87   "    height: 100vh;\n" \
88   "  }\n" \
89   "  div#Chat\n" \
90   "  {\n" \
91   "    display:        flex;\n" \
92   "    flex-direction: row;\n" \
93   "  }\n" \
94   "  div#Chat > div.MessagesAndInput\n" \
95   "  {\n" \
96   "    flex:             1 1 auto;" \
97   "    display:          flex;\n" \
98   "    flex-direction:   column;\n" \
99   "    width:            calc(100vw - 20em);\n" \
100   "  }\n" \
101   "  div#Chat > div.MessagesAndInput > div#Messages\n" \
102   "  {\n" \
103   "    flex:             1 1 auto;" \
104   "    display:          flex;\n" \
105   "    flex-direction:   column;\n" \
106   "    justify-content:  flex-start;\n" \
107   "    box-sizing:       border-box;\n" \
108   "    overflow-y:       scroll;\n" \
109   "    border:           2pt solid #888;\n" \
110   "    background-color: #eee;\n" \
111   "    height:           calc(100vh - 2em);\n" \
112   "  }\n" \
113   "  div#Chat > div.MessagesAndInput > div#Messages > div.Message > span\n" \
114   "  {\n" \
115   "    white-space: pre\n" \
116   "  }\n" \
117   "  div#Chat > div.MessagesAndInput > div#Messages > div.Message.error > span\n" \
118   "  {\n" \
119   "    color: red;\n" \
120   "  }\n" \
121   "  div#Chat > div.MessagesAndInput > div#Messages > div.Message.system > span\n" \
122   "  {\n" \
123   "    color: green;\n" \
124   "  }\n" \
125   "  div#Chat > div.MessagesAndInput > div#Messages > div.Message.moderator > span\n" \
126   "  {\n" \
127   "    color: #808000;\n" \
128   "  }\n" \
129   "  div#Chat > div.MessagesAndInput > div#Messages > div.Message.private > span\n" \
130   "  {\n" \
131   "    color: blue;\n" \
132   "  }\n" \
133   "  div#Chat > div.MessagesAndInput > div.Input\n" \
134   "  {\n" \
135   "    flex:             0 0 auto;" \
136   "    height:           2em;" \
137   "    display:          flex;" \
138   "    flex-direction:   row;" \
139   "    background-color: #eee;\n" \
140   "  }\n" \
141   "  div#Chat > div.MessagesAndInput > div.Input > input#InputMessage\n" \
142   "  {\n" \
143   "    flex:             1 1 auto;" \
144   "  }\n" \
145   "  div#Chat > div.MessagesAndInput > div.Input > button\n" \
146   "  {\n" \
147   "    flex:             0 0 auto;" \
148   "    width:            5em;" \
149   "    margin-left:      4pt;" \
150   "  }\n" \
151   "  div#Chat > div#Users\n" \
152   "  {\n" \
153   "    flex:             0 0 auto;" \
154   "    width:            20em;" \
155   "    display:          flex;\n" \
156   "    flex-direction:   column;\n" \
157   "    justify-content:  flex-start;\n" \
158   "    box-sizing:       border-box;\n" \
159   "    overflow-y:       scroll;\n" \
160   "    border:           2pt solid #888;\n" \
161   "    background-color: #eee;\n" \
162   "  }\n" \
163   "  div#Chat > div#Users > div\n" \
164   "  {\n" \
165   "    cursor: pointer;\n" \
166   "    user-select: none;\n" \
167   "    -webkit-user-select: none;\n" \
168   "  }\n" \
169   "  div#Chat > div#Users > div.selected\n" \
170   "  {\n" \
171   "    background-color: #7bf;\n" \
172   "  }\n" \
173   "</style>" \
174   "<script>\n" \
175   "  'use strict'\n;" \
176   "\n" \
177   "  let baseUrl;\n" \
178   "  let socket;\n" \
179   "  let connectedUsers = new Map();\n" \
180   "\n" \
181   "  window.addEventListener('load', window_onload);\n" \
182   "\n" \
183   " /**\n" \
184   "    This is the main procedure which initializes the chat and connects the first socket\n" \
185   "  */\n" \
186   "  function window_onload(event)\n" \
187   "  {\n" \
188   " /* Determine the base url (for http:/" "/ this is ws:/" "/ for https:/" \
189                                   "/ this must be wss:/" "/) */\n" \
190   "    baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + ':/" "/' + window.location.host + '/ChatServerWebSocket';\n" \
191   "    chat_generate();\n" \
192   "    chat_connect();\n" \
193   "  }\n" \
194   "\n" \
195   " /**\n" \
196   "    This function generates the chat using DOM\n" \
197   "  */\n" \
198   "  function chat_generate()\n" \
199   "  {\n" \
200   "    document.body.innerHTML = '';\n" \
201   "    let chat = document.createElement('div');\n" \
202   "    document.body.appendChild(chat);\n" \
203   "    chat.id = 'Chat';\n" \
204   "    let messagesAndInput = document.createElement('div');\n" \
205   "    chat.appendChild(messagesAndInput);\n" \
206   "    messagesAndInput.classList.add('MessagesAndInput');\n" \
207   "    let messages = document.createElement('div');\n" \
208   "    messagesAndInput.appendChild(messages);\n" \
209   "    messages.id = 'Messages';\n" \
210   "    let input = document.createElement('div');\n" \
211   "    messagesAndInput.appendChild(input);\n" \
212   "    input.classList.add('Input');\n" \
213   "    let inputMessage = document.createElement('input');\n" \
214   "    input.appendChild(inputMessage);\n" \
215   "    inputMessage.type = 'text';\n" \
216   "    inputMessage.id = 'InputMessage';\n" \
217   "    inputMessage.disabled = true;\n" \
218   "    inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
219   "    let inputMessageSend = document.createElement('button');\n" \
220   "    input.appendChild(inputMessageSend);\n" \
221   "    inputMessageSend.id = 'InputMessageButton';\n" \
222   "    inputMessageSend.disabled = true;\n" \
223   "    inputMessageSend.innerText = 'send';\n" \
224   "    inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \
225   "    let inputImage = document.createElement('input');\n" \
226   "    input.appendChild(inputImage);\n" \
227   "    inputImage.id = 'InputImage';\n" \
228   "    inputImage.type = 'file';\n" \
229   "    inputImage.accept = 'image /*';\n" \
230   "    inputImage.style.display = 'none';\n" \
231   "    inputImage.addEventListener('change', chat_onImageSelected);\n" \
232   "    let inputImageButton = document.createElement('button');\n" \
233   "    input.appendChild(inputImageButton);\n" \
234   "    inputImageButton.id = 'InputImageButton';\n" \
235   "    inputImageButton.disabled = true;\n" \
236   "    inputImageButton.innerText = 'image';\n" \
237   "    inputImageButton.addEventListener('click', chat_onImageClicked);\n" \
238   "    let users = document.createElement('div');\n" \
239   "    chat.appendChild(users);\n" \
240   "    users.id = 'Users';\n" \
241   "    users.addEventListener('click', chat_onUserClicked);\n" \
242   "    let allUsers = document.createElement('div');\n" \
243   "    users.appendChild(allUsers);\n" \
244   "    allUsers.classList.add('selected');\n" \
245   "    allUsers.innerText = '<everyone>';\n" \
246   "    allUsers.setAttribute('data-user', '0');\n" \
247   "  }\n" \
248   "\n" \
249   "  /**\n" \
250   "    This function creates and connects a WebSocket\n" \
251   "  */\n" \
252   "  function chat_connect()\n" \
253   "  {\n" \
254   "    chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \
255   "    socket = new WebSocket(baseUrl);\n" \
256   "    socket.binaryType = 'arraybuffer';\n" \
257   "    socket.onopen    = socket_onopen;\n" \
258   "    socket.onclose   = socket_onclose;\n" \
259   "    socket.onerror   = socket_onerror;\n" \
260   "    socket.onmessage = socket_onmessage;\n" \
261   "  }\n" \
262   "\n" \
263   " /**\n" \
264   "    This function adds new text to the chat list\n" \
265   "  */\n" \
266   "  function chat_addMessage(text, options)\n" \
267   "  {\n" \
268   "    let type = options && options.type || 'regular';\n" \
269   "    if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
270   "      type = 'regular';\n" \
271   "    let message = document.createElement('div');\n" \
272   "    message.classList.add('Message');\n" \
273   "    message.classList.add(type);\n" \
274   "    if(typeof(text) === 'string')\n" \
275   "    {\n" \
276   "      let content = document.createElement('span');\n" \
277   "      message.appendChild(content);\n" \
278   "      if(options && options.from)\n" \
279   "        content.innerText = `${options.from}: ${text}`;\n" \
280   "      else\n" \
281   "        content.innerText = text;\n" \
282   "      if(options && options.reconnect)\n" \
283   "      {\n" \
284   "        let span = document.createElement('span');\n" \
285   "        span.appendChild(document.createTextNode(' ('));\n" \
286   "        let reconnect = document.createElement('a');\n" \
287   "        reconnect.href = 'javascript:chat_connect()';\n" \
288   "        reconnect.innerText = 'reconnect';\n" \
289   "        span.appendChild(reconnect);\n" \
290   "        span.appendChild(document.createTextNode(')'));\n" \
291   "        message.appendChild(span);\n" \
292   "      }\n" \
293   "    }\n" \
294   "    else\n" \
295   "    {\n" \
296   "      let content = document.createElement('span');\n" \
297   "      message.appendChild(content);\n" \
298   "      if(options && options.from)\n" \
299   "      {\n" \
300   "        content.innerText = `${options.from}:\\n`;\n" \
301   "      }\n" \
302   "      if(options && options.pictureType && text instanceof Uint8Array)\n" \
303   "      {\n" \
304   "        let img = document.createElement('img');\n" \
305   "        content.appendChild(img);\n" \
306   "        img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \
307   "      }\n" \
308   "    }\n" \
309   "    document.getElementById('Messages').appendChild(message);\n" \
310   "    message.scrollIntoView();\n" \
311   "  }\n" \
312   "\n" \
313   " /**\n" \
314   "    This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \
315   "  */\n" \
316   "  function chat_onKeyDown(event)\n" \
317   "  {\n" \
318   "    if(event.key == 'Enter')\n" \
319   "      chat_onSendClicked();\n" \
320   "  }\n" \
321   "\n" \
322   " /**\n" \
323   "    This is the code to send a message or command, when clicking the 'send' button\n" \
324   "  */\n" \
325   "  function chat_onSendClicked(event)\n" \
326   "  {\n" \
327   "    let message = document.getElementById('InputMessage').value;\n" \
328   "    if(message.length == 0)\n" \
329   "      return;\n" \
330   "    if(message.substr(0, 1) == '/')\n" \
331   "    {\n" \
332   " /* command */ \n"     \
333   "      let match;\n" \
334   "      if(/^\\/disconnect\\s*$/.test(message))\n" \
335   "      {\n" \
336   "        socket.close(1000);\n" \
337   "      }\n" \
338   "      else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
339   "      {\n" \
340   "        message = message.substr(match[0].length);\n" \
341   "        let userId = chat_getUserIdByName(match[1]);\n" \
342   "        if(userId !== null)\n" \
343   "        {\n" \
344   "          socket.send(`private|${userId}|${message}`);\n" \
345   "        }\n" \
346   "        else\n" \
347   "        {\n" \
348   "          chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \
349   "        }\n" \
350   "      }\n" \
351   "      else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
352   "      {\n" \
353   "        let userId = chat_getUserIdByName(match[1]);\n" \
354   "        if(userId !== null)\n" \
355   "        {\n" \
356   "          socket.send(`ping|${userId}|`);\n" \
357   "        }\n" \
358   "        else\n" \
359   "        {\n" \
360   "          chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \
361   "        }\n" \
362   "      }\n" \
363   "      else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
364   "      {\n" \
365   "        socket.send(`name||${match[1]}`);\n" \
366   "      }\n" \
367   "      else\n" \
368   "      {\n" \
369   "        chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \
370   "      }\n" \
371   "    }\n" \
372   "    else\n" \
373   "    {\n" \
374   " /* regular chat message to the selected user */ \n"     \
375   "      let selectedUser = document.querySelector('div#Users > div.selected');\n" \
376   "      let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
377   "      if(selectedUserId == 0)\n" \
378   "        socket.send(`||${message}`);\n" \
379   "      else\n" \
380   "        socket.send(`private|${selectedUserId}|${message}`);\n" \
381   "    }\n" \
382   "    document.getElementById('InputMessage').value = '';\n" \
383   "  }\n" \
384   "\n" \
385   " /**\n" \
386   "    This is the event when the user hits the 'image' button\n" \
387   "  */\n" \
388   "  function chat_onImageClicked(event)\n" \
389   "  {\n" \
390   "    document.getElementById('InputImage').click();\n" \
391   "  }\n" \
392   "\n" \
393   " /**\n" \
394   "    This is the event when the user selected an image.\n" \
395   "    The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \
396   "  */\n" \
397   "  function chat_onImageSelected(event)\n" \
398   "  {\n" \
399   "    let file = event.target.files[0];\n" \
400   "    if(!file || !/^image\\/" "/.test(file.type))\n" \
401   "      return;\n" \
402   "    let selectedUser = document.querySelector('div#Users > div.selected');\n" \
403   "    let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
404   "    let reader = new FileReader();\n" \
405   "    reader.onload = function(event) {\n" \
406   "      chat_onImageRead(event, file.type, selectedUserId);\n" \
407   "    };\n" \
408   "    reader.readAsArrayBuffer(file);\n" \
409   "  }\n" \
410   "\n" \
411   " /**\n" \
412   "    This is the event when the user selected image has been read.\n" \
413   "    This will add our chat protocol prefix and send it via the websocket.\n" \
414   "  */\n" \
415   "  function chat_onImageRead(event, fileType, selectedUserId)\n" \
416   "  {\n" \
417   "    let encoder = new TextEncoder();\n" \
418   "    let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \
419   "    prefix = encoder.encode(prefix);\n" \
420   "    let byteData = new Uint8Array(event.target.result);\n" \
421   "    let totalLength = prefix.length + byteData.length;\n" \
422   "    let resultByteData = new Uint8Array(totalLength);\n" \
423   "    resultByteData.set(prefix, 0);\n" \
424   "    resultByteData.set(byteData, prefix.length);\n" \
425   "    socket.send(resultByteData);\n" \
426   "  }\n" \
427   "\n" \
428   " /**\n" \
429   "    This is the event when the user clicked a name in the user list.\n" \
430   "    This is useful to send private messages or images without needing to add the /m prefix.\n" \
431   "  */\n" \
432   "  function chat_onUserClicked(event, selectedUserId)\n" \
433   "  {\n" \
434   "    let newSelected = event.target.closest('div#Users > div');\n" \
435   "    if(newSelected === null)\n" \
436   "      return;\n" \
437   "    for(let div of this.querySelectorAll(':scope > div.selected'))\n" \
438   "      div.classList.remove('selected');\n" \
439   "    newSelected.classList.add('selected');\n" \
440   "  }\n" \
441   "\n" \
442   " /**\n" \
443   "    This functions returns the current id of a user identified by its name.\n" \
444   "  */\n" \
445   "  function chat_getUserIdByName(name)\n" \
446   "  {\n" \
447   "    let nameUpper = name.toUpperCase();\n" \
448   "    for(let pair of connectedUsers)\n" \
449   "    {\n" \
450   "      if(pair[1].toUpperCase() == nameUpper)\n" \
451   "        return pair[0];\n" \
452   "    }\n" \
453   "    return null;\n" \
454   "  }\n" \
455   "\n" \
456   " /**\n" \
457   "    This functions clears the entire user list (needed for reconnecting).\n" \
458   "  */\n" \
459   "  function chat_clearUserList()\n" \
460   "  {\n" \
461   "    let users = document.getElementById('Users');\n" \
462   "    for(let div of users.querySelectorAll(':scope > div'))\n" \
463   "    {\n" \
464   "      if(div.getAttribute('data-user') === '0')\n" \
465   "      {\n" \
466   "        div.classList.add('selected');\n" \
467   "      }\n" \
468   "      else\n" \
469   "      {\n" \
470   "        div.parentNode.removeChild(div);\n" \
471   "      }\n" \
472   "    }\n" \
473   "    return null;\n" \
474   "  }\n" \
475   "\n" \
476   " /**\n" \
477   "    This is the event when the socket has established a connection.\n" \
478   "    This will initialize an empty chat and enable the controls.\n" \
479   "  */\n" \
480   "  function socket_onopen(event)\n" \
481   "  {\n" \
482   "    connectedUsers.clear();\n" \
483   "    chat_clearUserList();\n" \
484   "    chat_addMessage('Connected!', { type: 'system' });\n" \
485   "    document.getElementById('InputMessage').disabled       = false;\n" \
486   "    document.getElementById('InputMessageButton').disabled = false;\n" \
487   "    document.getElementById('InputImageButton').disabled   = false;\n" \
488   "  }\n" \
489   "\n" \
490   " /**\n" \
491   "    This is the event when the socket has been closed.\n" \
492   "    This will lock the controls.\n" \
493   "  */\n" \
494   "  function socket_onclose(event)\n" \
495   "  {\n" \
496   "    chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \
497   "    document.getElementById('InputMessage').disabled       = true;\n" \
498   "    document.getElementById('InputMessageButton').disabled = true;\n" \
499   "    document.getElementById('InputImageButton').disabled   = true;\n" \
500   "  }\n" \
501   "\n" \
502   " /**\n" \
503   "    This is the event when the socket reported an error.\n" \
504   "    This will just make an output.\n" \
505   "    In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \
506   "  */\n" \
507   "  function socket_onerror(event)\n" \
508   "  {\n" \
509   "    console.error('WebSocket error reported: ', event);\n" \
510   "    chat_addMessage('The socket reported an error!', { type: 'error' });\n" \
511   "  }\n" \
512   "\n" \
513   " /**\n" \
514   "    This is the event when the socket has received a message.\n" \
515   "    This will parse the message and execute the corresponding command (or add the message).\n" \
516   "  */\n" \
517   "  function socket_onmessage(event)\n" \
518   "  {\n" \
519   "    if(typeof(event.data) === 'string')\n" \
520   "    {\n" \
521   " /* text message or command */ \n"     \
522   "      let message = event.data.split('|', 3);\n" \
523   "      switch(message[0])\n" \
524   "      {\n" \
525   "      case 'userinit':\n" \
526   "        connectedUsers.set(message[1], message[2]);\n" \
527   "        {\n" \
528   "          let users = document.getElementById('Users');\n" \
529   "          let div = document.createElement('div');\n" \
530   "          users.appendChild(div);\n" \
531   "          div.innerText = message[2];\n" \
532   "          div.setAttribute('data-user', message[1]);\n" \
533   "        }\n" \
534   "        break;\n" \
535   "      case 'useradd':\n" \
536   "        connectedUsers.set(message[1], message[2]);\n" \
537   "        chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \
538   "        {\n" \
539   "          let users = document.getElementById('Users');\n" \
540   "          let div = document.createElement('div');\n" \
541   "          users.appendChild(div);\n" \
542   "          div.innerText = message[2];\n" \
543   "          div.setAttribute('data-user', message[1]);\n" \
544   "        }\n" \
545   "        break;\n" \
546   "      case 'userdel':\n" \
547   "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \
548   "        connectedUsers.delete(message[1]);\n" \
549   "        {\n" \
550   "          let users = document.getElementById('Users');\n" \
551   "          let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
552   "          if(div !== null)\n" \
553   "          {\n" \
554   "            users.removeChild(div);\n" \
555   "            if(div.classList.contains('selected'))\n" \
556   "              users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
557   "          }\n" \
558   "        }\n" \
559   "        break;\n" \
560   "      case 'username':\n" \
561   "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \
562   "        connectedUsers.set(message[1], message[2]);\n" \
563   "        {\n" \
564   "          let users = document.getElementById('Users');\n" \
565   "          let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
566   "          if(div !== null)\n" \
567   "          {\n" \
568   "            div.innerText = message[2];\n" \
569   "          }\n" \
570   "        }\n" \
571   "        break;\n" \
572   "      case 'ping':\n" \
573   "        chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \
574   "        break;\n" \
575   "      default:\n" \
576   "        chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \
577   "        break;\n" \
578   "      }\n" \
579   "    }\n" \
580   "    else\n" \
581   "    {\n" \
582   " /* We received a binary frame, which means a picture here */ \n"     \
583   "      let byteData = new Uint8Array(event.data);\n" \
584   "      let decoder = new TextDecoder();\n" \
585   "      let message  = [ ];\n" \
586   " /* message type */ \n"     \
587   "      let j = 0;\n" \
588   "      let i = byteData.indexOf(0x7C, j); /* | = 0x7C;*/ \n"\
589   "      if(i < 0)\n" \
590   "        return;\n" \
591   "      message.push(decoder.decode(byteData.slice(0, i)));\n" \
592   " /* picture from */ \n"     \
593   "      j = i + 1;\n" \
594   "      i = byteData.indexOf(0x7C, j);\n" \
595   "      if(i < 0)\n" \
596   "        return;\n" \
597   "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
598   " /* picture encoding */ \n"     \
599   "      j = i + 1;\n" \
600   "      i = byteData.indexOf(0x7C, j);\n" \
601   "      if(i < 0)\n" \
602   "        return;\n" \
603   "      message.push(decoder.decode(byteData.slice(j, i)));\n" \
604   " /* picture */ \n"     \
605   "      byteData = byteData.slice(i + 1);\n" \
606   "      chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \
607   "    }\n" \
608   "  }\n" \
609   "</script>" \
610   "</head>" \
611   "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \
612   "</html>"
613 
614 #define PAGE_NOT_FOUND \
615   "404 Not Found"
616 
617 #define PAGE_INVALID_WEBSOCKET_REQUEST \
618   "Invalid WebSocket request!"
619 
620 /**
621  * This struct is used to keep the data of a connected chat user.
622  * It is passed to the socket-receive thread (connecteduser_receive_messages) as well as to
623  * the socket-send thread (connecteduser_send_messages).
624  * It can also be accessed via the global array users (mutex protected).
625  */
626 struct ConnectedUser
627 {
628   /* the TCP/IP socket for reading/writing */
629   MHD_socket fd;
630   /* the UpgradeResponseHandle of libmicrohttpd (needed for closing the socket) */
631   struct MHD_UpgradeResponseHandle *urh;
632   /* the websocket encode/decode stream */
633   struct MHD_WebSocketStream *ws;
634   /* the possibly read data at the start (only used once) */
635   char *extra_in;
636   size_t extra_in_size;
637   /* the unique user id (counting from 1, ids will never be re-used) */
638   size_t user_id;
639   /* the current user name */
640   char *user_name;
641   size_t user_name_len;
642   /* the zero-based index of the next message;
643      may be decremented when old messages are deleted */
644   size_t next_message_index;
645   /* specifies whether the websocket shall be closed (1) or not (0) */
646   int disconnect;
647   /* condition variable to wake up the sender of this connection */
648   pthread_cond_t wake_up_sender;
649   /* mutex to ensure that no send actions are mixed
650      (sending can be done by send and recv thread;
651       may not be simultaneously locked with chat_mutex by the same thread) */
652   pthread_mutex_t send_mutex;
653   /* specifies whether a ping shall be executed (1), is being executed (2) or
654      no ping is pending (0) */
655   int ping_status;
656   /* the start time of the ping, if a ping is running */
657   struct timespec ping_start;
658   /* the message used for the ping (must match the pong response)*/
659   char ping_message[128];
660   /* the length of the ping message (may not exceed 125) */
661   size_t ping_message_len;
662   /* the numeric ping message suffix to detect ping messages, which are too old */
663   int ping_counter;
664 };
665 
666 /**
667  * A single message, which has been send via the chat.
668  * This can be text, an image or a command.
669  */
670 struct Message
671 {
672   /* The user id of the sender. This is 0 if it is a system message- */
673   size_t from_user_id;
674   /* The user id of the recipient. This is 0 if every connected user shall receive it */
675   size_t to_user_id;
676   /* The data of the message. */
677   char *data;
678   size_t data_len;
679   /* Specifies whether the data is UTF-8 encoded text (0) or binary data (1) */
680   int is_binary;
681 };
682 
683 /* the unique user counter for new users (only accessed by main thread) */
684 size_t unique_user_id = 0;
685 
686 /* the chat data (users and messages; may be accessed by all threads, but is protected by mutex) */
687 pthread_mutex_t chat_mutex;
688 struct ConnectedUser **users = NULL;
689 size_t user_count = 0;
690 struct Message **messages = NULL;
691 size_t message_count = 0;
692 /* specifies whether all websockets must close (1) or not (0) */
693 volatile int disconnect_all = 0;
694 /* a counter for cleaning old messages (each 10 messages we will try to clean the list */
695 int clean_count = 0;
696 #define CLEANUP_LIMIT 10
697 
698 /**
699  * Change socket to blocking.
700  *
701  * @param fd the socket to manipulate
702  */
703 static void
make_blocking(MHD_socket fd)704 make_blocking (MHD_socket fd)
705 {
706 #if defined(MHD_POSIX_SOCKETS)
707   int flags;
708 
709   flags = fcntl (fd, F_GETFL);
710   if (-1 == flags)
711     abort ();
712   if ((flags & ~O_NONBLOCK) != flags)
713     if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
714       abort ();
715 #elif defined(MHD_WINSOCK_SOCKETS)
716   unsigned long flags = 0;
717 
718   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
719     abort ();
720 #endif /* MHD_WINSOCK_SOCKETS */
721 }
722 
723 
724 /**
725  * Sends all data of the given buffer via the TCP/IP socket
726  *
727  * @param fd  The TCP/IP socket which is used for sending
728  * @param buf The buffer with the data to send
729  * @param len The length in bytes of the data in the buffer
730  */
731 static void
send_all(struct ConnectedUser * cu,const char * buf,size_t len)732 send_all (struct ConnectedUser *cu,
733           const char *buf,
734           size_t len)
735 {
736   ssize_t ret;
737   size_t off;
738 
739   if (0 == pthread_mutex_lock (&cu->send_mutex))
740   {
741     for (off = 0; off < len; off += ret)
742     {
743       ret = send (cu->fd,
744                   &buf[off],
745                   (int) (len - off),
746                   0);
747       if (0 > ret)
748       {
749         if (EAGAIN == errno)
750         {
751           ret = 0;
752           continue;
753         }
754         break;
755       }
756       if (0 == ret)
757         break;
758     }
759     pthread_mutex_unlock (&cu->send_mutex);
760   }
761 }
762 
763 
764 /**
765  * Adds a new chat message to the list of messages.
766  *
767  * @param from_user_id the user id of the sender (0 means system)
768  * @param to_user_id   the user id of the recipiend (0 means everyone)
769  * @param data         the data to send (UTF-8 text or binary; will be copied)
770  * @param data_len     the length of the data to send
771  * @param is_binary    specifies whether the data is UTF-8 text (0) or binary (1)
772  * @param needs_lock   specifies whether the caller has already locked the global chat mutex (0) or
773  *                     if this procedure needs to lock it (1)
774  *
775  * @return             0 on success, other values on error
776  */
777 static int
chat_addmessage(size_t from_user_id,size_t to_user_id,char * data,size_t data_len,int is_binary,int needs_lock)778 chat_addmessage (size_t from_user_id,
779                  size_t to_user_id,
780                  char *data,
781                  size_t data_len,
782                  int is_binary,
783                  int needs_lock)
784 {
785   /* allocate the buffer and fill it with data */
786   struct Message *message = (struct Message *) malloc (sizeof (struct Message));
787   if (NULL == message)
788     return 1;
789 
790   memset (message, 0, sizeof (struct Message));
791   message->from_user_id = from_user_id;
792   message->to_user_id   = to_user_id;
793   message->is_binary    = is_binary;
794   message->data_len     = data_len;
795   message->data = malloc (data_len + 1);
796   if (NULL == message->data)
797   {
798     free (message);
799     return 1;
800   }
801   memcpy (message->data, data, data_len);
802   message->data[data_len] = 0;
803 
804   /* lock the global mutex if needed */
805   if (0 != needs_lock)
806   {
807     if (0 != pthread_mutex_lock (&chat_mutex))
808       return 1;
809   }
810 
811   /* add the new message to the global message list */
812   size_t message_count_ = message_count + 1;
813   struct Message **messages_ = (struct Message **) realloc (messages,
814                                                             message_count_
815                                                             * sizeof (struct
816                                                                       Message *));
817   if (NULL == messages_)
818   {
819     free (message);
820     if (0 != needs_lock)
821       pthread_mutex_unlock (&chat_mutex);
822     return 1;
823   }
824   messages_[message_count] = message;
825   messages = messages_;
826   message_count = message_count_;
827 
828   /* inform the sender threads about the new message */
829   for (size_t i = 0; i < user_count; ++i)
830     pthread_cond_signal (&users[i]->wake_up_sender);
831 
832   /* unlock the global mutex if needed */
833   if (0 != needs_lock)
834   {
835     if (0 != needs_lock)
836       pthread_mutex_unlock (&chat_mutex);
837   }
838   return 0;
839 }
840 
841 
842 /**
843  * Cleans up old messages
844  *
845  * @param needs_lock   specifies whether the caller has already locked the global chat mutex (0) or
846  *                     if this procedure needs to lock it (1)
847  * @return             0 on success, other values on error
848  */
849 static int
chat_clearmessages(int needs_lock)850 chat_clearmessages (int needs_lock)
851 {
852   /* lock the global mutex if needed */
853   if (0 != needs_lock)
854   {
855     if (0 != pthread_mutex_lock (&chat_mutex))
856       return 1;
857   }
858 
859   /* update the clean counter and check whether we need cleaning */
860   ++clean_count;
861   if (CLEANUP_LIMIT > clean_count)
862   {
863     /* no cleanup required */
864     if (0 != needs_lock)
865     {
866       pthread_mutex_unlock (&chat_mutex);
867     }
868     return 0;
869   }
870   clean_count = 0;
871 
872   /* check whether we got any messages (without them no cleaning is required */
873   if (0 < message_count)
874   {
875     /* then check whether we got any connected users */
876     if (0 < user_count)
877     {
878       /* determine the minimum index for the next message of all connected users */
879       size_t min_message = users[0]->next_message_index;
880       for (size_t i = 1; i < user_count; ++i)
881       {
882         if (min_message > users[i]->next_message_index)
883           min_message = users[i]->next_message_index;
884       }
885       if (0 < min_message)
886       {
887         /* remove all messages with index below min_message and update
888            the message indices of the users */
889         for (size_t i = 0; i < min_message; ++i)
890         {
891           free (messages[i]->data);
892           free (messages[i]);
893         }
894         for (size_t i = min_message; i < message_count; ++i)
895           messages[i - min_message] = messages[i];
896         message_count -= min_message;
897         for (size_t i = 0; i < user_count; ++i)
898           users[i]->next_message_index -= min_message;
899       }
900     }
901     else
902     {
903       /* without connected users, simply remove all messages */
904       for (size_t i = 0; i < message_count; ++i)
905       {
906         free (messages[i]->data);
907         free (messages[i]);
908       }
909       free (messages);
910       messages = NULL;
911       message_count = 0;
912     }
913   }
914 
915   /* unlock the global mutex if needed */
916   if (0 != needs_lock)
917   {
918     pthread_mutex_unlock (&chat_mutex);
919   }
920   return 0;
921 }
922 
923 
924 /**
925  * Adds a new chat user to the global user list.
926  * This will be called at the start of connecteduser_receive_messages.
927  *
928  * @param cu The connected user
929  * @return   0 on success, other values on error
930  */
931 static int
chat_adduser(struct ConnectedUser * cu)932 chat_adduser (struct ConnectedUser *cu)
933 {
934   /* initialize the notification message of the new user */
935   char user_index[32];
936   snprintf (user_index, 32, "%d", (int) cu->user_id);
937   size_t user_index_len = strlen (user_index);
938   size_t data_len = user_index_len + cu->user_name_len + 9;
939   char *data = (char *) malloc (data_len + 1);
940   if (NULL == data)
941     return 1;
942   strcpy (data, "useradd|");
943   strcat (data, user_index);
944   strcat (data, "|");
945   strcat (data, cu->user_name);
946 
947   /* lock the mutex */
948   if (0 != pthread_mutex_lock (&chat_mutex))
949   {
950     free (data);
951     return 1;
952   }
953   /* inform the other chat users about the new user */
954   if (0 != chat_addmessage (0,
955                             0,
956                             data,
957                             data_len,
958                             0,
959                             0))
960   {
961     free (data);
962     pthread_mutex_unlock (&chat_mutex);
963     return 1;
964   }
965   free (data);
966 
967   /* add the new user to the list */
968   size_t user_count_ = user_count + 1;
969   struct ConnectedUser **users_ = (struct ConnectedUser **) realloc (users,
970                                                                      user_count_
971                                                                      * sizeof (
972                                                                        struct
973                                                                        ConnectedUser
974                                                                        *));
975   if (NULL == users_)
976   {
977     /* realloc failed */
978     pthread_mutex_unlock (&chat_mutex);
979     return 1;
980   }
981   users_[user_count] = cu;
982   users      = users_;
983   user_count = user_count_;
984 
985   /* Initialize the next message index to the current message count. */
986   /* This will skip all old messages for this new connected user. */
987   cu->next_message_index = message_count;
988 
989   /* unlock the mutex */
990   pthread_mutex_unlock (&chat_mutex);
991   return 0;
992 }
993 
994 
995 /**
996  * Removes a chat user from the global user list.
997  *
998  * @param cu The connected user
999  * @return   0 on success, other values on error
1000  */
1001 static int
chat_removeuser(struct ConnectedUser * cu)1002 chat_removeuser (struct ConnectedUser *cu)
1003 {
1004   char user_index[32];
1005 
1006   /* initialize the chat message for the removed user */
1007   snprintf (user_index, 32, "%d", (int) cu->user_id);
1008   size_t user_index_len = strlen (user_index);
1009   size_t data_len = user_index_len + 9;
1010   char *data = (char *) malloc (data_len + 1);
1011   if (NULL == data)
1012     return 1;
1013   strcpy (data, "userdel|");
1014   strcat (data, user_index);
1015   strcat (data, "|");
1016 
1017   /* lock the mutex */
1018   if (0 != pthread_mutex_lock (&chat_mutex))
1019   {
1020     free (data);
1021     return 1;
1022   }
1023   /* inform the other chat users that the user is gone */
1024   int got_error = 0;
1025   if (0 != chat_addmessage (0, 0, data, data_len, 0, 0))
1026   {
1027     free (data);
1028     got_error = 1;
1029   }
1030 
1031   /* remove the user from the list */
1032   int found = 0;
1033   for (size_t i = 0; i < user_count; ++i)
1034   {
1035     if (cu == users[i])
1036     {
1037       found = 1;
1038       for (size_t j = i + 1; j < user_count; ++j)
1039       {
1040         users[j - 1] = users[j];
1041       }
1042       --user_count;
1043       break;
1044     }
1045   }
1046   if (0 == found)
1047     got_error = 1;
1048 
1049   /* unlock the mutex */
1050   pthread_mutex_unlock (&chat_mutex);
1051 
1052   return got_error;
1053 }
1054 
1055 
1056 /**
1057  * Renames a chat user
1058  *
1059  * @param cu           The connected user
1060  * @param new_name     The new user name. On success this pointer will be taken.
1061  * @param new_name_len The length of the new name
1062  * @return             0 on success, other values on error. 2 means name already in use.
1063  */
1064 static int
chat_renameuser(struct ConnectedUser * cu,char * new_name,size_t new_name_len)1065 chat_renameuser (struct ConnectedUser *cu,
1066                  char *new_name,
1067                  size_t new_name_len)
1068 {
1069   /* lock the mutex */
1070   if (0 != pthread_mutex_lock (&chat_mutex))
1071   {
1072     return 1;
1073   }
1074 
1075   /* check whether the name is already in use */
1076   for (size_t i = 0; i < user_count; ++i)
1077   {
1078     if (cu != users[i])
1079     {
1080       if ((users[i]->user_name_len == new_name_len) &&
1081           (0 == strcasecmp (users[i]->user_name, new_name)))
1082       {
1083         pthread_mutex_unlock (&chat_mutex);
1084         return 2;
1085       }
1086     }
1087   }
1088 
1089   /* generate the notification message */
1090   char user_index[32];
1091   snprintf (user_index, 32, "%d", (int) cu->user_id);
1092   size_t user_index_len = strlen (user_index);
1093   size_t data_len = user_index_len + new_name_len + 10;
1094   char *data = (char *) malloc (data_len + 1);
1095   if (NULL == data)
1096     return 1;
1097   strcpy (data, "username|");
1098   strcat (data, user_index);
1099   strcat (data, "|");
1100   strcat (data, new_name);
1101 
1102   /* inform the other chat users about the new name */
1103   if (0 != chat_addmessage (0, 0, data, data_len, 0, 0))
1104   {
1105     free (data);
1106     pthread_mutex_unlock (&chat_mutex);
1107     return 1;
1108   }
1109   free (data);
1110 
1111   /* accept the new user name */
1112   free (cu->user_name);
1113   cu->user_name = new_name;
1114   cu->user_name_len = new_name_len;
1115 
1116   /* unlock the mutex */
1117   pthread_mutex_unlock (&chat_mutex);
1118 
1119   return 0;
1120 }
1121 
1122 
1123 /**
1124 * Parses received data from the TCP/IP socket with the websocket stream
1125 *
1126 * @param cu           The connected user
1127 * @param new_name     The new user name
1128 * @param new_name_len The length of the new name
1129 * @return             0 on success, other values on error
1130 */
1131 static int
connecteduser_parse_received_websocket_stream(struct ConnectedUser * cu,char * buf,size_t buf_len)1132 connecteduser_parse_received_websocket_stream (struct ConnectedUser *cu,
1133                                                char *buf,
1134                                                size_t buf_len)
1135 {
1136   size_t buf_offset = 0;
1137   while (buf_offset < buf_len)
1138   {
1139     size_t new_offset = 0;
1140     char *frame_data = NULL;
1141     size_t frame_len  = 0;
1142     int status = MHD_websocket_decode (cu->ws,
1143                                        buf + buf_offset,
1144                                        buf_len - buf_offset,
1145                                        &new_offset,
1146                                        &frame_data,
1147                                        &frame_len);
1148     if (0 > status)
1149     {
1150       /* an error occurred and the connection must be closed */
1151       if (NULL != frame_data)
1152       {
1153         /* depending on the WebSocket flag */
1154         /* MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR */
1155         /* close frames might be generated on errors */
1156         send_all (cu,
1157                   frame_data,
1158                   frame_len);
1159         MHD_websocket_free (cu->ws, frame_data);
1160       }
1161       return 1;
1162     }
1163     else
1164     {
1165       buf_offset += new_offset;
1166 
1167       if (0 < status)
1168       {
1169         /* the frame is complete */
1170         switch (status)
1171         {
1172         case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
1173         case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
1174           /**
1175            * a text or binary frame has been received.
1176            * in this chat server example we use a simple protocol where
1177            * the JavaScript added a prefix like "<command>|<to_user_id>|data".
1178            * Some examples:
1179            * "||test" means a regular chat message to everyone with the message "test".
1180            * "private|1|secret" means a private chat message to user with id 1 with the message "secret".
1181            * "name||MyNewName" means that the user requests a rename to "MyNewName"
1182            * "ping|1|" means that the user with id 1 shall get a ping
1183            *
1184            * Binary data is handled here like text data.
1185            * The difference in the data is only checked by the JavaScript.
1186            */
1187           {
1188             size_t command      = 1000;
1189             size_t from_user_id = cu->user_id;
1190             size_t to_user_id   = 0;
1191             size_t i;
1192 
1193             /* parse the command */
1194             for (i = 0; i < frame_len; ++i)
1195             {
1196               if ('|' == frame_data[i])
1197               {
1198                 frame_data[i] = 0;
1199                 ++i;
1200                 break;
1201               }
1202             }
1203             if (0 < i)
1204             {
1205               if (i == 1)
1206               {
1207                 /* no command means regular message */
1208                 command = 0;
1209               }
1210               else if (0 == strcasecmp (frame_data, "private"))
1211               {
1212                 /* private means private message */
1213                 command = 1;
1214               }
1215               else if (0 == strcasecmp (frame_data, "name"))
1216               {
1217                 /* name means chat user rename */
1218                 command = 2;
1219               }
1220               else if (0 == strcasecmp (frame_data, "ping"))
1221               {
1222                 /* ping means a ping request */
1223                 command = 3;
1224               }
1225               else
1226               {
1227                 /* no other commands supported, so this means invalid */
1228                 command = 1000;
1229               }
1230             }
1231 
1232             /* parse the to_user_id, if given */
1233             size_t j = i;
1234             for (; j < frame_len; ++j)
1235             {
1236               if ('|' == frame_data[j])
1237               {
1238                 frame_data[j] = 0;
1239                 ++j;
1240                 break;
1241               }
1242             }
1243             if (i + 1 < j)
1244             {
1245               to_user_id = (size_t) atoi (frame_data + i);
1246             }
1247 
1248             /* decide via the command what action to do */
1249             if (frame_len >= j)
1250             {
1251               int is_binary = (MHD_WEBSOCKET_STATUS_BINARY_FRAME == status ? 1 :
1252                                0);
1253               switch (command)
1254               {
1255               case 0:
1256                 /* regular chat message */
1257                 {
1258                   /**
1259                   * Generate the message for the message list.
1260                   * Regular chat messages get the command "regular".
1261                   * After that we add the from_user_id, followed by the content.
1262                   * The content must always be copied with memcpy instead of strcat,
1263                   * because the data (binary as well as UTF-8 encoded) is allowed
1264                   * to contain the NUL character.
1265                   * However we will add a terminating NUL character,
1266                   * which is not included in the data length
1267                   * (and thus will not be send to the recipients).
1268                   * This is useful for debugging with an IDE.
1269                   */
1270                   char user_index[32];
1271                   snprintf (user_index, 32, "%d", (int) cu->user_id);
1272                   size_t user_index_len = strlen (user_index);
1273                   size_t data_len = user_index_len + frame_len - j + 9;
1274                   char *data = (char *) malloc (data_len + 1);
1275                   if (NULL != data)
1276                   {
1277                     strcpy (data, "regular|");
1278                     strcat (data, user_index);
1279                     strcat (data, "|");
1280                     size_t offset = strlen (data);
1281                     memcpy (data + offset,
1282                             frame_data + j,
1283                             frame_len - j);
1284                     data[data_len] = 0;
1285 
1286                     /* add the chat message to the global list */
1287                     chat_addmessage (from_user_id,
1288                                      0,
1289                                      data,
1290                                      data_len,
1291                                      is_binary,
1292                                      1);
1293                     free (data);
1294                   }
1295                 }
1296                 break;
1297 
1298               case 1:
1299                 /* private chat message */
1300                 if (0 != to_user_id)
1301                 {
1302                   /**
1303                    * Generate the message for the message list.
1304                    * This is similar to the code for regular messages above.
1305                    * The difference is the prefix "private"
1306                   */
1307                   char user_index[32];
1308                   snprintf (user_index, 32, "%d", (int) cu->user_id);
1309                   size_t user_index_len = strlen (user_index);
1310                   size_t data_len = user_index_len + frame_len - j + 9;
1311                   char *data = (char *) malloc (data_len + 1);
1312                   if (NULL != data)
1313                   {
1314 
1315                     strcpy (data, "private|");
1316                     strcat (data, user_index);
1317                     strcat (data, "|");
1318                     size_t offset = strlen (data);
1319                     memcpy (data + offset,
1320                             frame_data + j,
1321                             frame_len - j);
1322                     data[data_len] = 0;
1323 
1324                     /* add the chat message to the global list */
1325                     chat_addmessage (from_user_id,
1326                                      to_user_id,
1327                                      data,
1328                                      data_len,
1329                                      is_binary,
1330                                      1);
1331                     free (data);
1332                   }
1333                 }
1334                 break;
1335 
1336               case 2:
1337                 /* rename */
1338                 {
1339                   /* check whether the new name is valid and allocate a new buffer for it */
1340                   size_t new_name_len = frame_len - j;
1341                   if (0 == new_name_len)
1342                   {
1343                     chat_addmessage (0,
1344                                      from_user_id,
1345                                      "error||Your new name is invalid. You haven't been renamed.",
1346                                      58,
1347                                      0,
1348                                      1);
1349                     break;
1350                   }
1351                   char *new_name = (char *) malloc (new_name_len + 1);
1352                   if (NULL == new_name)
1353                   {
1354                     chat_addmessage (0,
1355                                      from_user_id,
1356                                      "error||Error while renaming. You haven't been renamed.",
1357                                      54,
1358                                      0,
1359                                      1);
1360                     break;
1361                   }
1362                   new_name[new_name_len] = 0;
1363                   for (size_t k = 0; k < new_name_len; ++k)
1364                   {
1365                     char c = frame_data[j + k];
1366                     if ((32 >= c) || (c >= 127))
1367                     {
1368                       free (new_name);
1369                       new_name = NULL;
1370                       chat_addmessage (0,
1371                                        from_user_id,
1372                                        "error||Your new name contains invalid characters. You haven't been renamed.",
1373                                        75,
1374                                        0,
1375                                        1);
1376                       break;
1377                     }
1378                     new_name[k] = c;
1379                   }
1380                   if (NULL == new_name)
1381                     break;
1382 
1383                   /* rename the user */
1384                   int rename_result = chat_renameuser (cu,
1385                                                        new_name,
1386                                                        new_name_len);
1387                   if (0 != rename_result)
1388                   {
1389                     /* the buffer will only be freed if no rename was possible */
1390                     free (new_name);
1391                     if (2 == rename_result)
1392                     {
1393                       chat_addmessage (0,
1394                                        from_user_id,
1395                                        "error||Your new name is already in use by another user. You haven't been renamed.",
1396                                        81,
1397                                        0,
1398                                        1);
1399                     }
1400                     else
1401                     {
1402                       chat_addmessage (0,
1403                                        from_user_id,
1404                                        "error||Error while renaming. You haven't been renamed.",
1405                                        54,
1406                                        0,
1407                                        1);
1408                     }
1409                   }
1410                 }
1411                 break;
1412 
1413               case 3:
1414                 /* ping */
1415                 {
1416                   if (0 == pthread_mutex_lock (&chat_mutex))
1417                   {
1418                     /* check whether the to_user exists */
1419                     struct ConnectedUser *ping_user = NULL;
1420                     for (size_t k = 0; k < user_count; ++k)
1421                     {
1422                       if (users[k]->user_id == to_user_id)
1423                       {
1424                         ping_user = users[k];
1425                         break;
1426                       }
1427                     }
1428                     if (NULL == ping_user)
1429                     {
1430                       chat_addmessage (0,
1431                                        from_user_id,
1432                                        "error||Couldn't find the specified user for pinging.",
1433                                        52,
1434                                        0,
1435                                        0);
1436                     }
1437                     else
1438                     {
1439                       /* if pinging is requested, */
1440                       /* we mark the user and inform the sender about this */
1441                       if (0 == ping_user->ping_status)
1442                       {
1443                         ping_user->ping_status = 1;
1444                         pthread_cond_signal (&ping_user->wake_up_sender);
1445                       }
1446                     }
1447                     pthread_mutex_unlock (&chat_mutex);
1448                   }
1449                   else
1450                   {
1451                     chat_addmessage (0,
1452                                      from_user_id,
1453                                      "error||Error while pinging.",
1454                                      27,
1455                                      0,
1456                                      1);
1457                   }
1458                 }
1459                 break;
1460 
1461               default:
1462                 /* invalid command */
1463                 chat_addmessage (0,
1464                                  from_user_id,
1465                                  "error||You sent an invalid command.",
1466                                  35,
1467                                  0,
1468                                  1);
1469                 break;
1470               }
1471             }
1472           }
1473           MHD_websocket_free (cu->ws,
1474                               frame_data);
1475           return 0;
1476 
1477         case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
1478           /* if we receive a close frame, we will respond with one */
1479           MHD_websocket_free (cu->ws,
1480                               frame_data);
1481           {
1482             char *result = NULL;
1483             size_t result_len = 0;
1484             int er = MHD_websocket_encode_close (cu->ws,
1485                                                  MHD_WEBSOCKET_CLOSEREASON_REGULAR,
1486                                                  NULL,
1487                                                  0,
1488                                                  &result,
1489                                                  &result_len);
1490             if (MHD_WEBSOCKET_STATUS_OK == er)
1491             {
1492               send_all (cu,
1493                         result,
1494                         result_len);
1495               MHD_websocket_free (cu->ws, result);
1496             }
1497           }
1498           return 1;
1499 
1500         case MHD_WEBSOCKET_STATUS_PING_FRAME:
1501           /* if we receive a ping frame, we will respond */
1502           /* with the corresponding pong frame */
1503           {
1504             char *pong = NULL;
1505             size_t pong_len = 0;
1506             int er = MHD_websocket_encode_pong (cu->ws,
1507                                                 frame_data,
1508                                                 frame_len,
1509                                                 &pong,
1510                                                 &pong_len);
1511 
1512             MHD_websocket_free (cu->ws,
1513                                 frame_data);
1514             if (MHD_WEBSOCKET_STATUS_OK == er)
1515             {
1516               send_all (cu,
1517                         pong,
1518                         pong_len);
1519               MHD_websocket_free (cu->ws,
1520                                   pong);
1521             }
1522           }
1523           return 0;
1524 
1525         case MHD_WEBSOCKET_STATUS_PONG_FRAME:
1526           /* if we receive a pong frame, */
1527           /* we will check whether we requested this frame and */
1528           /* whether it is the last requested pong */
1529           if (2 == cu->ping_status)
1530           {
1531             cu->ping_status = 0;
1532             struct timespec now;
1533             timespec_get (&now, TIME_UTC);
1534             if ((cu->ping_message_len == frame_len) &&
1535                 (0 == strcmp (frame_data,
1536                               cu->ping_message)))
1537             {
1538               int ping = (int) (((int64_t) (now.tv_sec
1539                                             - cu->ping_start.tv_sec))  * 1000
1540                                 + ((int64_t) (now.tv_nsec
1541                                               - cu->ping_start.tv_nsec))
1542                                 / 1000000);
1543               char result_text[240];
1544               strcpy (result_text,
1545                       "ping|");
1546               snprintf (result_text + 5, 235, "%d", (int) cu->user_id);
1547               strcat (result_text,
1548                       "|");
1549               snprintf (result_text + strlen (result_text), 240 - strlen (
1550                           result_text), "%d", (int) ping);
1551               chat_addmessage (0,
1552                                0,
1553                                result_text,
1554                                strlen (result_text),
1555                                0,
1556                                1);
1557             }
1558           }
1559           MHD_websocket_free (cu->ws,
1560                               frame_data);
1561           return 0;
1562 
1563         default:
1564           /* This case should really never happen, */
1565           /* because there are only five types of (finished) websocket frames. */
1566           /* If it is ever reached, it means that there is memory corruption. */
1567           MHD_websocket_free (cu->ws,
1568                               frame_data);
1569           return 1;
1570         }
1571       }
1572     }
1573   }
1574 
1575   return 0;
1576 }
1577 
1578 
1579 /**
1580  * Sends messages from the message list over the TCP/IP socket
1581  * after encoding it with the websocket stream.
1582  * This is also used for server-side actions,
1583  * because the thread for receiving messages waits for
1584  * incoming data and cannot be woken up.
1585  * But the sender thread can be woken up easily.
1586  *
1587  * @param cls          The connected user
1588  * @return             Always NULL
1589  */
1590 static void *
connecteduser_send_messages(void * cls)1591 connecteduser_send_messages (void *cls)
1592 {
1593   struct ConnectedUser *cu = cls;
1594 
1595   /* the main loop of sending messages requires to lock the mutex */
1596   if (0 == pthread_mutex_lock (&chat_mutex))
1597   {
1598     for (;;)
1599     {
1600       /* loop while not all messages processed */
1601       int all_messages_read = 0;
1602       while (0 == all_messages_read)
1603       {
1604         if (1 == disconnect_all)
1605         {
1606           /* the application closes and want that we disconnect all users */
1607           struct MHD_UpgradeResponseHandle *urh = cu->urh;
1608           if (NULL != urh)
1609           {
1610             /* Close the TCP/IP socket. */
1611             /* This will also wake-up the waiting receive-thread for this connected user. */
1612             cu->urh = NULL;
1613             MHD_upgrade_action (urh,
1614                                 MHD_UPGRADE_ACTION_CLOSE);
1615           }
1616           pthread_mutex_unlock (&chat_mutex);
1617           return NULL;
1618         }
1619         else if (1 == cu->disconnect)
1620         {
1621           /* The sender thread shall close. */
1622           /* This is only requested by the receive thread, so we can just leave. */
1623           pthread_mutex_unlock (&chat_mutex);
1624           return NULL;
1625         }
1626         else if (1 == cu->ping_status)
1627         {
1628           /* A pending ping is requested */
1629           ++cu->ping_counter;
1630           strcpy (cu->ping_message,
1631                   "libmicrohttpdchatserverpingdata");
1632           snprintf (cu->ping_message + 31, 97, "%d", (int) cu->ping_counter);
1633           cu->ping_message_len = strlen (cu->ping_message);
1634           char *frame_data = NULL;
1635           size_t frame_len = 0;
1636           int er = MHD_websocket_encode_ping (cu->ws,
1637                                               cu->ping_message,
1638                                               cu->ping_message_len,
1639                                               &frame_data,
1640                                               &frame_len);
1641           if (MHD_WEBSOCKET_STATUS_OK == er)
1642           {
1643             cu->ping_status = 2;
1644             timespec_get (&cu->ping_start, TIME_UTC);
1645 
1646             /* send the data via the TCP/IP socket and */
1647             /* unlock the mutex while sending */
1648             pthread_mutex_unlock (&chat_mutex);
1649             send_all (cu,
1650                       frame_data,
1651                       frame_len);
1652             if (0 != pthread_mutex_lock (&chat_mutex))
1653             {
1654               return NULL;
1655             }
1656           }
1657           MHD_websocket_free (cu->ws, frame_data);
1658         }
1659         else if (cu->next_message_index < message_count)
1660         {
1661           /* a chat message or command is pending */
1662           char *frame_data = NULL;
1663           size_t frame_len = 0;
1664           int er = 0;
1665           {
1666             struct Message *msg = messages[cu->next_message_index];
1667             if ((0 == msg->to_user_id) ||
1668                 (cu->user_id == msg->to_user_id) ||
1669                 (cu->user_id == msg->from_user_id) )
1670             {
1671               if (0 == msg->is_binary)
1672               {
1673                 er = MHD_websocket_encode_text (cu->ws,
1674                                                 msg->data,
1675                                                 msg->data_len,
1676                                                 MHD_WEBSOCKET_FRAGMENTATION_NONE,
1677                                                 &frame_data,
1678                                                 &frame_len,
1679                                                 NULL);
1680               }
1681               else
1682               {
1683                 er = MHD_websocket_encode_binary (cu->ws,
1684                                                   msg->data,
1685                                                   msg->data_len,
1686                                                   MHD_WEBSOCKET_FRAGMENTATION_NONE,
1687                                                   &frame_data,
1688                                                   &frame_len);
1689               }
1690             }
1691           }
1692           ++cu->next_message_index;
1693 
1694           /* send the data via the TCP/IP socket and */
1695           /* unlock the mutex while sending */
1696           pthread_mutex_unlock (&chat_mutex);
1697           if (MHD_WEBSOCKET_STATUS_OK == er)
1698           {
1699             send_all (cu,
1700                       frame_data,
1701                       frame_len);
1702           }
1703           MHD_websocket_free (cu->ws,
1704                               frame_data);
1705           if (0 != pthread_mutex_lock (&chat_mutex))
1706           {
1707             return NULL;
1708           }
1709           /* check whether there are still pending messages */
1710           all_messages_read = (cu->next_message_index < message_count) ? 0 : 1;
1711         }
1712         else
1713         {
1714           all_messages_read = 1;
1715         }
1716       }
1717       /* clear old messages */
1718       chat_clearmessages (0);
1719 
1720       /* Wait for wake up. */
1721       /* This will automatically unlock the mutex while waiting and */
1722       /* lock the mutex after waiting */
1723       pthread_cond_wait (&cu->wake_up_sender, &chat_mutex);
1724     }
1725   }
1726 
1727   return NULL;
1728 }
1729 
1730 
1731 /**
1732  * Receives messages from the TCP/IP socket and
1733  * initializes the connected user.
1734  *
1735  * @param cls The connected user
1736  * @return    Always NULL
1737  */
1738 static void *
connecteduser_receive_messages(void * cls)1739 connecteduser_receive_messages (void *cls)
1740 {
1741   struct ConnectedUser *cu = cls;
1742   char buf[128];
1743   ssize_t got;
1744   int result;
1745 
1746   /* make the socket blocking */
1747   make_blocking (cu->fd);
1748 
1749   /* generate the user name */
1750   {
1751     char user_name[32];
1752     strcpy (user_name, "User");
1753     snprintf (user_name + 4, 28, "%d", (int) cu->user_id);
1754     cu->user_name_len = strlen (user_name);
1755     cu->user_name = malloc (cu->user_name_len + 1);
1756     if (NULL == cu->user_name)
1757     {
1758       free (cu->extra_in);
1759       free (cu);
1760       MHD_upgrade_action (cu->urh,
1761                           MHD_UPGRADE_ACTION_CLOSE);
1762       return NULL;
1763     }
1764     strcpy (cu->user_name, user_name);
1765   }
1766 
1767   /* initialize the wake-up-sender condition variable */
1768   if (0 != pthread_cond_init (&cu->wake_up_sender, NULL))
1769   {
1770     MHD_upgrade_action (cu->urh,
1771                         MHD_UPGRADE_ACTION_CLOSE);
1772     free (cu->user_name);
1773     free (cu->extra_in);
1774     free (cu);
1775     return NULL;
1776   }
1777 
1778   /* initialize the send mutex */
1779   if (0 != pthread_mutex_init (&cu->send_mutex, NULL))
1780   {
1781     MHD_upgrade_action (cu->urh,
1782                         MHD_UPGRADE_ACTION_CLOSE);
1783     pthread_cond_destroy (&cu->wake_up_sender);
1784     free (cu->user_name);
1785     free (cu->extra_in);
1786     free (cu);
1787     return NULL;
1788   }
1789 
1790   /* add the user to the chat user list */
1791   chat_adduser (cu);
1792 
1793   /* initialize the web socket stream for encoding/decoding */
1794   result = MHD_websocket_stream_init (&cu->ws,
1795                                       MHD_WEBSOCKET_FLAG_SERVER
1796                                       | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
1797                                       0);
1798   if (MHD_WEBSOCKET_STATUS_OK != result)
1799   {
1800     chat_removeuser (cu);
1801     pthread_cond_destroy (&cu->wake_up_sender);
1802     pthread_mutex_destroy (&cu->send_mutex);
1803     MHD_upgrade_action (cu->urh,
1804                         MHD_UPGRADE_ACTION_CLOSE);
1805     free (cu->user_name);
1806     free (cu->extra_in);
1807     free (cu);
1808     return NULL;
1809   }
1810 
1811   /* send a list of all currently connected users (bypassing the messaging system) */
1812   {
1813     struct UserInit
1814     {
1815       char *user_init;
1816       size_t user_init_len;
1817     };
1818     struct UserInit *init_users = NULL;
1819     size_t init_users_len = 0;
1820 
1821     /* first collect all users without sending (so the mutex isn't locked too long) */
1822     if (0 == pthread_mutex_lock (&chat_mutex))
1823     {
1824       if (0 < user_count)
1825       {
1826         init_users = (struct UserInit *) malloc (user_count * sizeof (struct
1827                                                                       UserInit));
1828         if (NULL != init_users)
1829         {
1830           init_users_len = user_count;
1831           for (size_t i = 0; i < user_count; ++i)
1832           {
1833             char user_index[32];
1834             snprintf (user_index, 32, "%d", (int) users[i]->user_id);
1835             size_t user_index_len = strlen (user_index);
1836             struct UserInit iu;
1837             iu.user_init_len = user_index_len + users[i]->user_name_len + 10;
1838             iu.user_init = (char *) malloc (iu.user_init_len + 1);
1839             if (NULL != iu.user_init)
1840             {
1841               strcpy (iu.user_init, "userinit|");
1842               strcat (iu.user_init, user_index);
1843               strcat (iu.user_init, "|");
1844               if (0 < users[i]->user_name_len)
1845                 strcat (iu.user_init, users[i]->user_name);
1846             }
1847             init_users[i] = iu;
1848           }
1849         }
1850       }
1851       pthread_mutex_unlock (&chat_mutex);
1852     }
1853 
1854     /* then send all users to the connected client */
1855     for (size_t i = 0; i < init_users_len; ++i)
1856     {
1857       char *frame_data = NULL;
1858       size_t frame_len = 0;
1859       if ((0 < init_users[i].user_init_len) && (NULL !=
1860                                                 init_users[i].user_init) )
1861       {
1862         int status = MHD_websocket_encode_text (cu->ws,
1863                                                 init_users[i].user_init,
1864                                                 init_users[i].user_init_len,
1865                                                 MHD_WEBSOCKET_FRAGMENTATION_NONE,
1866                                                 &frame_data,
1867                                                 &frame_len,
1868                                                 NULL);
1869         if (MHD_WEBSOCKET_STATUS_OK == status)
1870         {
1871           send_all (cu,
1872                     frame_data,
1873                     frame_len);
1874           MHD_websocket_free (cu->ws,
1875                               frame_data);
1876         }
1877         free (init_users[i].user_init);
1878       }
1879     }
1880     free (init_users);
1881   }
1882 
1883   /* send the welcome message to the user (bypassing the messaging system) */
1884   {
1885     char *frame_data = NULL;
1886     size_t frame_len = 0;
1887     const char *welcome_msg = "moderator||" \
1888                               "Welcome to the libmicrohttpd WebSocket chatserver example.\n" \
1889                               "Supported commands are:\n" \
1890                               "  /m <user> <text> - sends a private message to the specified user\n" \
1891                               "  /ping <user> - sends a ping to the specified user\n" \
1892                               "  /name <name> - changes your name to the specified name\n" \
1893                               "  /disconnect - disconnects your websocket\n\n" \
1894                               "All messages, which does not start with a slash, " \
1895                               "are regular messages and will be sent to the selected user.\n\n" \
1896                               "Have fun!";
1897     MHD_websocket_encode_text (cu->ws,
1898                                welcome_msg,
1899                                strlen (welcome_msg),
1900                                MHD_WEBSOCKET_FRAGMENTATION_NONE,
1901                                &frame_data,
1902                                &frame_len,
1903                                NULL);
1904     send_all (cu,
1905               frame_data,
1906               frame_len);
1907     MHD_websocket_free (cu->ws,
1908                         frame_data);
1909   }
1910 
1911   /* start the message-send thread */
1912   pthread_t pt;
1913   if (0 != pthread_create (&pt,
1914                            NULL,
1915                            &connecteduser_send_messages,
1916                            cu))
1917     abort ();
1918 
1919   /* start by parsing extra data MHD may have already read, if any */
1920   if (0 != cu->extra_in_size)
1921   {
1922     if (0 != connecteduser_parse_received_websocket_stream (cu,
1923                                                             cu->extra_in,
1924                                                             cu->extra_in_size))
1925     {
1926       chat_removeuser (cu);
1927       if (0 == pthread_mutex_lock (&chat_mutex))
1928       {
1929         cu->disconnect = 1;
1930         pthread_cond_signal (&cu->wake_up_sender);
1931         pthread_mutex_unlock (&chat_mutex);
1932         pthread_join (pt, NULL);
1933       }
1934       struct MHD_UpgradeResponseHandle *urh = cu->urh;
1935       if (NULL != urh)
1936       {
1937         cu->urh = NULL;
1938         MHD_upgrade_action (urh,
1939                             MHD_UPGRADE_ACTION_CLOSE);
1940       }
1941       pthread_cond_destroy (&cu->wake_up_sender);
1942       pthread_mutex_destroy (&cu->send_mutex);
1943       MHD_websocket_stream_free (cu->ws);
1944       free (cu->user_name);
1945       free (cu->extra_in);
1946       free (cu);
1947       return NULL;
1948     }
1949     free (cu->extra_in);
1950     cu->extra_in = NULL;
1951   }
1952 
1953   /* the main loop for receiving data */
1954   while (1)
1955   {
1956     got = recv (cu->fd,
1957                 buf,
1958                 sizeof (buf),
1959                 0);
1960     if (0 >= got)
1961     {
1962       /* the TCP/IP socket has been closed */
1963       break;
1964     }
1965     if (0 < got)
1966     {
1967       if (0 != connecteduser_parse_received_websocket_stream (cu, buf,
1968                                                               (size_t) got))
1969       {
1970         /* A websocket protocol error occurred */
1971         chat_removeuser (cu);
1972         if (0 == pthread_mutex_lock (&chat_mutex))
1973         {
1974           cu->disconnect = 1;
1975           pthread_cond_signal (&cu->wake_up_sender);
1976           pthread_mutex_unlock (&chat_mutex);
1977           pthread_join (pt, NULL);
1978         }
1979         struct MHD_UpgradeResponseHandle *urh = cu->urh;
1980         if (NULL != urh)
1981         {
1982           cu->urh = NULL;
1983           MHD_upgrade_action (urh,
1984                               MHD_UPGRADE_ACTION_CLOSE);
1985         }
1986         pthread_cond_destroy (&cu->wake_up_sender);
1987         pthread_mutex_destroy (&cu->send_mutex);
1988         MHD_websocket_stream_free (cu->ws);
1989         free (cu->user_name);
1990         free (cu);
1991         return NULL;
1992       }
1993     }
1994   }
1995 
1996   /* cleanup */
1997   chat_removeuser (cu);
1998   if (0 == pthread_mutex_lock (&chat_mutex))
1999   {
2000     cu->disconnect = 1;
2001     pthread_cond_signal (&cu->wake_up_sender);
2002     pthread_mutex_unlock (&chat_mutex);
2003     pthread_join (pt, NULL);
2004   }
2005   struct MHD_UpgradeResponseHandle *urh = cu->urh;
2006   if (NULL != urh)
2007   {
2008     cu->urh = NULL;
2009     MHD_upgrade_action (urh,
2010                         MHD_UPGRADE_ACTION_CLOSE);
2011   }
2012   pthread_cond_destroy (&cu->wake_up_sender);
2013   pthread_mutex_destroy (&cu->send_mutex);
2014   MHD_websocket_stream_free (cu->ws);
2015   free (cu->user_name);
2016   free (cu);
2017 
2018   return NULL;
2019 }
2020 
2021 
2022 /**
2023  * Function called after a protocol "upgrade" response was sent
2024  * successfully and the socket should now be controlled by some
2025  * protocol other than HTTP.
2026  *
2027  * Any data already received on the socket will be made available in
2028  * @e extra_in.  This can happen if the application sent extra data
2029  * before MHD send the upgrade response.  The application should
2030  * treat data from @a extra_in as if it had read it from the socket.
2031  *
2032  * Note that the application must not close() @a sock directly,
2033  * but instead use #MHD_upgrade_action() for special operations
2034  * on @a sock.
2035  *
2036  * Data forwarding to "upgraded" @a sock will be started as soon
2037  * as this function return.
2038  *
2039  * Except when in 'thread-per-connection' mode, implementations
2040  * of this function should never block (as it will still be called
2041  * from within the main event loop).
2042  *
2043  * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
2044  * @param connection original HTTP connection handle,
2045  *                   giving the function a last chance
2046  *                   to inspect the original HTTP request
2047  * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
2048  * @param extra_in if we happened to have read bytes after the
2049  *                 HTTP header already (because the client sent
2050  *                 more than the HTTP header of the request before
2051  *                 we sent the upgrade response),
2052  *                 these are the extra bytes already read from @a sock
2053  *                 by MHD.  The application should treat these as if
2054  *                 it had read them from @a sock.
2055  * @param extra_in_size number of bytes in @a extra_in
2056  * @param sock socket to use for bi-directional communication
2057  *        with the client.  For HTTPS, this may not be a socket
2058  *        that is directly connected to the client and thus certain
2059  *        operations (TCP-specific setsockopt(), getsockopt(), etc.)
2060  *        may not work as expected (as the socket could be from a
2061  *        socketpair() or a TCP-loopback).  The application is expected
2062  *        to perform read()/recv() and write()/send() calls on the socket.
2063  *        The application may also call shutdown(), but must not call
2064  *        close() directly.
2065  * @param urh argument for #MHD_upgrade_action()s on this @a connection.
2066  *        Applications must eventually use this callback to (indirectly)
2067  *        perform the close() action on the @a sock.
2068  */
2069 static void
upgrade_handler(void * cls,struct MHD_Connection * connection,void * con_cls,const char * extra_in,size_t extra_in_size,MHD_socket fd,struct MHD_UpgradeResponseHandle * urh)2070 upgrade_handler (void *cls,
2071                  struct MHD_Connection *connection,
2072                  void *con_cls,
2073                  const char *extra_in,
2074                  size_t extra_in_size,
2075                  MHD_socket fd,
2076                  struct MHD_UpgradeResponseHandle *urh)
2077 {
2078   struct ConnectedUser *cu;
2079   pthread_t pt;
2080   (void) cls;         /* Unused. Silent compiler warning. */
2081   (void) connection;  /* Unused. Silent compiler warning. */
2082   (void) con_cls;     /* Unused. Silent compiler warning. */
2083 
2084   /* This callback must return as soon as possible. */
2085 
2086   /* allocate new connected user */
2087   cu = malloc (sizeof (struct ConnectedUser));
2088   if (NULL == cu)
2089     abort ();
2090   memset (cu, 0, sizeof (struct ConnectedUser));
2091   if (0 != extra_in_size)
2092   {
2093     cu->extra_in = malloc (extra_in_size);
2094     if (NULL == cu->extra_in)
2095       abort ();
2096     memcpy (cu->extra_in,
2097             extra_in,
2098             extra_in_size);
2099   }
2100   cu->extra_in_size = extra_in_size;
2101   cu->fd = fd;
2102   cu->urh = urh;
2103   cu->user_id = ++unique_user_id;
2104   cu->user_name = NULL;
2105   cu->user_name_len = 0;
2106 
2107   /* create thread for the new connected user */
2108   if (0 != pthread_create (&pt,
2109                            NULL,
2110                            &connecteduser_receive_messages,
2111                            cu))
2112     abort ();
2113   pthread_detach (pt);
2114 }
2115 
2116 
2117 /**
2118  * Function called by the MHD_daemon when the client tries to access a page.
2119  *
2120  * This is used to provide the main page
2121  * (in this example HTML + CSS + JavaScript is all in the same file)
2122  * and to initialize a websocket connection.
2123  * The rules for the initialization of a websocket connection
2124  * are listed near the URL check of "/ChatServerWebSocket".
2125  *
2126  * @param cls closure, whatever was given to #MHD_start_daemon().
2127  * @param connection The HTTP connection handle
2128  * @param url The requested URL
2129  * @param method The request method (typically "GET")
2130  * @param version The HTTP version
2131  * @param upload_data Given upload data for POST requests
2132  * @param upload_data_size The size of the upload data
2133  * @param ptr A pointer for request specific data
2134  * @return MHD_YES on success or MHD_NO on error.
2135  */
2136 static enum MHD_Result
access_handler(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)2137 access_handler (void *cls,
2138                 struct MHD_Connection *connection,
2139                 const char *url,
2140                 const char *method,
2141                 const char *version,
2142                 const char *upload_data,
2143                 size_t *upload_data_size,
2144                 void **ptr)
2145 {
2146   static int aptr;
2147   struct MHD_Response *response;
2148   int ret;
2149   (void) cls;               /* Unused. Silent compiler warning. */
2150   (void) version;           /* Unused. Silent compiler warning. */
2151   (void) upload_data;       /* Unused. Silent compiler warning. */
2152   (void) upload_data_size;  /* Unused. Silent compiler warning. */
2153 
2154   if (0 != strcmp (method, "GET"))
2155     return MHD_NO;              /* unexpected method */
2156   if (&aptr != *ptr)
2157   {
2158     /* do never respond on first call */
2159     *ptr = &aptr;
2160     return MHD_YES;
2161   }
2162   *ptr = NULL;                  /* reset when done */
2163   if (0 == strcmp (url, "/"))
2164   {
2165     /* Default page for visiting the server */
2166     struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
2167                                                                        PAGE),
2168                                                                      PAGE,
2169                                                                      MHD_RESPMEM_PERSISTENT);
2170     ret = MHD_queue_response (connection,
2171                               MHD_HTTP_OK,
2172                               response);
2173     MHD_destroy_response (response);
2174   }
2175   else if (0 == strcmp (url, "/ChatServerWebSocket"))
2176   {
2177     /**
2178      * The path for the chat has been accessed.
2179      * For a valid WebSocket request, at least five headers are required:
2180      * 1. "Host: <name>"
2181      * 2. "Connection: Upgrade"
2182      * 3. "Upgrade: websocket"
2183      * 4. "Sec-WebSocket-Version: 13"
2184      * 5. "Sec-WebSocket-Key: <base64 encoded value>"
2185      * Values are compared in a case-insensitive manner.
2186      * Furthermore it must be a HTTP/1.1 or higher GET request.
2187      * See: https://tools.ietf.org/html/rfc6455#section-4.2.1
2188      *
2189      * To make this example portable we skip the Host check
2190      */
2191 
2192     char is_valid = 1;
2193     const char *value = NULL;
2194     char sec_websocket_accept[29];
2195 
2196     /* check whether an websocket upgrade is requested */
2197     if (0 != MHD_websocket_check_http_version (version))
2198     {
2199       is_valid = 0;
2200     }
2201     value = MHD_lookup_connection_value (connection,
2202                                          MHD_HEADER_KIND,
2203                                          MHD_HTTP_HEADER_CONNECTION);
2204     if (0 != MHD_websocket_check_connection_header (value))
2205     {
2206       is_valid = 0;
2207     }
2208     value = MHD_lookup_connection_value (connection,
2209                                          MHD_HEADER_KIND,
2210                                          MHD_HTTP_HEADER_UPGRADE);
2211     if (0 != MHD_websocket_check_upgrade_header (value))
2212     {
2213       is_valid = 0;
2214     }
2215     value = MHD_lookup_connection_value (connection,
2216                                          MHD_HEADER_KIND,
2217                                          MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
2218     if (0 != MHD_websocket_check_version_header (value))
2219     {
2220       is_valid = 0;
2221     }
2222     value = MHD_lookup_connection_value (connection,
2223                                          MHD_HEADER_KIND,
2224                                          MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
2225     if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
2226     {
2227       is_valid = 0;
2228     }
2229 
2230     if (1 == is_valid)
2231     {
2232       /* create the response for upgrade */
2233       response = MHD_create_response_for_upgrade (&upgrade_handler,
2234                                                   NULL);
2235 
2236       /**
2237        * For the response we need at least the following headers:
2238        * 1. "Connection: Upgrade"
2239        * 2. "Upgrade: websocket"
2240        * 3. "Sec-WebSocket-Accept: <base64value>"
2241        * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept_header.
2242        * It requires the value of the Sec-WebSocket-Key header of the request.
2243        * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
2244        */
2245       MHD_add_response_header (response,
2246                                MHD_HTTP_HEADER_CONNECTION,
2247                                "Upgrade");
2248       MHD_add_response_header (response,
2249                                MHD_HTTP_HEADER_UPGRADE,
2250                                "websocket");
2251       MHD_add_response_header (response,
2252                                MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
2253                                sec_websocket_accept);
2254       ret = MHD_queue_response (connection,
2255                                 MHD_HTTP_SWITCHING_PROTOCOLS,
2256                                 response);
2257       MHD_destroy_response (response);
2258     }
2259     else
2260     {
2261       /* return error page */
2262       struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
2263                                                                          PAGE_INVALID_WEBSOCKET_REQUEST),
2264                                                                        PAGE_INVALID_WEBSOCKET_REQUEST,
2265                                                                        MHD_RESPMEM_PERSISTENT);
2266       ret = MHD_queue_response (connection,
2267                                 MHD_HTTP_BAD_REQUEST,
2268                                 response);
2269       MHD_destroy_response (response);
2270     }
2271   }
2272   else
2273   {
2274     struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
2275                                                                        PAGE_NOT_FOUND),
2276                                                                      PAGE_NOT_FOUND,
2277                                                                      MHD_RESPMEM_PERSISTENT);
2278     ret = MHD_queue_response (connection,
2279                               MHD_HTTP_NOT_FOUND,
2280                               response);
2281     MHD_destroy_response (response);
2282   }
2283   return ret;
2284 }
2285 
2286 
2287 /**
2288  * The main routine for this example
2289  *
2290  * This starts the daemon and waits for a key hit.
2291  * After this it will shutdown the daemon.
2292  */
2293 int
main(int argc,char * const * argv)2294 main (int argc,
2295       char *const *argv)
2296 {
2297   (void) argc;               /* Unused. Silent compiler warning. */
2298   (void) argv;               /* Unused. Silent compiler warning. */
2299   struct MHD_Daemon *d;
2300 
2301   if (0 != pthread_mutex_init (&chat_mutex, NULL))
2302     return 1;
2303 
2304 #if USE_HTTPS == 1
2305   const char private_key[] = "TODO: Enter your key in PEM format here";
2306   const char certificate[] = "TODO: Enter your certificate in PEM format here";
2307   d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO
2308                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
2309                         | MHD_USE_TLS,
2310                         443,
2311                         NULL, NULL,
2312                         &access_handler, NULL,
2313                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
2314                         MHD_OPTION_HTTPS_MEM_KEY, private_key,
2315                         MHD_OPTION_HTTPS_MEM_CERT, certificate,
2316                         MHD_OPTION_END);
2317 #else
2318   d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO
2319                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
2320                         80,
2321                         NULL, NULL,
2322                         &access_handler, NULL,
2323                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
2324                         MHD_OPTION_END);
2325 #endif
2326 
2327   if (NULL == d)
2328     return 1;
2329   (void) getc (stdin);
2330 
2331   if (0 == pthread_mutex_lock (&chat_mutex))
2332   {
2333     disconnect_all = 1;
2334     for (size_t i = 0; i < user_count; ++i)
2335       pthread_cond_signal (&users[i]->wake_up_sender);
2336     pthread_mutex_unlock (&chat_mutex);
2337   }
2338   sleep (2);
2339   if (0 == pthread_mutex_lock (&chat_mutex))
2340   {
2341     for (size_t i = 0; i < user_count; ++i)
2342     {
2343       struct MHD_UpgradeResponseHandle *urh = users[i]->urh;
2344       if (NULL != urh)
2345       {
2346         users[i]->urh = NULL;
2347         MHD_upgrade_action (users[i]->urh,
2348                             MHD_UPGRADE_ACTION_CLOSE);
2349       }
2350     }
2351     pthread_mutex_unlock (&chat_mutex);
2352   }
2353   sleep (2);
2354 
2355   /* usually we should wait here in a safe way for all threads to disconnect, */
2356   /* but we skip this in the example */
2357 
2358   pthread_mutex_destroy (&chat_mutex);
2359 
2360   MHD_stop_daemon (d);
2361 
2362   return 0;
2363 }
2364