aboutsummaryrefslogtreecommitdiff
path: root/src/examples/websocket_chatserver_example.c
diff options
context:
space:
mode:
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