aboutsummaryrefslogtreecommitdiff
path: root/src/examples/websocket_chatserver_example.c
diff options
context:
space:
mode:
authorDavid Gausmann <David.Gausmann@measX.com>2021-10-17 19:53:09 +0200
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-31 16:02:31 +0300
commitddad59c1e3a5d20b19003b1d47673501b0665b36 (patch)
tree8cd367e724477bc092d7dd38c83cd15f4c12c95f /src/examples/websocket_chatserver_example.c
parentffbb000f890f23e14d972a6660168aff4a97b66c (diff)
downloadlibmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.tar.gz
libmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.zip
websocket update
- added API documentation to libmicrohttpd.texi - added websocket tutorial chapter to libmicrohttpd-tutorial and an much easier example for the tutorial - added additional helper functions to ease the HTTP websocket handshake - the code can now be compiled on Linux without errors - changed sha1.c and sha1.h to the files provided by Evgeny (I replaced those files in src/microhttpd_ws/ with the files from src/microhttpd/ - maybe there is a smarter way...?) - removed dependency for "htons" and "htonl" (these functions are now implemented in MHD_websocket.c; no need for OS-dependent files anymore) - added an additional test script for testing of the library with any webbrowser (for manual practice test) - several bugfixes - parameters renamed - special things clarified (fragmentation, RNG for client mode) The new version of the API is at some points incompatible with the old version, but since it was in an experimental phase and it didn't compile on Linux, I guess this shouldn't bother anyone. From my point of view, I am now finished with the library and it could go out of experimental.
Diffstat (limited to 'src/examples/websocket_chatserver_example.c')
-rw-r--r--src/examples/websocket_chatserver_example.c1051
1 files changed, 510 insertions, 541 deletions
diff --git a/src/examples/websocket_chatserver_example.c b/src/examples/websocket_chatserver_example.c
index 701684cc..0893279b 100644
--- a/src/examples/websocket_chatserver_example.c
+++ b/src/examples/websocket_chatserver_example.c
@@ -43,6 +43,12 @@
43 See: https://github.com/coapp-packages/pthreads/issues/2 43 See: https://github.com/coapp-packages/pthreads/issues/2
44*/ 44*/
45#include "pthread_windows.h" 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
46#else 52#else
47/* 53/*
48 On Unix systems we can use pthread. 54 On Unix systems we can use pthread.
@@ -180,429 +186,429 @@
180 " function window_onload(event)\n" \ 186 " function window_onload(event)\n" \
181 " {\n" \ 187 " {\n" \
182 " // Determine the base url (for http:// this is ws:// for https:// this must be wss://)\n" \ 188 " // Determine the base url (for http:// this is ws:// for https:// this must be wss://)\n" \
183 // " baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/ChatServerWebSocket';\n" \ 189 " baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/ChatServerWebSocket';\n" \
184 // " chat_generate();\n" \ 190 " chat_generate();\n" \
185 // " chat_connect();\n" \ 191 " chat_connect();\n" \
186 // " }\n" \ 192 " }\n" \
187 // "\n" \ 193 "\n" \
188 // " /**\n" \ 194 " /**\n" \
189 // " This function generates the chat using DOM\n" \ 195 " This function generates the chat using DOM\n" \
190 // " */\n" \ 196 " */\n" \
191 // " function chat_generate()\n" \ 197 " function chat_generate()\n" \
192 // " {\n" \ 198 " {\n" \
193 // " document.body.innerHTML = '';\n" \ 199 " document.body.innerHTML = '';\n" \
194 // " let chat = document.createElement('div');\n" \ 200 " let chat = document.createElement('div');\n" \
195 // " document.body.appendChild(chat);\n" \ 201 " document.body.appendChild(chat);\n" \
196 // " chat.id = 'Chat';\n" \ 202 " chat.id = 'Chat';\n" \
197 // " let messagesAndInput = document.createElement('div');\n" \ 203 " let messagesAndInput = document.createElement('div');\n" \
198 // " chat.appendChild(messagesAndInput);\n" \ 204 " chat.appendChild(messagesAndInput);\n" \
199 // " messagesAndInput.classList.add('MessagesAndInput');\n" \ 205 " messagesAndInput.classList.add('MessagesAndInput');\n" \
200 // " let messages = document.createElement('div');\n" \ 206 " let messages = document.createElement('div');\n" \
201 // " messagesAndInput.appendChild(messages);\n" \ 207 " messagesAndInput.appendChild(messages);\n" \
202 // " messages.id = 'Messages';\n" \ 208 " messages.id = 'Messages';\n" \
203 // " let input = document.createElement('div');\n" \ 209 " let input = document.createElement('div');\n" \
204 // " messagesAndInput.appendChild(input);\n" \ 210 " messagesAndInput.appendChild(input);\n" \
205 // " input.classList.add('Input');\n" \ 211 " input.classList.add('Input');\n" \
206 // " let inputMessage = document.createElement('input');\n" \ 212 " let inputMessage = document.createElement('input');\n" \
207 // " input.appendChild(inputMessage);\n" \ 213 " input.appendChild(inputMessage);\n" \
208 // " inputMessage.type = 'text';\n" \ 214 " inputMessage.type = 'text';\n" \
209 // " inputMessage.id = 'InputMessage';\n" \ 215 " inputMessage.id = 'InputMessage';\n" \
210 // " inputMessage.disabled = true;\n" \ 216 " inputMessage.disabled = true;\n" \
211 // " inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \ 217 " inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
212 // " let inputMessageSend = document.createElement('button');\n" \ 218 " let inputMessageSend = document.createElement('button');\n" \
213 // " input.appendChild(inputMessageSend);\n" \ 219 " input.appendChild(inputMessageSend);\n" \
214 // " inputMessageSend.id = 'InputMessageButton';\n" \ 220 " inputMessageSend.id = 'InputMessageButton';\n" \
215 // " inputMessageSend.disabled = true;\n" \ 221 " inputMessageSend.disabled = true;\n" \
216 // " inputMessageSend.innerText = 'send';\n" \ 222 " inputMessageSend.innerText = 'send';\n" \
217 // " inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \ 223 " inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \
218 // " let inputImage = document.createElement('input');\n" \ 224 " let inputImage = document.createElement('input');\n" \
219 // " input.appendChild(inputImage);\n" \ 225 " input.appendChild(inputImage);\n" \
220 // " inputImage.id = 'InputImage';\n" \ 226 " inputImage.id = 'InputImage';\n" \
221 // " inputImage.type = 'file';\n" \ 227 " inputImage.type = 'file';\n" \
222 // " inputImage.accept = 'image/*';\n" \ 228 " inputImage.accept = 'image/*';\n" \
223 // " inputImage.style.display = 'none';\n" \ 229 " inputImage.style.display = 'none';\n" \
224 // " inputImage.addEventListener('change', chat_onImageSelected);\n" \ 230 " inputImage.addEventListener('change', chat_onImageSelected);\n" \
225 // " let inputImageButton = document.createElement('button');\n" \ 231 " let inputImageButton = document.createElement('button');\n" \
226 // " input.appendChild(inputImageButton);\n" \ 232 " input.appendChild(inputImageButton);\n" \
227 // " inputImageButton.id = 'InputImageButton';\n" \ 233 " inputImageButton.id = 'InputImageButton';\n" \
228 // " inputImageButton.disabled = true;\n" \ 234 " inputImageButton.disabled = true;\n" \
229 // " inputImageButton.innerText = 'image';\n" \ 235 " inputImageButton.innerText = 'image';\n" \
230 // " inputImageButton.addEventListener('click', chat_onImageClicked);\n" \ 236 " inputImageButton.addEventListener('click', chat_onImageClicked);\n" \
231 // " let users = document.createElement('div');\n" \ 237 " let users = document.createElement('div');\n" \
232 // " chat.appendChild(users);\n" \ 238 " chat.appendChild(users);\n" \
233 // " users.id = 'Users';\n" \ 239 " users.id = 'Users';\n" \
234 // " users.addEventListener('click', chat_onUserClicked);\n" \ 240 " users.addEventListener('click', chat_onUserClicked);\n" \
235 // " let allUsers = document.createElement('div');\n" \ 241 " let allUsers = document.createElement('div');\n" \
236 // " users.appendChild(allUsers);\n" \ 242 " users.appendChild(allUsers);\n" \
237 // " allUsers.classList.add('selected');\n" \ 243 " allUsers.classList.add('selected');\n" \
238 // " allUsers.innerText = '<everyone>';\n" \ 244 " allUsers.innerText = '<everyone>';\n" \
239 // " allUsers.setAttribute('data-user', '0');\n" \ 245 " allUsers.setAttribute('data-user', '0');\n" \
240 // " }\n" \ 246 " }\n" \
241 // "\n" \ 247 "\n" \
242 // " /**\n" \ 248 " /**\n" \
243 // " This function creates and connects a WebSocket\n" \ 249 " This function creates and connects a WebSocket\n" \
244 // " */\n" \ 250 " */\n" \
245 // " function chat_connect()\n" \ 251 " function chat_connect()\n" \
246 // " {\n" \ 252 " {\n" \
247 // " chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \ 253 " chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \
248 // " socket = new WebSocket(baseUrl);\n" \ 254 " socket = new WebSocket(baseUrl);\n" \
249 // " socket.binaryType = 'arraybuffer';\n" \ 255 " socket.binaryType = 'arraybuffer';\n" \
250 // " socket.onopen = socket_onopen;\n" \ 256 " socket.onopen = socket_onopen;\n" \
251 // " socket.onclose = socket_onclose;\n" \ 257 " socket.onclose = socket_onclose;\n" \
252 // " socket.onerror = socket_onerror;\n" \ 258 " socket.onerror = socket_onerror;\n" \
253 // " socket.onmessage = socket_onmessage;\n" \ 259 " socket.onmessage = socket_onmessage;\n" \
254 // " }\n" \ 260 " }\n" \
255 // "\n" \ 261 "\n" \
256 // " /**\n" \ 262 " /**\n" \
257 // " This function adds new text to the chat list\n" \ 263 " This function adds new text to the chat list\n" \
258 // " */\n" \ 264 " */\n" \
259 // " function chat_addMessage(text, options)\n" \ 265 " function chat_addMessage(text, options)\n" \
260 // " {\n" \ 266 " {\n" \
261 // " let type = options && options.type || 'regular';\n" \ 267 " let type = options && options.type || 'regular';\n" \
262 // " if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \ 268 " if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
263 // " type = 'regular';\n" \ 269 " type = 'regular';\n" \
264 // " let message = document.createElement('div');\n" \ 270 " let message = document.createElement('div');\n" \
265 // " message.classList.add('Message');\n" \ 271 " message.classList.add('Message');\n" \
266 // " message.classList.add(type);\n" \ 272 " message.classList.add(type);\n" \
267 // " if(typeof(text) === 'string')\n" \ 273 " if(typeof(text) === 'string')\n" \
268 // " {\n" \ 274 " {\n" \
269 // " let content = document.createElement('span');\n" \ 275 " let content = document.createElement('span');\n" \
270 // " message.appendChild(content);\n" \ 276 " message.appendChild(content);\n" \
271 // " if(options && options.from)\n" \ 277 " if(options && options.from)\n" \
272 // " content.innerText = `${options.from}: ${text}`;\n" \ 278 " content.innerText = `${options.from}: ${text}`;\n" \
273 // " else\n" \ 279 " else\n" \
274 // " content.innerText = text;\n" \ 280 " content.innerText = text;\n" \
275 // " if(options && options.reconnect)\n" \ 281 " if(options && options.reconnect)\n" \
276 // " {\n" \ 282 " {\n" \
277 // " let span = document.createElement('span');\n" \ 283 " let span = document.createElement('span');\n" \
278 // " span.appendChild(document.createTextNode(' ('));\n" \ 284 " span.appendChild(document.createTextNode(' ('));\n" \
279 // " let reconnect = document.createElement('a');\n" \ 285 " let reconnect = document.createElement('a');\n" \
280 // " reconnect.href = 'javascript:chat_connect()';\n" \ 286 " reconnect.href = 'javascript:chat_connect()';\n" \
281 // " reconnect.innerText = 'reconnect';\n" \ 287 " reconnect.innerText = 'reconnect';\n" \
282 // " span.appendChild(reconnect);\n" \ 288 " span.appendChild(reconnect);\n" \
283 // " span.appendChild(document.createTextNode(')'));\n" \ 289 " span.appendChild(document.createTextNode(')'));\n" \
284 // " message.appendChild(span);\n" \ 290 " message.appendChild(span);\n" \
285 // " }\n" \ 291 " }\n" \
286 // " }\n" \ 292 " }\n" \
287 // " else\n" \ 293 " else\n" \
288 // " {\n" \ 294 " {\n" \
289 // " let content = document.createElement('span');\n" \ 295 " let content = document.createElement('span');\n" \
290 // " message.appendChild(content);\n" \ 296 " message.appendChild(content);\n" \
291 // " if(options && options.from)\n" \ 297 " if(options && options.from)\n" \
292 // " {\n" \ 298 " {\n" \
293 // " content.innerText = `${options.from}:\\n`;\n" \ 299 " content.innerText = `${options.from}:\\n`;\n" \
294 // " }\n" \ 300 " }\n" \
295 // " if(options && options.pictureType && text instanceof Uint8Array)\n" \ 301 " if(options && options.pictureType && text instanceof Uint8Array)\n" \
296 // " {\n" \ 302 " {\n" \
297 // " let img = document.createElement('img');\n" \ 303 " let img = document.createElement('img');\n" \
298 // " content.appendChild(img);\n" \ 304 " content.appendChild(img);\n" \
299 // " img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \ 305 " img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \
300 // " }\n" \ 306 " }\n" \
301 // " }\n" \ 307 " }\n" \
302 // " document.getElementById('Messages').appendChild(message);\n" \ 308 " document.getElementById('Messages').appendChild(message);\n" \
303 // " message.scrollIntoView();\n" \ 309 " message.scrollIntoView();\n" \
304 // " }\n" \ 310 " }\n" \
305 // "\n" \ 311 "\n" \
306 // " /**\n" \ 312 " /**\n" \
307 // " This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \ 313 " This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \
308 // " */\n" \ 314 " */\n" \
309 // " function chat_onKeyDown(event)\n" \ 315 " function chat_onKeyDown(event)\n" \
310 // " {\n" \ 316 " {\n" \
311 // " if(event.key == 'Enter')\n" \ 317 " if(event.key == 'Enter')\n" \
312 // " chat_onSendClicked();\n" \ 318 " chat_onSendClicked();\n" \
313 // " }\n" \ 319 " }\n" \
314 // "\n" \ 320 "\n" \
315 // " /**\n" \ 321 " /**\n" \
316 // " This is the code to send a message or command, when clicking the 'send' button\n" \ 322 " This is the code to send a message or command, when clicking the 'send' button\n" \
317 // " */\n" \ 323 " */\n" \
318 // " function chat_onSendClicked(event)\n" \ 324 " function chat_onSendClicked(event)\n" \
319 // " {\n" \ 325 " {\n" \
320 // " let message = document.getElementById('InputMessage').value;\n" \ 326 " let message = document.getElementById('InputMessage').value;\n" \
321 // " if(message.length == 0)\n" \ 327 " if(message.length == 0)\n" \
322 // " return;\n" \ 328 " return;\n" \
323 // " if(message.substr(0, 1) == '/')\n" \ 329 " if(message.substr(0, 1) == '/')\n" \
324 // " {\n" \ 330 " {\n" \
325 // " // command\n" \ 331 " // command\n" \
326 // " let match;\n" \ 332 " let match;\n" \
327 // " if(/^\\/disconnect\\s*$/.test(message))\n" \ 333 " if(/^\\/disconnect\\s*$/.test(message))\n" \
328 // " {\n" \ 334 " {\n" \
329 // " socket.close(1000);\n" \ 335 " socket.close(1000);\n" \
330 // " }\n" \ 336 " }\n" \
331 // " else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \ 337 " else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
332 // " {\n" \ 338 " {\n" \
333 // " message = message.substr(match[0].length);\n" \ 339 " message = message.substr(match[0].length);\n" \
334 // " let userId = chat_getUserIdByName(match[1]);\n" \ 340 " let userId = chat_getUserIdByName(match[1]);\n" \
335 // " if(userId !== null)\n" \ 341 " if(userId !== null)\n" \
336 // " {\n" \ 342 " {\n" \
337 // " socket.send(`private|${userId}|${message}`);\n" \ 343 " socket.send(`private|${userId}|${message}`);\n" \
338 // " }\n" \ 344 " }\n" \
339 // " else\n" \ 345 " else\n" \
340 // " {\n" \ 346 " {\n" \
341 // " chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \ 347 " chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \
342 // " }\n" \ 348 " }\n" \
343 // " }\n" \ 349 " }\n" \
344 // " else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \ 350 " else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
345 // " {\n" \ 351 " {\n" \
346 // " let userId = chat_getUserIdByName(match[1]);\n" \ 352 " let userId = chat_getUserIdByName(match[1]);\n" \
347 // " if(userId !== null)\n" \ 353 " if(userId !== null)\n" \
348 // " {\n" \ 354 " {\n" \
349 // " socket.send(`ping|${userId}|`);\n" \ 355 " socket.send(`ping|${userId}|`);\n" \
350 // " }\n" \ 356 " }\n" \
351 // " else\n" \ 357 " else\n" \
352 // " {\n" \ 358 " {\n" \
353 // " chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \ 359 " chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \
354 // " }\n" \ 360 " }\n" \
355 // " }\n" \ 361 " }\n" \
356 // " else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \ 362 " else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
357 // " {\n" \ 363 " {\n" \
358 // " socket.send(`name||${match[1]}`);\n" \ 364 " socket.send(`name||${match[1]}`);\n" \
359 // " }\n" \ 365 " }\n" \
360 // " else\n" \ 366 " else\n" \
361 // " {\n" \ 367 " {\n" \
362 // " chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \ 368 " chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \
363 // " }\n" \ 369 " }\n" \
364 // " }\n" \ 370 " }\n" \
365 // " else\n" \ 371 " else\n" \
366 // " {\n" \ 372 " {\n" \
367 // " // regular chat message to the selected user\n" \ 373 " // regular chat message to the selected user\n" \
368 // " let selectedUser = document.querySelector('div#Users > div.selected');\n" \ 374 " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
369 // " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \ 375 " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
370 // " if(selectedUserId == 0)\n" \ 376 " if(selectedUserId == 0)\n" \
371 // " socket.send(`||${message}`);\n" \ 377 " socket.send(`||${message}`);\n" \
372 // " else\n" \ 378 " else\n" \
373 // " socket.send(`private|${selectedUserId}|${message}`);\n" \ 379 " socket.send(`private|${selectedUserId}|${message}`);\n" \
374 // " }\n" \ 380 " }\n" \
375 // " document.getElementById('InputMessage').value = '';\n" \ 381 " document.getElementById('InputMessage').value = '';\n" \
376 // " }\n" \ 382 " }\n" \
377 // "\n" \ 383 "\n" \
378 // " /**\n" \ 384 " /**\n" \
379 // " This is the event when the user hits the 'image' button\n" \ 385 " This is the event when the user hits the 'image' button\n" \
380 // " */\n" \ 386 " */\n" \
381 // " function chat_onImageClicked(event)\n" \ 387 " function chat_onImageClicked(event)\n" \
382 // " {\n" \ 388 " {\n" \
383 // " document.getElementById('InputImage').click();\n" \ 389 " document.getElementById('InputImage').click();\n" \
384 // " }\n" \ 390 " }\n" \
385 // "\n" \ 391 "\n" \
386 // " /**\n" \ 392 " /**\n" \
387 // " This is the event when the user selected an image.\n" \ 393 " This is the event when the user selected an image.\n" \
388 // " The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \ 394 " The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \
389 // " */\n" \ 395 " */\n" \
390 // " function chat_onImageSelected(event)\n" \ 396 " function chat_onImageSelected(event)\n" \
391 // " {\n" \ 397 " {\n" \
392 // " let file = event.target.files[0];\n" \ 398 " let file = event.target.files[0];\n" \
393 // " if(!file || !/^image\\//.test(file.type))\n" \ 399 " if(!file || !/^image\\//.test(file.type))\n" \
394 // " return;\n" \ 400 " return;\n" \
395 // " let selectedUser = document.querySelector('div#Users > div.selected');\n" \ 401 " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
396 // " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \ 402 " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
397 // " let reader = new FileReader();\n" \ 403 " let reader = new FileReader();\n" \
398 // " reader.onload = function(event) {\n" \ 404 " reader.onload = function(event) {\n" \
399 // " chat_onImageRead(event, file.type, selectedUserId);\n" \ 405 " chat_onImageRead(event, file.type, selectedUserId);\n" \
400 // " };\n" \ 406 " };\n" \
401 // " reader.readAsArrayBuffer(file);\n" \ 407 " reader.readAsArrayBuffer(file);\n" \
402 // " }\n" \ 408 " }\n" \
403 // "\n" \ 409 "\n" \
404 // " /**\n" \ 410 " /**\n" \
405 // " This is the event when the user selected image has been read.\n" \ 411 " This is the event when the user selected image has been read.\n" \
406 // " This will add our chat protocol prefix and send it via the websocket.\n" \ 412 " This will add our chat protocol prefix and send it via the websocket.\n" \
407 // " */\n" \ 413 " */\n" \
408 // " function chat_onImageRead(event, fileType, selectedUserId)\n" \ 414 " function chat_onImageRead(event, fileType, selectedUserId)\n" \
409 // " {\n" \ 415 " {\n" \
410 // " let encoder = new TextEncoder();\n" \ 416 " let encoder = new TextEncoder();\n" \
411 // " let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \ 417 " let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \
412 // " prefix = encoder.encode(prefix);\n" \ 418 " prefix = encoder.encode(prefix);\n" \
413 // " let byteData = new Uint8Array(event.target.result);\n" \ 419 " let byteData = new Uint8Array(event.target.result);\n" \
414 // " let totalLength = prefix.length + byteData.length;\n" \ 420 " let totalLength = prefix.length + byteData.length;\n" \
415 // " let resultByteData = new Uint8Array(totalLength);\n" \ 421 " let resultByteData = new Uint8Array(totalLength);\n" \
416 // " resultByteData.set(prefix, 0);\n" \ 422 " resultByteData.set(prefix, 0);\n" \
417 // " resultByteData.set(byteData, prefix.length);\n" \ 423 " resultByteData.set(byteData, prefix.length);\n" \
418 // " socket.send(resultByteData);\n" \ 424 " socket.send(resultByteData);\n" \
419 // " }\n" \ 425 " }\n" \
420 // "\n" \ 426 "\n" \
421 // " /**\n" \ 427 " /**\n" \
422 // " This is the event when the user clicked a name in the user list.\n" \ 428 " This is the event when the user clicked a name in the user list.\n" \
423 // " This is useful to send private messages or images without needing to add the /m prefix.\n" \ 429 " This is useful to send private messages or images without needing to add the /m prefix.\n" \
424 // " */\n" \ 430 " */\n" \
425 // " function chat_onUserClicked(event, selectedUserId)\n" \ 431 " function chat_onUserClicked(event, selectedUserId)\n" \
426 // " {\n" \ 432 " {\n" \
427 // " let newSelected = event.target.closest('div#Users > div');\n" \ 433 " let newSelected = event.target.closest('div#Users > div');\n" \
428 // " if(newSelected === null)\n" \ 434 " if(newSelected === null)\n" \
429 // " return;\n" \ 435 " return;\n" \
430 // " for(let div of this.querySelectorAll(':scope > div.selected'))\n" \ 436 " for(let div of this.querySelectorAll(':scope > div.selected'))\n" \
431 // " div.classList.remove('selected');\n" \ 437 " div.classList.remove('selected');\n" \
432 // " newSelected.classList.add('selected');\n" \ 438 " newSelected.classList.add('selected');\n" \
433 // " }\n" \ 439 " }\n" \
434 // "\n" \ 440 "\n" \
435 // " /**\n" \ 441 " /**\n" \
436 // " This functions returns the current id of a user identified by its name.\n" \ 442 " This functions returns the current id of a user identified by its name.\n" \
437 // " */\n" \ 443 " */\n" \
438 // " function chat_getUserIdByName(name)\n" \ 444 " function chat_getUserIdByName(name)\n" \
439 // " {\n" \ 445 " {\n" \
440 // " let nameUpper = name.toUpperCase();\n" \ 446 " let nameUpper = name.toUpperCase();\n" \
441 // " for(let pair of connectedUsers)\n" \ 447 " for(let pair of connectedUsers)\n" \
442 // " {\n" \ 448 " {\n" \
443 // " if(pair[1].toUpperCase() == nameUpper)\n" \ 449 " if(pair[1].toUpperCase() == nameUpper)\n" \
444 // " return pair[0];\n" \ 450 " return pair[0];\n" \
445 // " }\n" \ 451 " }\n" \
446 // " return null;\n" \ 452 " return null;\n" \
447 // " }\n" \ 453 " }\n" \
448 // "\n" \ 454 "\n" \
449 // " /**\n" \ 455 " /**\n" \
450 // " This functions clears the entire user list (needed for reconnecting).\n" \ 456 " This functions clears the entire user list (needed for reconnecting).\n" \
451 // " */\n" \ 457 " */\n" \
452 // " function chat_clearUserList()\n" \ 458 " function chat_clearUserList()\n" \
453 // " {\n" \ 459 " {\n" \
454 // " let users = document.getElementById('Users');\n" \ 460 " let users = document.getElementById('Users');\n" \
455 // " for(let div of users.querySelectorAll(':scope > div'))\n" \ 461 " for(let div of users.querySelectorAll(':scope > div'))\n" \
456 // " {\n" \ 462 " {\n" \
457 // " if(div.getAttribute('data-user') === '0')\n" \ 463 " if(div.getAttribute('data-user') === '0')\n" \
458 // " {\n" \ 464 " {\n" \
459 // " div.classList.add('selected');\n" \ 465 " div.classList.add('selected');\n" \
460 // " }\n" \ 466 " }\n" \
461 // " else\n" \ 467 " else\n" \
462 // " {\n" \ 468 " {\n" \
463 // " div.parentNode.removeChild(div);\n" \ 469 " div.parentNode.removeChild(div);\n" \
464 // " }\n" \ 470 " }\n" \
465 // " }\n" \ 471 " }\n" \
466 // " return null;\n" \ 472 " return null;\n" \
467 // " }\n" \ 473 " }\n" \
468 // "\n" \ 474 "\n" \
469 // " /**\n" \ 475 " /**\n" \
470 // " This is the event when the socket has established a connection.\n" \ 476 " This is the event when the socket has established a connection.\n" \
471 // " This will initialize an empty chat and enable the controls.\n" \ 477 " This will initialize an empty chat and enable the controls.\n" \
472 // " */\n" \ 478 " */\n" \
473 // " function socket_onopen(event)\n" \ 479 " function socket_onopen(event)\n" \
474 // " {\n" \ 480 " {\n" \
475 // " connectedUsers.clear();\n" \ 481 " connectedUsers.clear();\n" \
476 // " chat_clearUserList();\n" \ 482 " chat_clearUserList();\n" \
477 // " chat_addMessage('Connected!', { type: 'system' });\n" \ 483 " chat_addMessage('Connected!', { type: 'system' });\n" \
478 // " document.getElementById('InputMessage').disabled = false;\n" \ 484 " document.getElementById('InputMessage').disabled = false;\n" \
479 // " document.getElementById('InputMessageButton').disabled = false;\n" \ 485 " document.getElementById('InputMessageButton').disabled = false;\n" \
480 // " document.getElementById('InputImageButton').disabled = false;\n" \ 486 " document.getElementById('InputImageButton').disabled = false;\n" \
481 // " }\n" \ 487 " }\n" \
482 // "\n" \ 488 "\n" \
483 // " /**\n" \ 489 " /**\n" \
484 // " This is the event when the socket has been closed.\n" \ 490 " This is the event when the socket has been closed.\n" \
485 // " This will lock the controls.\n" \ 491 " This will lock the controls.\n" \
486 // " */\n" \ 492 " */\n" \
487 // " function socket_onclose(event)\n" \ 493 " function socket_onclose(event)\n" \
488 // " {\n" \ 494 " {\n" \
489 // " chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \ 495 " chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \
490 // " document.getElementById('InputMessage').disabled = true;\n" \ 496 " document.getElementById('InputMessage').disabled = true;\n" \
491 // " document.getElementById('InputMessageButton').disabled = true;\n" \ 497 " document.getElementById('InputMessageButton').disabled = true;\n" \
492 // " document.getElementById('InputImageButton').disabled = true;\n" \ 498 " document.getElementById('InputImageButton').disabled = true;\n" \
493 // " }\n" \ 499 " }\n" \
494 // "\n" \ 500 "\n" \
495 // " /**\n" \ 501 " /**\n" \
496 // " This is the event when the socket reported an error.\n" \ 502 " This is the event when the socket reported an error.\n" \
497 // " This will just make an output.\n" \ 503 " This will just make an output.\n" \
498 // " In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \ 504 " In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \
499 // " */\n" \ 505 " */\n" \
500 // " function socket_onerror(event)\n" \ 506 " function socket_onerror(event)\n" \
501 // " {\n" \ 507 " {\n" \
502 // " console.error('WebSocket error reported: ', event);\n" \ 508 " console.error('WebSocket error reported: ', event);\n" \
503 // " chat_addMessage('The socket reported an error!', { type: 'error' });\n" \ 509 " chat_addMessage('The socket reported an error!', { type: 'error' });\n" \
504 // " }\n" \ 510 " }\n" \
505 // "\n" \ 511 "\n" \
506 // " /**\n" \ 512 " /**\n" \
507 // " This is the event when the socket has received a message.\n" \ 513 " This is the event when the socket has received a message.\n" \
508 // " This will parse the message and execute the corresponding command (or add the message).\n" \ 514 " This will parse the message and execute the corresponding command (or add the message).\n" \
509 // " */\n" \ 515 " */\n" \
510 // " function socket_onmessage(event)\n" \ 516 " function socket_onmessage(event)\n" \
511 // " {\n" \ 517 " {\n" \
512 // " if(typeof(event.data) === 'string')\n" \ 518 " if(typeof(event.data) === 'string')\n" \
513 // " {\n" \ 519 " {\n" \
514 // " // text message or command\n" \ 520 " // text message or command\n" \
515 // " let message = event.data.split('|', 3);\n" \ 521 " let message = event.data.split('|', 3);\n" \
516 // " switch(message[0])\n" \ 522 " switch(message[0])\n" \
517 // " {\n" \ 523 " {\n" \
518 // " case 'userinit':\n" \ 524 " case 'userinit':\n" \
519 // " connectedUsers.set(message[1], message[2]);\n" \ 525 " connectedUsers.set(message[1], message[2]);\n" \
520 // " {\n" \ 526 " {\n" \
521 // " let users = document.getElementById('Users');\n" \ 527 " let users = document.getElementById('Users');\n" \
522 // " let div = document.createElement('div');\n" \ 528 " let div = document.createElement('div');\n" \
523 // " users.appendChild(div);\n" \ 529 " users.appendChild(div);\n" \
524 // " div.innerText = message[2];\n" \ 530 " div.innerText = message[2];\n" \
525 // " div.setAttribute('data-user', message[1]);\n" \ 531 " div.setAttribute('data-user', message[1]);\n" \
526 // " }\n" \ 532 " }\n" \
527 // " break;\n" \ 533 " break;\n" \
528 // " case 'useradd':\n" \ 534 " case 'useradd':\n" \
529 // " connectedUsers.set(message[1], message[2]);\n" \ 535 " connectedUsers.set(message[1], message[2]);\n" \
530 // " chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \ 536 " chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \
531 // " {\n" \ 537 " {\n" \
532 // " let users = document.getElementById('Users');\n" \ 538 " let users = document.getElementById('Users');\n" \
533 // " let div = document.createElement('div');\n" \ 539 " let div = document.createElement('div');\n" \
534 // " users.appendChild(div);\n" \ 540 " users.appendChild(div);\n" \
535 // " div.innerText = message[2];\n" \ 541 " div.innerText = message[2];\n" \
536 // " div.setAttribute('data-user', message[1]);\n" \ 542 " div.setAttribute('data-user', message[1]);\n" \
537 // " }\n" \ 543 " }\n" \
538 // " break;\n" \ 544 " break;\n" \
539 // " case 'userdel':\n" \ 545 " case 'userdel':\n" \
540 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \ 546 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \
541 // " connectedUsers.delete(message[1]);\n" \ 547 " connectedUsers.delete(message[1]);\n" \
542 // " {\n" \ 548 " {\n" \
543 // " let users = document.getElementById('Users');\n" \ 549 " let users = document.getElementById('Users');\n" \
544 // " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \ 550 " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
545 // " if(div !== null)\n" \ 551 " if(div !== null)\n" \
546 // " {\n" \ 552 " {\n" \
547 // " users.removeChild(div);\n" \ 553 " users.removeChild(div);\n" \
548 // " if(div.classList.contains('selected'))\n" \ 554 " if(div.classList.contains('selected'))\n" \
549 // " users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \ 555 " users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
550 // " }\n" \ 556 " }\n" \
551 // " }\n" \ 557 " }\n" \
552 // " break;\n" \ 558 " break;\n" \
553 // " case 'username':\n" \ 559 " case 'username':\n" \
554 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \ 560 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \
555 // " connectedUsers.set(message[1], message[2]);\n" \ 561 " connectedUsers.set(message[1], message[2]);\n" \
556 // " {\n" \ 562 " {\n" \
557 // " let users = document.getElementById('Users');\n" \ 563 " let users = document.getElementById('Users');\n" \
558 // " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \ 564 " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
559 // " if(div !== null)\n" \ 565 " if(div !== null)\n" \
560 // " {\n" \ 566 " {\n" \
561 // " div.innerText = message[2];\n" \ 567 " div.innerText = message[2];\n" \
562 // " }\n" \ 568 " }\n" \
563 // " }\n" \ 569 " }\n" \
564 // " break;\n" \ 570 " break;\n" \
565 // " case 'ping':\n" \ 571 " case 'ping':\n" \
566 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \ 572 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \
567 // " break;\n" \ 573 " break;\n" \
568 // " default:\n" \ 574 " default:\n" \
569 // " chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \ 575 " chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \
570 // " break;\n" \ 576 " break;\n" \
571 // " }\n" \ 577 " }\n" \
572 // " }\n" \ 578 " }\n" \
573 // " else\n" \ 579 " else\n" \
574 // " {\n" \ 580 " {\n" \
575 // " // We received a binary frame, which means a picture here\n" \ 581 " // We received a binary frame, which means a picture here\n" \
576 // " let byteData = new Uint8Array(event.data);\n" \ 582 " let byteData = new Uint8Array(event.data);\n" \
577 // " let decoder = new TextDecoder();\n" \ 583 " let decoder = new TextDecoder();\n" \
578 // " let message = [ ];\n" \ 584 " let message = [ ];\n" \
579 // " // message type\n" \ 585 " // message type\n" \
580 // " let j = 0;\n" \ 586 " let j = 0;\n" \
581 // " let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \ 587 " let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \
582 // " if(i < 0)\n" \ 588 " if(i < 0)\n" \
583 // " return;\n" \ 589 " return;\n" \
584 // " message.push(decoder.decode(byteData.slice(0, i)));\n" \ 590 " message.push(decoder.decode(byteData.slice(0, i)));\n" \
585 // " // picture from\n" \ 591 " // picture from\n" \
586 // " j = i + 1;\n" \ 592 " j = i + 1;\n" \
587 // " i = byteData.indexOf(0x7C, j);\n" \ 593 " i = byteData.indexOf(0x7C, j);\n" \
588 // " if(i < 0)\n" \ 594 " if(i < 0)\n" \
589 // " return;\n" \ 595 " return;\n" \
590 // " message.push(decoder.decode(byteData.slice(j, i)));\n" \ 596 " message.push(decoder.decode(byteData.slice(j, i)));\n" \
591 // " // picture encoding\n" \ 597 " // picture encoding\n" \
592 // " j = i + 1;\n" \ 598 " j = i + 1;\n" \
593 // " i = byteData.indexOf(0x7C, j);\n" \ 599 " i = byteData.indexOf(0x7C, j);\n" \
594 // " if(i < 0)\n" \ 600 " if(i < 0)\n" \
595 // " return;\n" \ 601 " return;\n" \
596 // " message.push(decoder.decode(byteData.slice(j, i)));\n" \ 602 " message.push(decoder.decode(byteData.slice(j, i)));\n" \
597 // " // picture\n" \ 603 " // picture\n" \
598 // " byteData = byteData.slice(i + 1);\n" \ 604 " byteData = byteData.slice(i + 1);\n" \
599 // " chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \ 605 " chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \
600 // " }\n" \ 606 " }\n" \
601 // " }\n" \ 607 " }\n" \
602 // "</script>" \ 608 "</script>" \
603 // "</head>" \ 609 "</head>" \
604 // "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \ 610 "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \
605 // "</html>" 611 "</html>"
606 612
607#define PAGE_NOT_FOUND \ 613#define PAGE_NOT_FOUND \
608 "404 Not Found" 614 "404 Not Found"
@@ -708,7 +714,7 @@ make_blocking (MHD_socket fd)
708#elif defined(MHD_WINSOCK_SOCKETS) 714#elif defined(MHD_WINSOCK_SOCKETS)
709 unsigned long flags = 0; 715 unsigned long flags = 0;
710 716
711 ioctlsocket (fd, (int) FIONBIO, &flags); 717 ioctlsocket (fd, FIONBIO, &flags);
712#endif /* MHD_WINSOCK_SOCKETS */ 718#endif /* MHD_WINSOCK_SOCKETS */
713 719
714} 720}
@@ -739,7 +745,6 @@ send_all (struct ConnectedUser*cu,
739 0); 745 0);
740 if (0 > ret) 746 if (0 > ret)
741 { 747 {
742 int err = errno;
743 if (EAGAIN == errno) 748 if (EAGAIN == errno)
744 { 749 {
745 ret = 0; 750 ret = 0;
@@ -927,7 +932,7 @@ chat_adduser (struct ConnectedUser*cu)
927{ 932{
928 /* initialize the notification message of the new user */ 933 /* initialize the notification message of the new user */
929 char user_index[32]; 934 char user_index[32];
930 itoa ((int) cu->user_id, user_index, 10); 935 snprintf (user_index, 32, "%d", (int) cu->user_id);
931 size_t user_index_len = strlen (user_index); 936 size_t user_index_len = strlen (user_index);
932 size_t data_len = user_index_len + cu->user_name_len + 9; 937 size_t data_len = user_index_len + cu->user_name_len + 9;
933 char*data = (char*) malloc (data_len + 1); 938 char*data = (char*) malloc (data_len + 1);
@@ -998,7 +1003,7 @@ chat_removeuser (struct ConnectedUser*cu)
998 char user_index[32]; 1003 char user_index[32];
999 1004
1000 /* initialize the chat message for the removed user */ 1005 /* initialize the chat message for the removed user */
1001 itoa ((int) cu->user_id, user_index, 10); 1006 snprintf (user_index, 32, "%d", (int) cu->user_id);
1002 size_t user_index_len = strlen (user_index); 1007 size_t user_index_len = strlen (user_index);
1003 size_t data_len = user_index_len + 9; 1008 size_t data_len = user_index_len + 9;
1004 char*data = (char*) malloc (data_len + 1); 1009 char*data = (char*) malloc (data_len + 1);
@@ -1071,9 +1076,8 @@ chat_renameuser (struct ConnectedUser*cu,
1071 { 1076 {
1072 if (cu != users[i]) 1077 if (cu != users[i])
1073 { 1078 {
1074 if ((users[i]->user_name_len == new_name_len) && (0 == stricmp ( 1079 if ((users[i]->user_name_len == new_name_len) &&
1075 users[i]->user_name, 1080 (0 == strcasecmp (users[i]->user_name, new_name)))
1076 new_name)))
1077 { 1081 {
1078 pthread_mutex_unlock (&chat_mutex); 1082 pthread_mutex_unlock (&chat_mutex);
1079 return 2; 1083 return 2;
@@ -1083,7 +1087,7 @@ chat_renameuser (struct ConnectedUser*cu,
1083 1087
1084 /* generate the notification message */ 1088 /* generate the notification message */
1085 char user_index[32]; 1089 char user_index[32];
1086 itoa ((int) cu->user_id, user_index, 10); 1090 snprintf (user_index, 32, "%d", (int) cu->user_id);
1087 size_t user_index_len = strlen (user_index); 1091 size_t user_index_len = strlen (user_index);
1088 size_t data_len = user_index_len + new_name_len + 10; 1092 size_t data_len = user_index_len + new_name_len + 10;
1089 char*data = (char*) malloc (data_len + 1); 1093 char*data = (char*) malloc (data_len + 1);
@@ -1202,17 +1206,17 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1202 /* no command means regular message */ 1206 /* no command means regular message */
1203 command = 0; 1207 command = 0;
1204 } 1208 }
1205 else if (0 == stricmp (frame_data, "private")) 1209 else if (0 == strcasecmp (frame_data, "private"))
1206 { 1210 {
1207 /* private means private message */ 1211 /* private means private message */
1208 command = 1; 1212 command = 1;
1209 } 1213 }
1210 else if (0 == stricmp (frame_data, "name")) 1214 else if (0 == strcasecmp (frame_data, "name"))
1211 { 1215 {
1212 /* name means chat user rename */ 1216 /* name means chat user rename */
1213 command = 2; 1217 command = 2;
1214 } 1218 }
1215 else if (0 == stricmp (frame_data, "ping")) 1219 else if (0 == strcasecmp (frame_data, "ping"))
1216 { 1220 {
1217 /* ping means a ping request */ 1221 /* ping means a ping request */
1218 command = 3; 1222 command = 3;
@@ -1263,7 +1267,7 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1263 * This is useful for debugging with an IDE. 1267 * This is useful for debugging with an IDE.
1264 */ 1268 */
1265 char user_index[32]; 1269 char user_index[32];
1266 itoa ((int) from_user_id, user_index, 10); 1270 snprintf (user_index, 32, "%d", (int) cu->user_id);
1267 size_t user_index_len = strlen (user_index); 1271 size_t user_index_len = strlen (user_index);
1268 size_t data_len = user_index_len + frame_len - j + 9; 1272 size_t data_len = user_index_len + frame_len - j + 9;
1269 char*data = (char*) malloc (data_len + 1); 1273 char*data = (char*) malloc (data_len + 1);
@@ -1300,7 +1304,7 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1300 * The difference is the prefix "private" 1304 * The difference is the prefix "private"
1301 */ 1305 */
1302 char user_index[32]; 1306 char user_index[32];
1303 itoa ((int) from_user_id, user_index, 10); 1307 snprintf (user_index, 32, "%d", (int) cu->user_id);
1304 size_t user_index_len = strlen (user_index); 1308 size_t user_index_len = strlen (user_index);
1305 size_t data_len = user_index_len + frame_len - j + 9; 1309 size_t data_len = user_index_len + frame_len - j + 9;
1306 char*data = (char*) malloc (data_len + 1); 1310 char*data = (char*) malloc (data_len + 1);
@@ -1538,14 +1542,10 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1538 char result_text[240]; 1542 char result_text[240];
1539 strcpy (result_text, 1543 strcpy (result_text,
1540 "ping|"); 1544 "ping|");
1541 itoa ((int) cu->user_id, 1545 snprintf (result_text + 5, 235, "%d", (int) cu->user_id);
1542 result_text + 5,
1543 10);
1544 strcat (result_text, 1546 strcat (result_text,
1545 "|"); 1547 "|");
1546 itoa (ping, 1548 snprintf (result_text + strlen (result_text), 240 - strlen (result_text), "%d", (int) ping);
1547 result_text + strlen (result_text),
1548 10);
1549 chat_addmessage (0, 1549 chat_addmessage (0,
1550 0, 1550 0,
1551 result_text, 1551 result_text,
@@ -1627,9 +1627,7 @@ connecteduser_send_messages (void*cls)
1627 ++cu->ping_counter; 1627 ++cu->ping_counter;
1628 strcpy (cu->ping_message, 1628 strcpy (cu->ping_message,
1629 "libmicrohttpdchatserverpingdata"); 1629 "libmicrohttpdchatserverpingdata");
1630 itoa (cu->ping_counter, 1630 snprintf (cu->ping_message + 31, 97, "%d", (int) cu->ping_counter);
1631 cu->ping_message + 31,
1632 10);
1633 cu->ping_message_len = strlen (cu->ping_message); 1631 cu->ping_message_len = strlen (cu->ping_message);
1634 char*frame_data = NULL; 1632 char*frame_data = NULL;
1635 size_t frame_len = 0; 1633 size_t frame_len = 0;
@@ -1750,7 +1748,7 @@ connecteduser_receive_messages (void *cls)
1750 { 1748 {
1751 char user_name[32]; 1749 char user_name[32];
1752 strcpy (user_name, "User"); 1750 strcpy (user_name, "User");
1753 itoa ((int) cu->user_id, user_name + 4, 10); 1751 snprintf (user_name + 4, 28, "%d", (int) cu->user_id);
1754 cu->user_name_len = strlen (user_name); 1752 cu->user_name_len = strlen (user_name);
1755 cu->user_name = malloc (cu->user_name_len + 1); 1753 cu->user_name = malloc (cu->user_name_len + 1);
1756 if (NULL == cu->user_name) 1754 if (NULL == cu->user_name)
@@ -1831,7 +1829,7 @@ connecteduser_receive_messages (void *cls)
1831 for (size_t i = 0; i < user_count; ++i) 1829 for (size_t i = 0; i < user_count; ++i)
1832 { 1830 {
1833 char user_index[32]; 1831 char user_index[32];
1834 itoa ((int) users[i]->user_id, user_index, 10); 1832 snprintf (user_index, 32, "%d", (int) users[i]->user_id);
1835 size_t user_index_len = strlen (user_index); 1833 size_t user_index_len = strlen (user_index);
1836 struct UserInit iu; 1834 struct UserInit iu;
1837 iu.user_init_len = user_index_len + users[i]->user_name_len + 10; 1835 iu.user_init_len = user_index_len + users[i]->user_name_len + 10;
@@ -1891,15 +1889,16 @@ connecteduser_receive_messages (void *cls)
1891 " /ping <user> - sends a ping to the specified user\n" \ 1889 " /ping <user> - sends a ping to the specified user\n" \
1892 " /name <name> - changes your name to the specified name\n" \ 1890 " /name <name> - changes your name to the specified name\n" \
1893 " /disconnect - disconnects your websocket\n\n" \ 1891 " /disconnect - disconnects your websocket\n\n" \
1894 "All messages, which does not start a slash, are regular messages, which will be sent to selected user.\n\n" \ 1892 "All messages, which does not start with a slash, " \
1893 "are regular messages and will be sent to the selected user.\n\n" \
1895 "Have fun!"; 1894 "Have fun!";
1896 int r = MHD_websocket_encode_text (cu->ws, 1895 MHD_websocket_encode_text (cu->ws,
1897 welcome_msg, 1896 welcome_msg,
1898 strlen (welcome_msg), 1897 strlen (welcome_msg),
1899 MHD_WEBSOCKET_FRAGMENTATION_NONE, 1898 MHD_WEBSOCKET_FRAGMENTATION_NONE,
1900 &frame_data, 1899 &frame_data,
1901 &frame_len, 1900 &frame_len,
1902 NULL); 1901 NULL);
1903 send_all (cu, 1902 send_all (cu,
1904 frame_data, 1903 frame_data,
1905 frame_len); 1904 frame_len);
@@ -2132,7 +2131,7 @@ upgrade_handler (void *cls,
2132 * @param ptr A pointer for request specific data 2131 * @param ptr A pointer for request specific data
2133 * @return MHD_YES on success or MHD_NO on error. 2132 * @return MHD_YES on success or MHD_NO on error.
2134 */ 2133 */
2135static int 2134static enum MHD_Result
2136access_handler (void *cls, 2135access_handler (void *cls,
2137 struct MHD_Connection *connection, 2136 struct MHD_Connection *connection,
2138 const char *url, 2137 const char *url,
@@ -2185,118 +2184,88 @@ access_handler (void *cls,
2185 * Furthermore it must be a HTTP/1.1 or higher GET request. 2184 * Furthermore it must be a HTTP/1.1 or higher GET request.
2186 * See: https://tools.ietf.org/html/rfc6455#section-4.2.1 2185 * See: https://tools.ietf.org/html/rfc6455#section-4.2.1
2187 * 2186 *
2188 * To ease this example we skip the following checks: 2187 * To make this example portable we skip the Host check
2189 * - Whether the HTTP version is 1.1 or newer
2190 * - Whether Connection is Upgrade, because this header may
2191 * contain multiple values.
2192 * - The requested Host (because we don't know)
2193 */ 2188 */
2194 2189
2190 char is_valid = 1;
2191 const char* value = NULL;
2192 char sec_websocket_accept[29];
2193
2195 /* check whether an websocket upgrade is requested */ 2194 /* check whether an websocket upgrade is requested */
2196 const char*value = MHD_lookup_connection_value (connection, 2195 if (0 != MHD_websocket_check_http_version (version))
2197 MHD_HEADER_KIND,
2198 MHD_HTTP_HEADER_UPGRADE);
2199 if ((0 == value) || (0 != stricmp (value, "websocket")))
2200 { 2196 {
2201 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2197 is_valid = 0;
2202 PAGE_INVALID_WEBSOCKET_REQUEST), 2198 }
2203 PAGE_INVALID_WEBSOCKET_REQUEST, 2199 value = MHD_lookup_connection_value (connection,
2204 MHD_RESPMEM_PERSISTENT); 2200 MHD_HEADER_KIND,
2205 ret = MHD_queue_response (connection, 2201 MHD_HTTP_HEADER_CONNECTION);
2206 MHD_HTTP_BAD_REQUEST, 2202 if (0 != MHD_websocket_check_connection_header (value))
2207 response); 2203 {
2208 MHD_destroy_response (response); 2204 is_valid = 0;
2209 return ret; 2205 }
2206 value = MHD_lookup_connection_value (connection,
2207 MHD_HEADER_KIND,
2208 MHD_HTTP_HEADER_UPGRADE);
2209 if (0 != MHD_websocket_check_upgrade_header (value))
2210 {
2211 is_valid = 0;
2210 } 2212 }
2211
2212 /* check the protocol version */
2213 value = MHD_lookup_connection_value (connection, 2213 value = MHD_lookup_connection_value (connection,
2214 MHD_HEADER_KIND, 2214 MHD_HEADER_KIND,
2215 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION); 2215 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
2216 if ((0 == value) || (0 != stricmp (value, "13"))) 2216 if (0 != MHD_websocket_check_version_header (value))
2217 { 2217 {
2218 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2218 is_valid = 0;
2219 PAGE_INVALID_WEBSOCKET_REQUEST),
2220 PAGE_INVALID_WEBSOCKET_REQUEST,
2221 MHD_RESPMEM_PERSISTENT);
2222 ret = MHD_queue_response (connection,
2223 MHD_HTTP_BAD_REQUEST,
2224 response);
2225 MHD_destroy_response (response);
2226 return ret;
2227 } 2219 }
2228 2220 value = MHD_lookup_connection_value (connection,
2229 /* read the websocket key (required for the response) */ 2221 MHD_HEADER_KIND,
2230 value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
2231 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY); 2222 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
2232 if (0 == value) 2223 if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
2233 { 2224 {
2234 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2225 is_valid = 0;
2235 PAGE_INVALID_WEBSOCKET_REQUEST),
2236 PAGE_INVALID_WEBSOCKET_REQUEST,
2237 MHD_RESPMEM_PERSISTENT);
2238 ret = MHD_queue_response (connection,
2239 MHD_HTTP_BAD_REQUEST,
2240 response);
2241 MHD_destroy_response (response);
2242 return ret;
2243 } 2226 }
2244 2227
2245 /* generate the response accept header */ 2228 if (1 == is_valid)
2246 char sec_websocket_accept[29];
2247 if (0 != MHD_websocket_create_accept (value, sec_websocket_accept))
2248 { 2229 {
2249 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2230 /* create the response for upgrade */
2250 PAGE_INVALID_WEBSOCKET_REQUEST), 2231 response = MHD_create_response_for_upgrade (&upgrade_handler,
2251 PAGE_INVALID_WEBSOCKET_REQUEST, 2232 NULL);
2252 MHD_RESPMEM_PERSISTENT); 2233
2234 /**
2235 * For the response we need at least the following headers:
2236 * 1. "Connection: Upgrade"
2237 * 2. "Upgrade: websocket"
2238 * 3. "Sec-WebSocket-Accept: <base64value>"
2239 * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept_header.
2240 * It requires the value of the Sec-WebSocket-Key header of the request.
2241 * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
2242 */
2243 MHD_add_response_header (response,
2244 MHD_HTTP_HEADER_CONNECTION,
2245 "Upgrade");
2246 MHD_add_response_header (response,
2247 MHD_HTTP_HEADER_UPGRADE,
2248 "websocket");
2249 MHD_add_response_header (response,
2250 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
2251 sec_websocket_accept);
2253 ret = MHD_queue_response (connection, 2252 ret = MHD_queue_response (connection,
2254 MHD_HTTP_BAD_REQUEST, 2253 MHD_HTTP_SWITCHING_PROTOCOLS,
2255 response); 2254 response);
2256 MHD_destroy_response (response); 2255 MHD_destroy_response (response);
2257 return ret;
2258 } 2256 }
2259 2257 else
2260 /* only for this example: don't accept incoming connection when we are already shutting down */
2261 if (0 != disconnect_all)
2262 { 2258 {
2259 /* return error page */
2263 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2260 struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
2264 PAGE_INVALID_WEBSOCKET_REQUEST), 2261 PAGE_INVALID_WEBSOCKET_REQUEST),
2265 PAGE_INVALID_WEBSOCKET_REQUEST, 2262 PAGE_INVALID_WEBSOCKET_REQUEST,
2266 MHD_RESPMEM_PERSISTENT); 2263 MHD_RESPMEM_PERSISTENT);
2267 ret = MHD_queue_response (connection, 2264 ret = MHD_queue_response (connection,
2268 MHD_HTTP_SERVICE_UNAVAILABLE, 2265 MHD_HTTP_BAD_REQUEST,
2269 response); 2266 response);
2270 MHD_destroy_response (response); 2267 MHD_destroy_response (response);
2271 return ret;
2272 } 2268 }
2273
2274 /* create the response for upgrade */
2275 response = MHD_create_response_for_upgrade (&upgrade_handler,
2276 NULL);
2277
2278 /**
2279 * For the response we need at least the following headers:
2280 * 1. "Connection: Upgrade"
2281 * 2. "Upgrade: websocket"
2282 * 3. "Sec-WebSocket-Accept: <base64value>"
2283 * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept.
2284 * It requires the value of the Sec-WebSocket-Key header of the request.
2285 * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
2286 */
2287 MHD_add_response_header (response,
2288 MHD_HTTP_HEADER_CONNECTION,
2289 "Upgrade");
2290 MHD_add_response_header (response,
2291 MHD_HTTP_HEADER_UPGRADE,
2292 "websocket");
2293 MHD_add_response_header (response,
2294 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
2295 sec_websocket_accept);
2296 ret = MHD_queue_response (connection,
2297 MHD_HTTP_SWITCHING_PROTOCOLS,
2298 response);
2299 MHD_destroy_response (response);
2300 } 2269 }
2301 else 2270 else
2302 { 2271 {
@@ -2353,7 +2322,7 @@ main (int argc,
2353 MHD_OPTION_END); 2322 MHD_OPTION_END);
2354#endif 2323#endif
2355 2324
2356 if (d == NULL) 2325 if (NULL == d)
2357 return 1; 2326 return 1;
2358 (void) getc (stdin); 2327 (void) getc (stdin);
2359 2328