aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/examples/websocket_chatserver_example.c1051
-rw-r--r--src/include/microhttpd_ws.h534
-rw-r--r--src/microhttpd_ws/mhd_websocket.c587
-rw-r--r--src/microhttpd_ws/sha1.c720
-rw-r--r--src/microhttpd_ws/sha1.h245
-rw-r--r--src/microhttpd_ws/test_websocket.c1247
-rw-r--r--src/microhttpd_ws/test_websocket_browser.c563
7 files changed, 3513 insertions, 1434 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
diff --git a/src/include/microhttpd_ws.h b/src/include/microhttpd_ws.h
index bfbd550a..f19c140d 100644
--- a/src/include/microhttpd_ws.h
+++ b/src/include/microhttpd_ws.h
@@ -51,29 +51,27 @@ struct MHD_WebSocketStream;
51enum MHD_WEBSOCKET_FLAG 51enum MHD_WEBSOCKET_FLAG
52{ 52{
53 /** 53 /**
54 * The websocket is used by the server (default). 54 * The websocket stream is initialized in server mode (default).
55 * Thus all outgoing payload will not be "masked". 55 * Thus all outgoing payload will not be "masked".
56 * All incoming payload must be masked. 56 * All incoming payload must be masked.
57 * This cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT 57 * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT
58 */ 58 */
59 MHD_WEBSOCKET_FLAG_SERVER = 0, 59 MHD_WEBSOCKET_FLAG_SERVER = 0,
60 /** 60 /**
61 * The websocket is used by the client 61 * The websocket stream is initialized in client mode.
62 * (not used if you provide the server). 62 * You will usually never use that mode in combination with libmicrohttpd,
63 * Thus all outgoing payload will be "masked" (XOR-ed with random values). 63 * because libmicrohttpd provides a server and not a client.
64 * In client mode all outgoing payload will be "masked"
65 * (XOR-ed with random values).
64 * All incoming payload must be unmasked. 66 * All incoming payload must be unmasked.
65 * Please note that this implementation doesn't use a strong random 67 * If you use this mode, you must always call #MHD_websocket_stream_init2()
66 * number generator for the mask as suggested in RFC6455 10.3, because 68 * instead of #MHD_websocket_stream_init(), because you need
67 * the main intention of this implementation is the use as server 69 * to pass a random number generator callback function for masking.
68 * with MHD, which doesn't need masking. 70 * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER
69 * Instead a weak random number generator is used (`rand()`).
70 * You can set the seed for the random number generator
71 * by calling #MHD_websocket_srand().
72 * This cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER
73 */ 71 */
74 MHD_WEBSOCKET_FLAG_CLIENT = 1, 72 MHD_WEBSOCKET_FLAG_CLIENT = 1,
75 /** 73 /**
76 * You don't want to get fragmented data while decoding. 74 * You don't want to get fragmented data while decoding (default).
77 * Fragmented frames will be internally put together until 75 * Fragmented frames will be internally put together until
78 * they are complete. 76 * they are complete.
79 * Whether or not data is fragmented is decided 77 * Whether or not data is fragmented is decided
@@ -85,11 +83,11 @@ enum MHD_WEBSOCKET_FLAG
85 * You want fragmented data, if it appears while decoding. 83 * You want fragmented data, if it appears while decoding.
86 * You will receive the content of the fragmented frame, 84 * You will receive the content of the fragmented frame,
87 * but if you are decoding text, you will never get an unfinished 85 * but if you are decoding text, you will never get an unfinished
88 * UTF-8 sequences (if the sequence appears between two fragments). 86 * UTF-8 sequence (if the sequence appears between two fragments).
89 * Instead the text will end before the unfinished UTF-8 sequence. 87 * Instead the text will end before the unfinished UTF-8 sequence.
90 * With the next fragment, which finishes the UTF-8 sequence, 88 * With the next fragment, which finishes the UTF-8 sequence,
91 * you will get the complete UTF-8 sequence. 89 * you will get the complete UTF-8 sequence.
92 * This cannot be used together with #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS 90 * This cannot be used together with #MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
93 */ 91 */
94 MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS = 2, 92 MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS = 2,
95 /** 93 /**
@@ -97,7 +95,7 @@ enum MHD_WEBSOCKET_FLAG
97 * protocol errors, a matching close frame will automatically 95 * protocol errors, a matching close frame will automatically
98 * be generated. 96 * be generated.
99 * The close frame will be returned via the parameters 97 * The close frame will be returned via the parameters
100 * result and result_len of #MHD_websocket_decode() and 98 * `payload` and `payload_len` of #MHD_websocket_decode() and
101 * the return value is negative 99 * the return value is negative
102 * (a value of `enum MHD_WEBSOCKET_STATUS`). 100 * (a value of `enum MHD_WEBSOCKET_STATUS`).
103 * The generated close frame must be freed by the caller 101 * The generated close frame must be freed by the caller
@@ -140,7 +138,7 @@ enum MHD_WEBSOCKET_FRAGMENTATION
140 * You want to use fragmentation. 138 * You want to use fragmentation.
141 * The encoded frame is the last frame of 139 * The encoded frame is the last frame of
142 * the series of data frames, but also not the first one. 140 * the series of data frames, but also not the first one.
143 * After this frame, you may send all type of frames again. 141 * After this frame, you may send all types of frames again.
144 */ 142 */
145 MHD_WEBSOCKET_FRAGMENTATION_LAST = 3 143 MHD_WEBSOCKET_FRAGMENTATION_LAST = 3
146}; 144};
@@ -157,18 +155,19 @@ enum MHD_WEBSOCKET_STATUS
157 * The call succeeded. 155 * The call succeeded.
158 * For #MHD_websocket_decode() this means that no error occurred, 156 * For #MHD_websocket_decode() this means that no error occurred,
159 * but also no frame has been completed yet. 157 * but also no frame has been completed yet.
158 * For other functions this means simply a success.
160 */ 159 */
161 MHD_WEBSOCKET_STATUS_OK = 0, 160 MHD_WEBSOCKET_STATUS_OK = 0,
162 /** 161 /**
163 * #MHD_websocket_decode() has decoded a text frame. 162 * #MHD_websocket_decode() has decoded a text frame.
164 * The parameters result and result_len are filled with the decoded text 163 * The parameters `payload` and `payload_len` are filled with
165 * (if any). 164 * the decoded text (if any).
166 */ 165 */
167 MHD_WEBSOCKET_STATUS_TEXT_FRAME = 0x1, 166 MHD_WEBSOCKET_STATUS_TEXT_FRAME = 0x1,
168 /** 167 /**
169 * #MHD_websocket_decode() has decoded a binary frame. 168 * #MHD_websocket_decode() has decoded a binary frame.
170 * The parameters result and result_len are filled with the decoded 169 * The parameters `payload` and `payload_len` are filled with
171 * binary data (if any). 170 * the decoded binary data (if any).
172 */ 171 */
173 MHD_WEBSOCKET_STATUS_BINARY_FRAME = 0x2, 172 MHD_WEBSOCKET_STATUS_BINARY_FRAME = 0x2,
174 /** 173 /**
@@ -176,12 +175,13 @@ enum MHD_WEBSOCKET_STATUS
176 * This means you must close the socket using #MHD_upgrade_action() 175 * This means you must close the socket using #MHD_upgrade_action()
177 * with #MHD_UPGRADE_ACTION_CLOSE. 176 * with #MHD_UPGRADE_ACTION_CLOSE.
178 * You may respond with a close frame before closing. 177 * You may respond with a close frame before closing.
179 * The parameters result and result_len are filled with 178 * The parameters `payload` and `payload_len` are filled with
180 * the close reason (if any). 179 * the close reason (if any).
181 * The close reason starts with a two byte sequence of close code 180 * The close reason starts with a two byte sequence of close code
182 * in network byte order (see `enum MHD_WEBSOCKET_CLOSEREASON`). 181 * in network byte order (see `enum MHD_WEBSOCKET_CLOSEREASON`).
183 * After these two bytes a UTF-8 encoded close reason may follow. 182 * After these two bytes a UTF-8 encoded close reason may follow.
184 * Compare with result_len to decide whether there is any close reason. 183 * You can call #MHD_websocket_split_close_reason() to split that
184 * close reason.
185 */ 185 */
186 MHD_WEBSOCKET_STATUS_CLOSE_FRAME = 0x8, 186 MHD_WEBSOCKET_STATUS_CLOSE_FRAME = 0x8,
187 /** 187 /**
@@ -189,7 +189,7 @@ enum MHD_WEBSOCKET_STATUS
189 * You should respond to this with a pong frame. 189 * You should respond to this with a pong frame.
190 * The pong frame must contain the same binary data as 190 * The pong frame must contain the same binary data as
191 * the corresponding ping frame (if it had any). 191 * the corresponding ping frame (if it had any).
192 * The parameters result and result_len are filled with 192 * The parameters `payload` and `payload_len` are filled with
193 * the binary ping data (if any). 193 * the binary ping data (if any).
194 */ 194 */
195 MHD_WEBSOCKET_STATUS_PING_FRAME = 0x9, 195 MHD_WEBSOCKET_STATUS_PING_FRAME = 0x9,
@@ -199,57 +199,79 @@ enum MHD_WEBSOCKET_STATUS
199 * a ping frame before. 199 * a ping frame before.
200 * The binary data should be equal to your ping frame and can be 200 * The binary data should be equal to your ping frame and can be
201 * used to distinguish the response if you sent multiple ping frames. 201 * used to distinguish the response if you sent multiple ping frames.
202 * The parameters result and result_len are filled with 202 * The parameters `payload` and `payload_len` are filled with
203 * the binary pong data (if any). 203 * the binary pong data (if any).
204 */ 204 */
205 MHD_WEBSOCKET_STATUS_PONG_FRAME = 0xA, 205 MHD_WEBSOCKET_STATUS_PONG_FRAME = 0xA,
206 /** 206 /**
207 * #MHD_websocket_decode() has decoded a text frame fragment. 207 * #MHD_websocket_decode() has decoded a text frame fragment.
208 * The parameters result and result_len are filled with the decoded text 208 * The parameters `payload` and `payload_len` are filled with
209 * (if any). 209 * the decoded text (if any).
210 * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAME, but it can only 210 * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAME, but it can only
211 * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during 211 * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during
212 * the call of #MHD_websocket_stream_init() or 212 * the call of #MHD_websocket_stream_init() or
213 * #MHD_websocket_stream_init2(). 213 * #MHD_websocket_stream_init2().
214 */ 214 */
215 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT = 0x11, 215 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT = 0x11,
216 /** 216 /**
217 * #MHD_websocket_decode() has decoded a binary frame fragment. 217 * #MHD_websocket_decode() has decoded a binary frame fragment.
218 * The parameters result and result_len are filled with the decoded 218 * The parameters `payload` and `payload_len` are filled with
219 * binary data (if any). 219 * the decoded binary data (if any).
220 * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAME, but it can only 220 * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAME, but it can only
221 * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during 221 * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during
222 * the call of #MHD_websocket_stream_init() or 222 * the call of #MHD_websocket_stream_init() or
223 * #MHD_websocket_stream_init2(). 223 * #MHD_websocket_stream_init2().
224 */ 224 */
225 MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT = 0x12, 225 MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT = 0x12,
226 /** 226 /**
227 * #MHD_websocket_decode() has decoded the last text frame fragment. 227 * #MHD_websocket_decode() has decoded the next text frame fragment.
228 * The parameters result and result_len are filled with the decoded text 228 * The parameters `payload` and `payload_len` are filled with
229 * (if any). 229 * the decoded text (if any).
230 * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, but it appears 230 * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT, but it appears
231 * only for the last fragment of a series of fragments. 231 * only after the first and before the last fragment of a series of fragments.
232 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS 232 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
233 * during the call of #MHD_websocket_stream_init() or 233 * during the call of #MHD_websocket_stream_init() or
234 * #MHD_websocket_stream_init2(). 234 * #MHD_websocket_stream_init2().
235 */ 235 */
236 MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT = 0x21, 236 MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT = 0x21,
237 /** 237 /**
238 * #MHD_websocket_decode() has decoded the last binary frame fragment. 238 * #MHD_websocket_decode() has decoded the next binary frame fragment.
239 * The parameters result and result_len are filled with the decoded 239 * The parameters `payload` and `payload_len` are filled with
240 * binary data (if any). 240 * the decoded binary data (if any).
241 * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, but it appears 241 * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT, but it appears
242 * only for the last fragment of a series of fragments. 242 * only after the first and before the last fragment of a series of fragments.
243 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS 243 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
244 * during the call of #MHD_websocket_stream_init() or 244 * during the call of #MHD_websocket_stream_init() or
245 * #MHD_websocket_stream_init2(). 245 * #MHD_websocket_stream_init2().
246 */ 246 */
247 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT = 0x22, 247 MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT = 0x22,
248 /**
249 * #MHD_websocket_decode() has decoded the last text frame fragment.
250 * The parameters `payload` and `payload_len` are filled with
251 * the decoded text (if any).
252 * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT, but it appears
253 * only for the last fragment of a series of fragments.
254 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
255 * during the call of #MHD_websocket_stream_init() or
256 * #MHD_websocket_stream_init2().
257 */
258 MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT = 0x41,
259 /**
260 * #MHD_websocket_decode() has decoded the last binary frame fragment.
261 * The parameters `payload` and `payload_len` are filled with
262 * the decoded binary data (if any).
263 * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT, but it appears
264 * only for the last fragment of a series of fragments.
265 * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
266 * during the call of #MHD_websocket_stream_init() or
267 * #MHD_websocket_stream_init2().
268 */
269 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT = 0x42,
248 /** 270 /**
249 * The call failed and the stream is invalid now for decoding. 271 * The call failed and the stream is invalid now for decoding.
250 * You must close the websocket now using #MHD_upgrade_action() 272 * You must close the websocket now using #MHD_upgrade_action()
251 * with #MHD_UPGRADE_ACTION_CLOSE. 273 * with #MHD_UPGRADE_ACTION_CLOSE.
252 * You can send a close frame before closing. 274 * You may send a close frame before closing.
253 * This is only used by #MHD_websocket_decode() and happens 275 * This is only used by #MHD_websocket_decode() and happens
254 * if the stream contains errors (i. e. invalid byte data). 276 * if the stream contains errors (i. e. invalid byte data).
255 */ 277 */
@@ -259,10 +281,12 @@ enum MHD_WEBSOCKET_STATUS
259 * been marked invalid. 281 * been marked invalid.
260 * You must close the websocket now using #MHD_upgrade_action() 282 * You must close the websocket now using #MHD_upgrade_action()
261 * with #MHD_UPGRADE_ACTION_CLOSE. 283 * with #MHD_UPGRADE_ACTION_CLOSE.
262 * You can send a close frame before closing. 284 * You may send a close frame before closing.
263 * This is only used by #MHD_websocket_decode() and happens 285 * This is only used by #MHD_websocket_decode() and happens
264 * if you call #MDM_websocket_decode() again after is 286 * if you call #MDM_websocket_decode() again after
265 * has been invalidated. 287 * has been invalidated.
288 * You can call #MHD_websocket_stream_is_valid() at any time
289 * to check whether a stream is invalid or not.
266 */ 290 */
267 MHD_WEBSOCKET_STATUS_STREAM_BROKEN = -2, 291 MHD_WEBSOCKET_STATUS_STREAM_BROKEN = -2,
268 /** 292 /**
@@ -271,8 +295,8 @@ enum MHD_WEBSOCKET_STATUS
271 * possible later if enough memory is available. 295 * possible later if enough memory is available.
272 * This could happen while decoding if you received a too big data frame. 296 * This could happen while decoding if you received a too big data frame.
273 * You could try to specify max_payload_size during the call of 297 * You could try to specify max_payload_size during the call of
274 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2() then to 298 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2() to
275 * avoid this and close the frame instead. 299 * avoid this and close the websocket instead.
276 */ 300 */
277 MHD_WEBSOCKET_STATUS_MEMORY_ERROR = -3, 301 MHD_WEBSOCKET_STATUS_MEMORY_ERROR = -3,
278 /** 302 /**
@@ -286,28 +310,39 @@ enum MHD_WEBSOCKET_STATUS
286 * If you got this return code from #MHD_websocket_decode() then 310 * If you got this return code from #MHD_websocket_decode() then
287 * the stream becomes invalid and the websocket must be closed 311 * the stream becomes invalid and the websocket must be closed
288 * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE. 312 * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE.
289 * You can send a close frame before closing. 313 * You may send a close frame before closing.
290 * The maximum payload size is specified during the call of 314 * The maximum payload size is specified during the call of
291 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2(). 315 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2().
292 * This can also appear if you specified 0 as maximum payload size 316 * This can also appear if you specified 0 as maximum payload size
293 * when the message is greater than the maximum allocatable memory size 317 * when the message is greater than the maximum allocatable memory size
294 * (i. e. more than 4 GB on 32 bit systems). 318 * (i. e. more than 4 GiB on 32 bit systems).
295 * If you got this return code from #MHD_websocket_encode_close(), 319 * If you got this return code from #MHD_websocket_encode_close(),
296 * #MHD_websocket_encode_ping() or #MHD_websocket_encode_pong() then 320 * #MHD_websocket_encode_ping() or #MHD_websocket_encode_pong() then
297 * you passed to much payload data. The stream remains valid then. 321 * you passed to much payload data. The stream remains valid then.
298 */ 322 */
299 MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED = -5, 323 MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED = -5,
300 /** 324 /**
301 * An UTF-8 text is invalid. 325 * An UTF-8 sequence is invalid.
302 * If you got this return code from #MHD_websocket_decode() then 326 * If you got this return code from #MHD_websocket_decode() then
303 * the stream becomes invalid and you must close the websocket 327 * the stream becomes invalid and you must close the websocket
304 * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE. 328 * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE.
305 * You can send a close frame before closing. 329 * You may send a close frame before closing.
306 * If you got this from #MHD_websocket_encode_text() or 330 * If you got this from #MHD_websocket_encode_text() or
307 * #MHD_websocket_encode_close() then you passed invalid UTF-8 text. 331 * #MHD_websocket_encode_close() then you passed invalid UTF-8 text.
308 * The stream remains valid then. 332 * The stream remains valid then.
309 */ 333 */
310 MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR = -6 334 MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR = -6,
335 /**
336 * A check routine for the HTTP headers came to the conclusion that
337 * the header value isn't valid for a websocket handshake request.
338 * This value can only be returned from the following functions:
339 * * #MHD_websocket_check_http_version()
340 * * #MHD_websocket_check_connection_header()
341 * * #MHD_websocket_check_upgrade_header()
342 * * #MHD_websocket_check_version_header()
343 * * #MHD_websocket_create_accept_header()
344 */
345 MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER = -7
311}; 346};
312 347
313/** 348/**
@@ -332,22 +367,22 @@ enum MHD_WEBSOCKET_CLOSEREASON
332 * This value is used as placeholder for #MHD_websocket_encode_close() 367 * This value is used as placeholder for #MHD_websocket_encode_close()
333 * to tell that you don't want to specify any reason. 368 * to tell that you don't want to specify any reason.
334 * If you use this value then no reason text may be used. 369 * If you use this value then no reason text may be used.
335 * This value cannot a result of decoding, because this value 370 * This value cannot be a result of decoding, because this value
336 * is not a valid close reason for the WebSocket protocol. 371 * is not a valid close reason for the websocket protocol.
337 */ 372 */
338 MHD_WEBSOCKET_CLOSEREASON_NO_REASON = 0, 373 MHD_WEBSOCKET_CLOSEREASON_NO_REASON = 0,
339 /** 374 /**
340 * You close the websocket fulfilled its purpose and shall 375 * You close the websocket because it fulfilled its purpose and shall
341 * now be closed in a normal, planned way. 376 * now be closed in a normal, planned way.
342 */ 377 */
343 MHD_WEBSOCKET_CLOSEREASON_REGULAR = 1000, 378 MHD_WEBSOCKET_CLOSEREASON_REGULAR = 1000,
344 /** 379 /**
345 * You close the websocket because are shutting down the server or 380 * You close the websocket because you are shutting down the server or
346 * something similar. 381 * something similar.
347 */ 382 */
348 MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001, 383 MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001,
349 /** 384 /**
350 * You close the websocket because you a protocol error occurred 385 * You close the websocket because a protocol error occurred
351 * during decoding (i. e. invalid byte data). 386 * during decoding (i. e. invalid byte data).
352 */ 387 */
353 MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR = 1002, 388 MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR = 1002,
@@ -371,7 +406,8 @@ enum MHD_WEBSOCKET_CLOSEREASON
371 */ 406 */
372 MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED = 1008, 407 MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED = 1008,
373 /** 408 /**
374 * You close the websocket because you received a frame which is too big to process. 409 * You close the websocket because you received a frame which is too big
410 * to process.
375 * You can specify the maximum allowed payload size during the call of 411 * You can specify the maximum allowed payload size during the call of
376 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2(). 412 * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2().
377 */ 413 */
@@ -497,61 +533,170 @@ enum MHD_WEBSOCKET_VALIDITY
497 MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES = 2 533 MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES = 2
498}; 534};
499/** 535/**
500 * This method is called by many websocket 536 * This callback function is used internally by many websocket functions
501 * functions for allocating data. 537 * for allocating data.
502 * By default 'malloc' is used. 538 * By default `malloc()` is used.
503 * This can be used for operating systems like Windows 539 * You can use your own allocation function with
504 * where malloc, realloc and free are compiler dependent. 540 * #MHD_websocket_stream_init2() if you wish to.
541 * This can be useful for operating systems like Windows
542 * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
543 * You can call the associated `malloc()` callback of
544 * a websocket stream with #MHD_websocket_malloc().
505 * 545 *
506 * @param len new size 546 * @param buf_len buffer size in bytes
507 * @return allocated memory 547 * @return allocated memory
508 * @ingroup websocket 548 * @ingroup websocket
509 */ 549 */
510typedef void* 550typedef void*
511(*MHD_WebSocketMallocCallback) (size_t len); 551(*MHD_WebSocketMallocCallback) (size_t buf_len);
512/** 552/**
513 * This method is called by many websocket 553 * This callback function is used internally by many websocket
514 * functions for reallocating data. 554 * functions for reallocating data.
515 * By default 'realloc' is used. 555 * By default `realloc()` is used.
516 * This can be used for operating systems like Windows 556 * You can use your own reallocation function with
517 * where malloc, realloc and free are compiler dependent. 557 * #MHD_websocket_stream_init2() if you wish to.
558 * This can be useful for operating systems like Windows
559 * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
560 * You can call the associated `realloc()` callback of
561 * a websocket stream with #MHD_websocket_realloc().
518 * 562 *
519 * @param cls closure 563 * @param buf buffer
520 * @param len new size 564 * @param new_buf_len new buffer size in bytes
521 * @return reallocated memory 565 * @return reallocated memory
522 * @ingroup websocket 566 * @ingroup websocket
523 */ 567 */
524typedef void* 568typedef void*
525(*MHD_WebSocketReallocCallback) (void *cls, size_t len); 569(*MHD_WebSocketReallocCallback) (void *buf, size_t new_buf_len);
526/** 570/**
527 * This method is called by many websocket 571 * This callback function is used internally by many websocket
528 * functions for freeing data. 572 * functions for freeing data.
529 * By default 'free' is used. 573 * By default `free()` is used.
530 * This can be used for operating systems like Windows 574 * You can use your own free function with
531 * where malloc, realloc and free are compiler dependent. 575 * #MHD_websocket_stream_init2() if you wish to.
576 * This can be useful for operating systems like Windows
577 * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
578 * You can call the associated `free()` callback of
579 * a websocket stream with #MHD_websocket_free().
532 * 580 *
533 * @param cls closure 581 * @param buf buffer
534 * @ingroup websocket 582 * @ingroup websocket
535 */ 583 */
536typedef void 584typedef void
537(*MHD_WebSocketFreeCallback) (void *cls); 585(*MHD_WebSocketFreeCallback) (void *buf);
586/**
587 * This callback function is used for generating random numbers
588 * for masking payload data in client mode.
589 * If you use websockets in server mode with libmicrohttpd then
590 * you don't need a random number generator, because
591 * the server doesn't mask its outgoing messageses.
592 * However if you wish to use a websocket stream in client mode,
593 * you must pass this callback function to #MHD_websocket_stream_init2().
594 *
595 * @param cls closure specified in #MHD_websocket_stream_init2()
596 * @param buf buffer to fill with random values
597 * @param buf_len size of buffer in bytes
598 * @return The number of generated random bytes.
599 * Should usually equal to buf_len.
600 * @ingroup websocket
601 */
602typedef size_t
603(*MHD_WebSocketRandomNumberGenerator) (void *cls, void* buf, size_t buf_len);
604
605/**
606 * Checks the HTTP version of the incoming request.
607 * Websocket requests are only allowed for HTTP/1.1 or above.
608 *
609 * @param http_version The value of the 'version' parameter of your
610 * access_handler callback
611 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
612 * 0 means the HTTP version is correct for a websocket request,
613 * a value less than zero means that the HTTP version isn't
614 * valid for a websocket request.
615 * @ingroup websocket
616 */
617_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
618MHD_websocket_check_http_version (const char* http_version);
619
620/**
621 * Checks the value of the 'Connection' HTTP request header.
622 * Websocket requests require the token 'Upgrade' in
623 * the 'Connection' HTTP request header.
624 *
625 * @param connection_header The value of the 'Connection' request header.
626 * You can get this request header value by passing
627 * #MHD_HTTP_HEADER_CONNECTION to
628 * #MHD_lookup_connection_value().
629 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
630 * 0 means the 'Connection' request header is correct
631 * for a websocket request,
632 * a value less than zero means that the 'Connection' header isn't
633 * valid for a websocket request.
634 * @ingroup websocket
635 */
636_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
637MHD_websocket_check_connection_header (const char* connection_header);
638
639/**
640 * Checks the value of the 'Upgrade' HTTP request header.
641 * Websocket requests require the value 'websocket' in
642 * the 'Upgrade' HTTP request header.
643 *
644 * @param upgrade_header The value of the 'Upgrade' request header.
645 * You can get this request header value by passing
646 * #MHD_HTTP_HEADER_UPGRADE to
647 * #MHD_lookup_connection_value().
648 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
649 * 0 means the 'Upgrade' request header is correct
650 * for a websocket request,
651 * a value less than zero means that the 'Upgrade' header isn't
652 * valid for a websocket request.
653 * @ingroup websocket
654 */
655_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
656MHD_websocket_check_upgrade_header (const char* upgrade_header);
538 657
539/** 658/**
540 * Creates the response value for the incoming 'Sec-WebSocket-Key' header. 659 * Checks the value of the 'Sec-WebSocket-Version' HTTP request header.
541 * The generated value must be sent to the client as 'Sec-WebSocket-Accept' response header. 660 * Websocket requests require the value '13'
661 * in the 'Sec-WebSocket-Version' HTTP request header.
542 * 662 *
543 * @param sec_websocket_key The value of the 'Sec-WebSocket-Key' request header 663 * @param version_header The value of the 'Sec-WebSocket-Version'
664 * request header.
665 * You can get this request header value by passing
666 * #MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION to
667 * #MHD_lookup_connection_value().
668 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
669 * 0 means the 'Sec-WebSocket-Version' request header is correct
670 * for a websocket request,
671 * a value less than zero means that the 'Sec-WebSocket-Version'
672 * header isn't valid for a websocket request.
673 * @ingroup websocket
674 */
675_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
676MHD_websocket_check_version_header (const char* version_header);
677
678/**
679 * Creates the response value for the 'Sec-WebSocket-Key' HTTP request header.
680 * The generated value must be sent to the client
681 * as 'Sec-WebSocket-Accept' HTTP response header.
682 *
683 * @param sec_websocket_key The value of the 'Sec-WebSocket-Key'
684 * request header.
685 * You can get this request header value by passing
686 * #MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY to
687 * #MHD_lookup_connection_value().
544 * @param[out] sec_websocket_accept The response buffer, which will receive 688 * @param[out] sec_websocket_accept The response buffer, which will receive
545 * the generated 'Sec-WebSocket-Accept' header. 689 * the generated 'Sec-WebSocket-Accept' header.
546 * This buffer must be at least 29 bytes long and 690 * This buffer must be at least 29 bytes long and
547 * will contain a terminating NUL character. 691 * will contain the response value plus
692 * a terminating NUL on success.
548 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 693 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
549 * Typically 0 on success or less than 0 on errors. 694 * Typically 0 on success or less than 0 on errors.
550 * @ingroup websocket 695 * @ingroup websocket
551 */ 696 */
552_MHD_EXTERN int 697_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
553MHD_websocket_create_accept (const char*sec_websocket_key, 698MHD_websocket_create_accept_header (const char* sec_websocket_key,
554 char*sec_websocket_accept); 699 char* sec_websocket_accept);
555 700
556/** 701/**
557 * Creates a new websocket stream, used for decoding/encoding. 702 * Creates a new websocket stream, used for decoding/encoding.
@@ -559,13 +704,13 @@ MHD_websocket_create_accept (const char*sec_websocket_key,
559 * @param[out] ws The websocket stream 704 * @param[out] ws The websocket stream
560 * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values 705 * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values
561 * to modify the behavior of the websocket stream. 706 * to modify the behavior of the websocket stream.
562 * @param max_message_size The maximum size for incoming payload 707 * @param max_payload_size The maximum size for incoming payload
563 * data in bytes. Use 0 to allow each size. 708 * data in bytes. Use 0 to allow each size.
564 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 709 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
565 * Typically 0 on success or less than 0 on errors. 710 * Typically 0 on success or less than 0 on errors.
566 * @ingroup websocket 711 * @ingroup websocket
567 */ 712 */
568_MHD_EXTERN int 713_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
569MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, 714MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
570 int flags, 715 int flags,
571 size_t max_payload_size); 716 size_t max_payload_size);
@@ -573,26 +718,42 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
573/** 718/**
574 * Creates a new websocket stream, used for decoding/encoding, 719 * Creates a new websocket stream, used for decoding/encoding,
575 * but with custom memory functions for malloc, realloc and free. 720 * but with custom memory functions for malloc, realloc and free.
721 * Also a random number generator can be specified for client mode.
576 * 722 *
577 * @param[out] ws The websocket stream 723 * @param[out] ws The websocket stream
578 * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values 724 * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values
579 * to modify the behavior of the websocket stream. 725 * to modify the behavior of the websocket stream.
580 * @param max_message_size The maximum size for incoming payload 726 * @param max_payload_size The maximum size for incoming payload
581 * data in bytes. Use 0 to allow each size. 727 * data in bytes. Use 0 to allow each size.
582 * @param callback_malloc The callback function for 'malloc'. 728 * @param callback_malloc The callback function for `malloc()`.
583 * @param callback_realloc The callback function for 'realloc'. 729 * @param callback_realloc The callback function for `realloc()`.
584 * @param callback_free The callback function for 'free'. 730 * @param callback_free The callback function for `free()`.
731 * @param cls_rng A closure for the random number generator callback.
732 * This is only required when
733 * MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
734 * The given value is passed to
735 * the random number generator.
736 * May be NULL if not needed.
737 * Should be NULL when you are
738 * not using MHD_WEBSOCKET_FLAG_CLIENT.
739 * @param callback_rng A callback function for a
740 * secure random number generator.
741 * This is only required when
742 * MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
743 * Should be NULL otherwise.
585 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 744 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
586 * Typically 0 on success or less than 0 on errors. 745 * Typically 0 on success or less than 0 on errors.
587 * @ingroup websocket 746 * @ingroup websocket
588 */ 747 */
589_MHD_EXTERN int 748_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
590MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, 749MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
591 int flags, 750 int flags,
592 size_t max_payload_size, 751 size_t max_payload_size,
593 MHD_WebSocketMallocCallback callback_malloc, 752 MHD_WebSocketMallocCallback callback_malloc,
594 MHD_WebSocketReallocCallback callback_realloc, 753 MHD_WebSocketReallocCallback callback_realloc,
595 MHD_WebSocketFreeCallback callback_free); 754 MHD_WebSocketFreeCallback callback_free,
755 void* cls_rng,
756 MHD_WebSocketRandomNumberGenerator callback_rng);
596 757
597/** 758/**
598 * Frees a websocket stream 759 * Frees a websocket stream
@@ -602,7 +763,7 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
602 * Typically 0 on success or less than 0 on errors. 763 * Typically 0 on success or less than 0 on errors.
603 * @ingroup websocket 764 * @ingroup websocket
604 */ 765 */
605_MHD_EXTERN int 766_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
606MHD_websocket_stream_free (struct MHD_WebSocketStream*ws); 767MHD_websocket_stream_free (struct MHD_WebSocketStream*ws);
607 768
608/** 769/**
@@ -615,7 +776,7 @@ MHD_websocket_stream_free (struct MHD_WebSocketStream*ws);
615 * Typically 0 on success or less than 0 on errors. 776 * Typically 0 on success or less than 0 on errors.
616 * @ingroup websocket 777 * @ingroup websocket
617 */ 778 */
618_MHD_EXTERN int 779_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
619MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws); 780MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws);
620 781
621/** 782/**
@@ -627,11 +788,11 @@ MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws);
627 * @return A value of `enum MHD_WEBSOCKET_VALIDITY`. 788 * @return A value of `enum MHD_WEBSOCKET_VALIDITY`.
628 * @ingroup websocket 789 * @ingroup websocket
629 */ 790 */
630_MHD_EXTERN int 791_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
631MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws); 792MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
632 793
633/** 794/**
634 * Decodes a byte sequence via this websocket stream. 795 * Decodes a byte sequence for a websocket stream.
635 * Decoding is done until either a frame is complete or 796 * Decoding is done until either a frame is complete or
636 * the end of the byte sequence is reached. 797 * the end of the byte sequence is reached.
637 * 798 *
@@ -644,11 +805,11 @@ MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
644 * than @a streambuf_len when a frame is decoded 805 * than @a streambuf_len when a frame is decoded
645 * before the end of the buffer is reached. 806 * before the end of the buffer is reached.
646 * The remaining bytes of @a buf must be passed 807 * The remaining bytes of @a buf must be passed
647 * in the following decoding. 808 * to the next call of this function.
648 * @param[out] payload This variable receives a buffer with the decoded 809 * @param[out] payload Pointer to a variable, which receives a buffer
649 * payload data. 810 * with the decoded payload data.
650 * If no decoded data is available this is NULL. 811 * If no decoded data is available this is NULL.
651 * When this variable is not NULL then 812 * When the returned value is not NULL then
652 * the buffer contains always @a payload_len bytes plus 813 * the buffer contains always @a payload_len bytes plus
653 * one terminating NUL character. 814 * one terminating NUL character.
654 * The caller must free this buffer 815 * The caller must free this buffer
@@ -657,11 +818,11 @@ MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
657 * #MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR 818 * #MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
658 * upon creation of this websocket stream and 819 * upon creation of this websocket stream and
659 * a decoding error occurred 820 * a decoding error occurred
660 * (return value less than 0), then this 821 * (function return value less than 0), then this
661 * buffer contains a generated close frame 822 * buffer contains a generated close frame
662 * which must be sent via the socket to the recipient. 823 * which must be sent via the socket to the recipient.
663 * If you passed the flag #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS 824 * If you passed the flag #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
664 * upon creation of this websocket stream then 825 * upon creation of the websocket stream then
665 * this payload may only be a part of the complete message. 826 * this payload may only be a part of the complete message.
666 * Only complete UTF-8 sequences are returned 827 * Only complete UTF-8 sequences are returned
667 * for fragmented text frames. 828 * for fragmented text frames.
@@ -670,58 +831,58 @@ MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws);
670 * @param[out] payload_len The length of the result payload buffer in bytes. 831 * @param[out] payload_len The length of the result payload buffer in bytes.
671 * 832 *
672 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 833 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
673 * This is greater than 0 if a frame has is complete, equal to 0 if more data 834 * This is greater than 0 if a frame has is complete,
674 * is needed an less than 0 on errors. 835 * equal to 0 if more data is needed an less than 0 on errors.
675 * @ingroup websocket 836 * @ingroup websocket
676 */ 837 */
677_MHD_EXTERN int 838_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
678MHD_websocket_decode (struct MHD_WebSocketStream*ws, 839MHD_websocket_decode (struct MHD_WebSocketStream* ws,
679 const char*streambuf, 840 const char* streambuf,
680 size_t streambuf_len, 841 size_t streambuf_len,
681 size_t*streambuf_read_len, 842 size_t* streambuf_read_len,
682 char**payload, 843 char** payload,
683 size_t*payload_len); 844 size_t* payload_len);
684 845
685/** 846/**
686 * Splits the payload of of a decoded close frame. 847 * Splits the payload of a decoded close frame.
687 * 848 *
688 * @param payload The payload of the close frame. 849 * @param payload The payload of the close frame.
689 * This parameter may be NULL if @a payload_len is 0. 850 * This parameter may only be NULL if @a payload_len is 0.
690 * @param payload_len The length of @a payload. 851 * @param payload_len The length of @a payload.
691 * @param[out] reason_code The numeric close reason. 852 * @param[out] reason_code The numeric close reason.
692 * If there was no close reason, this is 853 * If there was no close reason, this is
693 * #MHD_WEBSOCKET_CLOSEREASON_NO_REASON. 854 * #MHD_WEBSOCKET_CLOSEREASON_NO_REASON.
694 * Compare with `enum MHD_WEBSOCKET_CLOSEREASON`. 855 * Compare with `enum MHD_WEBSOCKET_CLOSEREASON`.
695 * This parameter is optional and can be NULL. 856 * This parameter is optional and may be NULL.
696 * @param[out] reason_utf8 The literal close reason. 857 * @param[out] reason_utf8 The literal close reason.
697 * If there was no literal close reason, this is NULL. 858 * If there was no literal close reason, this is NULL.
698 * This parameter is optional and can be NULL. 859 * This parameter is optional and may be NULL.
699 * Please note that no memory is allocated 860 * Please note that no memory is allocated
700 * in this function. 861 * in this function.
701 * If not NULL the returned value of this parameter 862 * If not NULL the returned value of this parameter
702 * points to a position in the specified @a payload. 863 * points to a position in the specified @a payload.
703 * @param[out] reason_utf8_len The length of the literal close reason. 864 * @param[out] reason_utf8_len The length of the literal close reason.
704 * If there was no literal close reason, this is 0. 865 * If there was no literal close reason, this is 0.
705 * This parameter is optional and can be NULL. 866 * This parameter is optional and may be NULL.
706 * 867 *
707 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 868 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
708 * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success 869 * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
709 * or a value less than 0 on errors. 870 * or a value less than 0 on errors.
710 * @ingroup websocket 871 * @ingroup websocket
711 */ 872 */
712_MHD_EXTERN int 873_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
713MHD_websocket_split_close_reason (const char*payload, 874MHD_websocket_split_close_reason (const char* payload,
714 size_t payload_len, 875 size_t payload_len,
715 unsigned short*reason_code, 876 unsigned short* reason_code,
716 const char**reason_utf8, 877 const char** reason_utf8,
717 size_t*reason_utf8_len); 878 size_t* reason_utf8_len);
718 879
719/** 880/**
720 * Encodes an UTF-8 encoded text into websocket text frame. 881 * Encodes an UTF-8 encoded text into websocket text frame.
721 * 882 *
722 * @param ws The websocket stream. 883 * @param ws The websocket stream.
723 * @param payload_utf8 The UTF-8 encoded text to send. 884 * @param payload_utf8 The UTF-8 encoded text to send.
724 * This can be NULL if payload_utf8_len is 0. 885 * This may be NULL if payload_utf8_len is 0.
725 * @param payload_utf8_len The length of the UTF-8 encoded text in bytes. 886 * @param payload_utf8_len The length of the UTF-8 encoded text in bytes.
726 * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION` 887 * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION`
727 * to specify the fragmentation behavior. 888 * to specify the fragmentation behavior.
@@ -735,11 +896,11 @@ MHD_websocket_split_close_reason (const char*payload,
735 * The caller must free this buffer using #MHD_websocket_free(). 896 * The caller must free this buffer using #MHD_websocket_free().
736 * @param[out] frame_len The length of the encoded frame in bytes. 897 * @param[out] frame_len The length of the encoded frame in bytes.
737 * @param[out] utf8_step This parameter is required for fragmentation and 898 * @param[out] utf8_step This parameter is required for fragmentation and
738 * can be NULL if no fragmentation is used. 899 * should be NULL if no fragmentation is used.
739 * It contains information about the last encoded 900 * It contains information about the last encoded
740 * UTF-8 sequence and is required to continue a previous 901 * UTF-8 sequence and is required to continue a previous
741 * UTF-8 sequence when fragmentation is used. 902 * UTF-8 sequence when fragmentation is used.
742 * The `enum MHD_WEBSOCKET_UTF8STEP` is for this. 903 * `enum MHD_WEBSOCKET_UTF8STEP` is for this value.
743 * If you start a new fragment using 904 * If you start a new fragment using
744 * MHD_WEBSOCKET_FRAGMENTATION_NONE or 905 * MHD_WEBSOCKET_FRAGMENTATION_NONE or
745 * MHD_WEBSOCKET_FRAGMENTATION_FIRST the value 906 * MHD_WEBSOCKET_FRAGMENTATION_FIRST the value
@@ -751,17 +912,17 @@ MHD_websocket_split_close_reason (const char*payload,
751 * or a value less than 0 on errors. 912 * or a value less than 0 on errors.
752 * @ingroup websocket 913 * @ingroup websocket
753 */ 914 */
754_MHD_EXTERN int 915_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
755MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, 916MHD_websocket_encode_text (struct MHD_WebSocketStream* ws,
756 const char*payload_utf8, 917 const char* payload_utf8,
757 size_t payload_utf8_len, 918 size_t payload_utf8_len,
758 int fragmentation, 919 int fragmentation,
759 char**frame, 920 char** frame,
760 size_t*frame_len, 921 size_t* frame_len,
761 int*utf8_step); 922 int* utf8_step);
762 923
763/** 924/**
764 * Encodes a binary data into websocket binary frame. 925 * Encodes binary data into websocket binary frame.
765 * 926 *
766 * @param ws The websocket stream. 927 * @param ws The websocket stream.
767 * @param payload The binary data to send. 928 * @param payload The binary data to send.
@@ -786,13 +947,13 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
786 * or a value less than 0 on errors. 947 * or a value less than 0 on errors.
787 * @ingroup websocket 948 * @ingroup websocket
788 */ 949 */
789_MHD_EXTERN int 950_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
790MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, 951MHD_websocket_encode_binary (struct MHD_WebSocketStream* ws,
791 const char*payload, 952 const char* payload,
792 size_t payload_len, 953 size_t payload_len,
793 int fragmentation, 954 int fragmentation,
794 char**frame, 955 char** frame,
795 size_t*frame_len); 956 size_t* frame_len);
796 957
797/** 958/**
798 * Encodes a websocket ping frame 959 * Encodes a websocket ping frame
@@ -815,18 +976,18 @@ MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
815 * or a value less than 0 on errors. 976 * or a value less than 0 on errors.
816 * @ingroup websocket 977 * @ingroup websocket
817 */ 978 */
818_MHD_EXTERN int 979_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
819MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, 980MHD_websocket_encode_ping (struct MHD_WebSocketStream* ws,
820 const char*payload, 981 const char* payload,
821 size_t payload_len, 982 size_t payload_len,
822 char**frame, 983 char** frame,
823 size_t*frame_len); 984 size_t* frame_len);
824 985
825/** 986/**
826 * Encodes a websocket pong frame 987 * Encodes a websocket pong frame
827 * 988 *
828 * @param ws The websocket stream. 989 * @param ws The websocket stream.
829 * @param payload The binary pong payload data, which is typically 990 * @param payload The binary pong payload data, which should be
830 * the decoded payload from the received ping frame. 991 * the decoded payload from the received ping frame.
831 * This may be NULL if @a payload_len is 0. 992 * This may be NULL if @a payload_len is 0.
832 * @param payload_len The length of the payload data in bytes. 993 * @param payload_len The length of the payload data in bytes.
@@ -848,12 +1009,12 @@ MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
848 * or a value less than 0 on errors. 1009 * or a value less than 0 on errors.
849 * @ingroup websocket 1010 * @ingroup websocket
850 */ 1011 */
851_MHD_EXTERN int 1012_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
852MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, 1013MHD_websocket_encode_pong (struct MHD_WebSocketStream* ws,
853 const char*payload, 1014 const char* payload,
854 size_t payload_len, 1015 size_t payload_len,
855 char**frame, 1016 char** frame,
856 size_t*frame_len); 1017 size_t* frame_len);
857 1018
858/** 1019/**
859 * Encodes a websocket close frame 1020 * Encodes a websocket close frame
@@ -890,58 +1051,35 @@ MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
890 * or a value less than 0 on errors. 1051 * or a value less than 0 on errors.
891 * @ingroup websocket 1052 * @ingroup websocket
892 */ 1053 */
893_MHD_EXTERN int 1054_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
894MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, 1055MHD_websocket_encode_close (struct MHD_WebSocketStream* ws,
895 unsigned short reason_code, 1056 unsigned short reason_code,
896 const char*reason_utf8, 1057 const char* reason_utf8,
897 size_t reason_utf8_len, 1058 size_t reason_utf8_len,
898 char**frame, 1059 char** frame,
899 size_t*frame_len); 1060 size_t* frame_len);
900
901/**
902 * Sets the seed for the random number generated used for
903 * the generation of masked frames (this is only used for client websockets).
904 * This seed is used for all websocket streams.
905 * Internally `srand()` is called.
906 * Please note that on some situations
907 * (where `rand()` and `srand()` are shared between your program
908 * and this library) this could cause unwanted results in your program if
909 * your program relies on a specific seed.
910 *
911 * @param seed The seed used for the initialization of
912 * the pseudo random number generator.
913 * Typically `time(NULL)` is used here to
914 * generate a seed.
915 *
916 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
917 * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
918 * or a value less than 0 on errors.
919 * @ingroup websocket
920 */
921_MHD_EXTERN int
922MHD_websocket_srand (unsigned long seed);
923 1061
924/** 1062/**
925 * Allocates memory with the associated 'malloc' function 1063 * Allocates memory with the associated 'malloc' function
926 * of the websocket stream 1064 * of the websocket stream
927 * 1065 *
928 * @param ws The websocket stream. 1066 * @param ws The websocket stream.
929 * @param len The length of the memory to allocate in bytes 1067 * @param buf_len The length of the memory to allocate in bytes
930 * 1068 *
931 * @return The allocated memory on success or NULL on failure. 1069 * @return The allocated memory on success or NULL on failure.
932 * @ingroup websocket 1070 * @ingroup websocket
933 */ 1071 */
934_MHD_EXTERN void* 1072_MHD_EXTERN void*
935MHD_websocket_malloc (struct MHD_WebSocketStream*ws, 1073MHD_websocket_malloc (struct MHD_WebSocketStream* ws,
936 size_t len); 1074 size_t buf_len);
937 1075
938/** 1076/**
939 * Reallocates memory with the associated 'realloc' function 1077 * Reallocates memory with the associated 'realloc' function
940 * of the websocket stream 1078 * of the websocket stream
941 * 1079 *
942 * @param ws The websocket stream. 1080 * @param ws The websocket stream.
943 * @param cls The previously allocated memory or NULL 1081 * @param buf The previously allocated memory or NULL
944 * @param len The new length of the memory in bytes 1082 * @param new_buf_len The new length of the memory in bytes
945 * 1083 *
946 * @return The allocated memory on success or NULL on failure. 1084 * @return The allocated memory on success or NULL on failure.
947 * If NULL is returned the previously allocated buffer 1085 * If NULL is returned the previously allocated buffer
@@ -949,16 +1087,16 @@ MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
949 * @ingroup websocket 1087 * @ingroup websocket
950 */ 1088 */
951_MHD_EXTERN void* 1089_MHD_EXTERN void*
952MHD_websocket_realloc (struct MHD_WebSocketStream*ws, 1090MHD_websocket_realloc (struct MHD_WebSocketStream* ws,
953 void*cls, 1091 void* buf,
954 size_t len); 1092 size_t new_buf_len);
955 1093
956/** 1094/**
957 * Frees memory with the associated 'free' function 1095 * Frees memory with the associated 'free' function
958 * of the websocket stream 1096 * of the websocket stream
959 * 1097 *
960 * @param ws The websocket stream. 1098 * @param ws The websocket stream.
961 * @param cls The previously allocated memory or NULL 1099 * @param buf The previously allocated memory or NULL
962 * 1100 *
963 * @return A value of `enum MHD_WEBSOCKET_STATUS`. 1101 * @return A value of `enum MHD_WEBSOCKET_STATUS`.
964 * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success 1102 * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success
@@ -966,8 +1104,8 @@ MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
966 * @ingroup websocket 1104 * @ingroup websocket
967 */ 1105 */
968_MHD_EXTERN int 1106_MHD_EXTERN int
969MHD_websocket_free (struct MHD_WebSocketStream*ws, 1107MHD_websocket_free (struct MHD_WebSocketStream* ws,
970 void*cls); 1108 void* buf);
971 1109
972#if 0 /* keep Emacsens' auto-indent happy */ 1110#if 0 /* keep Emacsens' auto-indent happy */
973{ 1111{
diff --git a/src/microhttpd_ws/mhd_websocket.c b/src/microhttpd_ws/mhd_websocket.c
index ea1a5bda..da787f7d 100644
--- a/src/microhttpd_ws/mhd_websocket.c
+++ b/src/microhttpd_ws/mhd_websocket.c
@@ -36,6 +36,10 @@ struct MHD_WebSocketStream
36 MHD_WebSocketReallocCallback realloc; 36 MHD_WebSocketReallocCallback realloc;
37 /* The function pointer to free for payload (can be used to use different memory management) */ 37 /* The function pointer to free for payload (can be used to use different memory management) */
38 MHD_WebSocketFreeCallback free; 38 MHD_WebSocketFreeCallback free;
39 /* A closure for the random number generator (only used for client mode; usually not required) */
40 void* cls_rng;
41 /* The random number generator (only used for client mode; usually not required) */
42 MHD_WebSocketRandomNumberGenerator rng;
39 /* The flags specified upon initialization. It may alter the behavior of decoding/encoding */ 43 /* The flags specified upon initialization. It may alter the behavior of decoding/encoding */
40 int flags; 44 int flags;
41 /* The current step for the decoder. 0 means start of a frame. */ 45 /* The current step for the decoder. 0 means start of a frame. */
@@ -122,13 +126,6 @@ enum MHD_WebSocket_UTF8Result
122 MHD_WebSocket_UTF8Result_Incomplete = 2 126 MHD_WebSocket_UTF8Result_Incomplete = 2
123}; 127};
124 128
125#define htonll(x) \
126 ((1 == htonl (1)) ? (x) : ((uint64_t) htonl ((x) & 0xFFFFFFFF) << 32) \
127 | htonl ((x) >> 32))
128#define ntohll(x) \
129 ((1 == ntohl (1)) ? (x) : ((uint64_t) ntohl ((x) & 0xFFFFFFFF) << 32) \
130 | ntohl ((x) >> 32))
131
132static void 129static void
133MHD_websocket_copy_payload (char*dst, 130MHD_websocket_copy_payload (char*dst,
134 const char*src, 131 const char*src,
@@ -142,12 +139,12 @@ MHD_websocket_check_utf8 (const char*buf,
142 int*utf8_step, 139 int*utf8_step,
143 size_t*buf_offset); 140 size_t*buf_offset);
144 141
145static int 142static enum MHD_WEBSOCKET_STATUS
146MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, 143MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
147 char**payload, 144 char**payload,
148 size_t*payload_len); 145 size_t*payload_len);
149 146
150static int 147static enum MHD_WEBSOCKET_STATUS
151MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, 148MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
152 char**payload, 149 char**payload,
153 size_t*payload_len); 150 size_t*payload_len);
@@ -158,7 +155,7 @@ static char
158MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws, 155MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
159 size_t payload_len); 156 size_t payload_len);
160 157
161static int 158static enum MHD_WEBSOCKET_STATUS
162MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, 159MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
163 const char*payload, 160 const char*payload,
164 size_t payload_len, 161 size_t payload_len,
@@ -167,7 +164,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
167 size_t*frame_len, 164 size_t*frame_len,
168 char opcode); 165 char opcode);
169 166
170static int 167static enum MHD_WEBSOCKET_STATUS
171MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, 168MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
172 const char*payload, 169 const char*payload,
173 size_t payload_len, 170 size_t payload_len,
@@ -176,35 +173,338 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
176 char opcode); 173 char opcode);
177 174
178static uint32_t 175static uint32_t
179MHD_websocket_generate_mask (); 176MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws);
177
178static uint16_t
179MHD_htons (uint16_t value);
180
181static uint64_t
182MHD_htonll (uint64_t value);
183
184
185/**
186 * Checks whether the HTTP version is 1.1 or above.
187 */
188_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
189MHD_websocket_check_http_version (const char* http_version)
190{
191 /* validate parameters */
192 if (NULL == http_version)
193 {
194 /* Like with the other check routines, */
195 /* NULL is threated as "value not given" and not as parameter error */
196 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
197 }
198
199 /* Check whether the version has a valid format */
200 /* RFC 1945 3.1: The format must be "HTTP/x.x" where x is */
201 /* any digit and must appear at least once */
202 if ('H' != http_version[0] ||
203 'T' != http_version[1] ||
204 'T' != http_version[2] ||
205 'P' != http_version[3] ||
206 '/' != http_version[4])
207 {
208 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
209 }
210
211 /* Find the major and minor part of the version */
212 /* RFC 1945 3.1: Both numbers must be threated as separate integers. */
213 /* Leading zeros must be ignored and both integers may have multiple digits */
214 const char* major = NULL;
215 const char* dot = NULL;
216 size_t i = 5;
217 for (;;)
218 {
219 char c = http_version[i];
220 if ('0' <= c && '9' >= c)
221 {
222 if (NULL == major ||
223 (http_version + i == major + 1 && '0' == *major) )
224 {
225 major = http_version + i;
226 }
227 ++i;
228 }
229 else if ('.' == http_version[i])
230 {
231 dot = http_version + i;
232 ++i;
233 break;
234 }
235 else
236 {
237 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
238 }
239 }
240 const char* minor = NULL;
241 const char* end = NULL;
242 for (;;)
243 {
244 char c = http_version[i];
245 if ('0' <= c && '9' >= c)
246 {
247 if (NULL == minor ||
248 (http_version + i == minor + 1 && '0' == *minor) )
249 {
250 minor = http_version + i;
251 }
252 ++i;
253 }
254 else if (0 == c)
255 {
256 end = http_version + i;
257 break;
258 }
259 else
260 {
261 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
262 }
263 }
264 if (NULL == major || NULL == dot || NULL == minor || NULL == end)
265 {
266 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
267 }
268 if (2 <= dot - major || '2' <= *major ||
269 ('1' == *major && (2 <= end - minor || '1' <= *minor)) )
270 {
271 return MHD_WEBSOCKET_STATUS_OK;
272 }
273
274 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
275}
276
277
278/**
279 * Checks whether the "Connection" request header has the 'Upgrade' token.
280 */
281_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
282MHD_websocket_check_connection_header (const char* connection_header)
283{
284 /* validate parameters */
285 if (NULL == connection_header)
286 {
287 /* To be compatible with the return value */
288 /* of MHD_lookup_connection_value, */
289 /* NULL is threated as "value not given" and not as parameter error */
290 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
291 }
292
293 /* Check whether the Connection includes an Upgrade token */
294 /* RFC 7230 6.1: Multiple tokens may appear. */
295 /* RFC 7230 3.2.6: Tokens are comma separated */
296 const char* token_start = NULL;
297 const char* token_end = NULL;
298 for(size_t i = 0; ; ++i)
299 {
300 char c = connection_header[i];
301
302 /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
303 /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
304 /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
305 /* DIGIT / ALPHA */
306 if ('!' == c || '#' == c || '$' == c || '%' == c ||
307 '&' == c || '\'' == c || '*' == c ||
308 '+' == c || '-' == c || '.' == c || '^' == c ||
309 '_' == c || '`' == c || '|' == c || '~' == c ||
310 ('0' <= c && '9' >= c) ||
311 ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c) )
312 {
313 /* This is a valid token character */
314 if (NULL == token_start)
315 {
316 token_start = connection_header + i;
317 }
318 token_end = connection_header + i + 1;
319 }
320 else if (' ' == c || '\t' == c)
321 {
322 /* White-spaces around tokens will be ignored */
323 }
324 else if (',' == c || 0 == c)
325 {
326 /* Check the token (case-insensitive) */
327 if (NULL != token_start)
328 {
329 if ( 7 == (token_end - token_start) )
330 {
331 if ( ('U' == token_start[0] || 'u' == token_start[0]) &&
332 ('P' == token_start[1] || 'p' == token_start[1]) &&
333 ('G' == token_start[2] || 'g' == token_start[2]) &&
334 ('R' == token_start[3] || 'r' == token_start[3]) &&
335 ('A' == token_start[4] || 'a' == token_start[4]) &&
336 ('D' == token_start[5] || 'd' == token_start[5]) &&
337 ('E' == token_start[6] || 'e' == token_start[6]) )
338 {
339 /* The token equals to "Upgrade" */
340 return MHD_WEBSOCKET_STATUS_OK;
341 }
342 }
343 }
344 if (0 == c)
345 {
346 break;
347 }
348 token_start = NULL;
349 token_end = NULL;
350 }
351 else
352 {
353 /* RFC 7230 3.2.6: Other characters are not allowed */
354 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
355 }
356 }
357 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
358}
359
360
361/**
362 * Checks whether the "Upgrade" request header has the "websocket" keyword.
363 */
364_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
365MHD_websocket_check_upgrade_header (const char* upgrade_header)
366{
367 /* validate parameters */
368 if (NULL == upgrade_header)
369 {
370 /* To be compatible with the return value */
371 /* of MHD_lookup_connection_value, */
372 /* NULL is threated as "value not given" and not as parameter error */
373 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
374 }
375
376 /* Check whether the Connection includes an Upgrade token */
377 /* RFC 7230 6.1: Multiple tokens may appear. */
378 /* RFC 7230 3.2.6: Tokens are comma separated */
379 const char* keyword_start = NULL;
380 const char* keyword_end = NULL;
381 for(size_t i = 0; ; ++i)
382 {
383 char c = upgrade_header[i];
384
385 /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
386 /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
387 /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
388 /* DIGIT / ALPHA */
389 /* We also allow "/" here as the sub-delimiter for the protocol version */
390 if ('!' == c || '#' == c || '$' == c || '%' == c ||
391 '&' == c || '\'' == c || '*' == c ||
392 '+' == c || '-' == c || '.' == c || '^' == c ||
393 '_' == c || '`' == c || '|' == c || '~' == c ||
394 '/' == c ||
395 ('0' <= c && '9' >= c) ||
396 ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c) )
397 {
398 /* This is a valid token character */
399 if (NULL == keyword_start)
400 {
401 keyword_start = upgrade_header + i;
402 }
403 keyword_end = upgrade_header + i + 1;
404 }
405 else if (' ' == c || '\t' == c)
406 {
407 /* White-spaces around tokens will be ignored */
408 }
409 else if (',' == c || 0 == c)
410 {
411 /* Check the token (case-insensitive) */
412 if (NULL != keyword_start)
413 {
414 if ( 9 == (keyword_end - keyword_start) )
415 {
416 if ( ('W' == keyword_start[0] || 'w' == keyword_start[0]) &&
417 ('E' == keyword_start[1] || 'e' == keyword_start[1]) &&
418 ('B' == keyword_start[2] || 'b' == keyword_start[2]) &&
419 ('S' == keyword_start[3] || 's' == keyword_start[3]) &&
420 ('O' == keyword_start[4] || 'o' == keyword_start[4]) &&
421 ('C' == keyword_start[5] || 'c' == keyword_start[5]) &&
422 ('K' == keyword_start[6] || 'k' == keyword_start[6]) &&
423 ('E' == keyword_start[7] || 'e' == keyword_start[7]) &&
424 ('T' == keyword_start[8] || 't' == keyword_start[8]) )
425 {
426 /* The keyword equals to "websocket" */
427 return MHD_WEBSOCKET_STATUS_OK;
428 }
429 }
430 }
431 if (0 == c)
432 {
433 break;
434 }
435 keyword_start = NULL;
436 keyword_end = NULL;
437 }
438 else
439 {
440 /* RFC 7230 3.2.6: Other characters are not allowed */
441 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
442 }
443 }
444 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
445}
446
447
448/**
449 * Checks whether the "Sec-WebSocket-Version" request header
450 * equals to "13"
451 */
452_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
453MHD_websocket_check_version_header (const char* version_header)
454{
455 /* validate parameters */
456 if (NULL == version_header)
457 {
458 /* To be compatible with the return value */
459 /* of MHD_lookup_connection_value, */
460 /* NULL is threated as "value not given" and not as parameter error */
461 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
462 }
463
464 if ('1' == version_header[0] &&
465 '3' == version_header[1] &&
466 0 == version_header[2])
467 {
468 /* The version equals to "13" */
469 return MHD_WEBSOCKET_STATUS_OK;
470 }
471 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
472}
473
180 474
181/** 475/**
182 * Creates the response for the Sec-WebSocket-Accept header 476 * Creates the response for the Sec-WebSocket-Accept header
183 */ 477 */
184_MHD_EXTERN int 478_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
185MHD_websocket_create_accept (const char*sec_websocket_key, 479MHD_websocket_create_accept_header (const char*sec_websocket_key,
186 char*sec_websocket_accept) 480 char*sec_websocket_accept)
187{ 481{
188 /* initialize output variables for errors cases */ 482 /* initialize output variables for errors cases */
189 if (NULL != sec_websocket_accept) 483 if (NULL != sec_websocket_accept)
190 *sec_websocket_accept = 0; 484 *sec_websocket_accept = 0;
191 485
192 /* validate parameters */ 486 /* validate parameters */
193 if ((NULL == sec_websocket_key) || 487 if (NULL == sec_websocket_accept)
194 (NULL == sec_websocket_accept) )
195 { 488 {
196 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; 489 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
197 } 490 }
491 if (NULL == sec_websocket_key)
492 {
493 /* NULL is not a parameter error, */
494 /* because MHD_lookup_connection_value returns NULL */
495 /* if the header wasn't found */
496 return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
497 }
198 498
199 /* build SHA1 hash of the given key and the UUID appended */ 499 /* build SHA1 hash of the given key and the UUID appended */
200 char sha1[20]; 500 char sha1[20];
201 const char*suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 501 const char*suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
202 int length = (int) strlen (sec_websocket_key); 502 int length = (int) strlen (sec_websocket_key);
203 struct sha1_ctx ctx; 503 struct sha1_ctx ctx;
204 sha1_init_ctx (&ctx); 504 MHD_SHA1_init (&ctx);
205 sha1_process_bytes (sec_websocket_key, length, &ctx); 505 MHD_SHA1_update (&ctx, (const uint8_t*) sec_websocket_key, length);
206 sha1_process_bytes (suffix, 36, &ctx); 506 MHD_SHA1_update (&ctx, (const uint8_t*) suffix, 36);
207 sha1_finish_ctx (&ctx, sha1); 507 MHD_SHA1_finish (&ctx, (uint8_t*) sha1);
208 508
209 /* base64 encode that SHA1 hash */ 509 /* base64 encode that SHA1 hash */
210 /* (simple algorithm here; SHA1 has always 20 bytes, */ 510 /* (simple algorithm here; SHA1 has always 20 bytes, */
@@ -234,7 +534,7 @@ MHD_websocket_create_accept (const char*sec_websocket_key,
234/** 534/**
235 * Initializes a new websocket stream 535 * Initializes a new websocket stream
236 */ 536 */
237_MHD_EXTERN int 537_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
238MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, 538MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
239 int flags, 539 int flags,
240 size_t max_payload_size) 540 size_t max_payload_size)
@@ -244,7 +544,9 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
244 max_payload_size, 544 max_payload_size,
245 malloc, 545 malloc,
246 realloc, 546 realloc,
247 free); 547 free,
548 NULL,
549 NULL);
248} 550}
249 551
250 552
@@ -252,13 +554,15 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
252 * Initializes a new websocket stream with 554 * Initializes a new websocket stream with
253 * additional parameters for allocation functions 555 * additional parameters for allocation functions
254 */ 556 */
255_MHD_EXTERN int 557_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
256MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, 558MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
257 int flags, 559 int flags,
258 size_t max_payload_size, 560 size_t max_payload_size,
259 MHD_WebSocketMallocCallback callback_malloc, 561 MHD_WebSocketMallocCallback callback_malloc,
260 MHD_WebSocketReallocCallback callback_realloc, 562 MHD_WebSocketReallocCallback callback_realloc,
261 MHD_WebSocketFreeCallback callback_free) 563 MHD_WebSocketFreeCallback callback_free,
564 void* cls_rng,
565 MHD_WebSocketRandomNumberGenerator callback_rng)
262{ 566{
263 /* initialize output variables for errors cases */ 567 /* initialize output variables for errors cases */
264 if (NULL != ws) 568 if (NULL != ws)
@@ -270,7 +574,9 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
270 ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) || 574 ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) ||
271 (NULL == callback_malloc) || 575 (NULL == callback_malloc) ||
272 (NULL == callback_realloc) || 576 (NULL == callback_realloc) ||
273 (NULL == callback_free) ) 577 (NULL == callback_free) ||
578 ((0 != (flags & MHD_WEBSOCKET_FLAG_CLIENT)) &&
579 (NULL == callback_rng)))
274 { 580 {
275 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; 581 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
276 } 582 }
@@ -288,6 +594,8 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
288 ws_->malloc = callback_malloc; 594 ws_->malloc = callback_malloc;
289 ws_->realloc = callback_realloc; 595 ws_->realloc = callback_realloc;
290 ws_->free = callback_free; 596 ws_->free = callback_free;
597 ws_->cls_rng = cls_rng;
598 ws_->rng = callback_rng;
291 ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID; 599 ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID;
292 600
293 /* return stream */ 601 /* return stream */
@@ -300,7 +608,7 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
300/** 608/**
301 * Frees a previously allocated websocket stream 609 * Frees a previously allocated websocket stream
302 */ 610 */
303_MHD_EXTERN int 611_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
304MHD_websocket_stream_free (struct MHD_WebSocketStream*ws) 612MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
305{ 613{
306 /* validate parameters */ 614 /* validate parameters */
@@ -323,7 +631,7 @@ MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
323/** 631/**
324 * Invalidates a websocket stream (no more decoding possible) 632 * Invalidates a websocket stream (no more decoding possible)
325 */ 633 */
326_MHD_EXTERN int 634_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
327MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws) 635MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
328{ 636{
329 /* validate parameters */ 637 /* validate parameters */
@@ -340,7 +648,7 @@ MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
340/** 648/**
341 * Returns whether a websocket stream is valid 649 * Returns whether a websocket stream is valid
342 */ 650 */
343_MHD_EXTERN int 651_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
344MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws) 652MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
345{ 653{
346 /* validate parameters */ 654 /* validate parameters */
@@ -354,7 +662,7 @@ MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
354/** 662/**
355 * Decodes incoming data to a websocket frame 663 * Decodes incoming data to a websocket frame
356 */ 664 */
357_MHD_EXTERN int 665_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
358MHD_websocket_decode (struct MHD_WebSocketStream*ws, 666MHD_websocket_decode (struct MHD_WebSocketStream*ws,
359 const char*streambuf, 667 const char*streambuf,
360 size_t streambuf_len, 668 size_t streambuf_len,
@@ -372,7 +680,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
372 680
373 /* validate parameters */ 681 /* validate parameters */
374 if ((NULL == ws) || 682 if ((NULL == ws) ||
375 (NULL == streambuf) && (0 != streambuf_len) || 683 ((NULL == streambuf) && (0 != streambuf_len)) ||
376 (NULL == streambuf_read_len) || 684 (NULL == streambuf_read_len) ||
377 (NULL == payload) || 685 (NULL == payload) ||
378 (NULL == payload_len) ) 686 (NULL == payload_len) )
@@ -657,8 +965,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
657 else 965 else
658 { 966 {
659 size_t size = (size_t) frame_len; 967 size_t size = (size_t) frame_len;
660 if ((SIZE_MAX < size) || ws->max_payload_size && 968 if ((SIZE_MAX < size) ||
661 (ws->max_payload_size < size) ) 969 (ws->max_payload_size && (ws->max_payload_size < size)) )
662 { 970 {
663 /* RFC 6455 7.4.1 1009: If the message is too big to process, we may close the connection */ 971 /* RFC 6455 7.4.1 1009: If the message is too big to process, we may close the connection */
664 ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; 972 ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
@@ -713,8 +1021,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
713 case MHD_WebSocket_DecodeStep_Length2of2: 1021 case MHD_WebSocket_DecodeStep_Length2of2:
714 { 1022 {
715 ws->frame_header [ws->frame_header_size++] = streambuf [current++]; 1023 ws->frame_header [ws->frame_header_size++] = streambuf [current++];
716 size_t size = (size_t) htons (*((unsigned 1024 size_t size = (size_t) MHD_htons (*((uint16_t*) &ws->frame_header [2]));
717 short*) &ws->frame_header [2]));
718 if (125 >= size) 1025 if (125 >= size)
719 { 1026 {
720 /* RFC 6455 5.2 Payload length: The minimal number of bytes */ 1027 /* RFC 6455 5.2 Payload length: The minimal number of bytes */
@@ -733,8 +1040,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
733 *streambuf_read_len = current; 1040 *streambuf_read_len = current;
734 return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; 1041 return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
735 } 1042 }
736 if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < 1043 if ((SIZE_MAX < size) ||
737 size) ) 1044 (ws->max_payload_size && (ws->max_payload_size < size)) )
738 { 1045 {
739 /* RFC 6455 7.4.1 1009: If the message is too big to process, */ 1046 /* RFC 6455 7.4.1 1009: If the message is too big to process, */
740 /* we may close the connection */ 1047 /* we may close the connection */
@@ -771,7 +1078,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
771 case MHD_WebSocket_DecodeStep_Length8of8: 1078 case MHD_WebSocket_DecodeStep_Length8of8:
772 { 1079 {
773 ws->frame_header [ws->frame_header_size++] = streambuf [current++]; 1080 ws->frame_header [ws->frame_header_size++] = streambuf [current++];
774 uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2])); 1081 uint64_t size = MHD_htonll (*((uint64_t*) &ws->frame_header [2]));
775 if (0x7fffffffffffffff < size) 1082 if (0x7fffffffffffffff < size)
776 { 1083 {
777 /* RFC 6455 5.2 frame-payload-length-63: The length may */ 1084 /* RFC 6455 5.2 frame-payload-length-63: The length may */
@@ -809,8 +1116,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
809 *streambuf_read_len = current; 1116 *streambuf_read_len = current;
810 return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; 1117 return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
811 } 1118 }
812 if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < 1119 if ((SIZE_MAX < size) ||
813 size) ) 1120 (ws->max_payload_size && (ws->max_payload_size < size)) )
814 { 1121 {
815 /* RFC 6455 7.4.1 1009: If the message is too big to process, */ 1122 /* RFC 6455 7.4.1 1009: If the message is too big to process, */
816 /* we may close the connection */ 1123 /* we may close the connection */
@@ -893,13 +1200,13 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
893 & 0x03)); 1200 & 0x03));
894 current += bytes_to_take; 1201 current += bytes_to_take;
895 ws->payload_index += bytes_to_take; 1202 ws->payload_index += bytes_to_take;
896 if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame == 1203 if (((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
897 ws->decode_step) && 1204 ws->decode_step) &&
898 (MHD_WebSocket_Opcode_Text == ws->data_type) || 1205 (MHD_WebSocket_Opcode_Text == ws->data_type)) ||
899 (MHD_WebSocket_DecodeStep_PayloadOfControlFrame == 1206 ((MHD_WebSocket_DecodeStep_PayloadOfControlFrame ==
900 ws->decode_step) && 1207 ws->decode_step) &&
901 (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) && 1208 (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) &&
902 (2 < ws->payload_index) ) 1209 (2 < ws->payload_index)) )
903 { 1210 {
904 /* RFC 6455 8.1: We need to check the UTF-8 validity */ 1211 /* RFC 6455 8.1: We need to check the UTF-8 validity */
905 int utf8_step; 1212 int utf8_step;
@@ -1016,7 +1323,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
1016} 1323}
1017 1324
1018 1325
1019static int 1326static enum MHD_WEBSOCKET_STATUS
1020MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, 1327MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
1021 char**payload, 1328 char**payload,
1022 size_t*payload_len) 1329 size_t*payload_len)
@@ -1119,13 +1426,15 @@ MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
1119} 1426}
1120 1427
1121 1428
1122static int 1429static enum MHD_WEBSOCKET_STATUS
1123MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, 1430MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1124 char**payload, 1431 char**payload,
1125 size_t*payload_len) 1432 size_t*payload_len)
1126{ 1433{
1127 /* all payload data of the current frame has been received */ 1434 /* all payload data of the current frame has been received */
1128 char is_fin = ws->frame_header [0] & 0x80; 1435 char is_continue = MHD_WebSocket_Opcode_Continuation ==
1436 (ws->frame_header [0] & 0x0F);
1437 char is_fin = ws->frame_header [0] & 0x80;
1129 if (0 != is_fin) 1438 if (0 != is_fin)
1130 { 1439 {
1131 /* the frame is complete */ 1440 /* the frame is complete */
@@ -1134,9 +1443,9 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1134 /* data frame */ 1443 /* data frame */
1135 char data_type = ws->data_type; 1444 char data_type = ws->data_type;
1136 if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) && 1445 if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) &&
1137 (MHD_WebSocket_Opcode_Continuation == (ws->frame_header [0] & 0x0F))) 1446 (0 != is_continue))
1138 { 1447 {
1139 data_type |= 0x20; /* mark as last fragment */ 1448 data_type |= 0x40; /* mark as last fragment */
1140 } 1449 }
1141 *payload = ws->data_payload; 1450 *payload = ws->data_payload;
1142 *payload_len = ws->data_payload_size; 1451 *payload_len = ws->data_payload_size;
@@ -1170,7 +1479,7 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1170 { 1479 {
1171 /* the last UTF-8 sequence is incomplete, so we keep the start of 1480 /* the last UTF-8 sequence is incomplete, so we keep the start of
1172 that and only return the part before */ 1481 that and only return the part before */
1173 size_t given_utf8; 1482 size_t given_utf8 = 0;
1174 switch (ws->data_utf8_step) 1483 switch (ws->data_utf8_step)
1175 { 1484 {
1176 /* one byte given */ 1485 /* one byte given */
@@ -1220,7 +1529,10 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1220 ws->decode_step = MHD_WebSocket_DecodeStep_Start; 1529 ws->decode_step = MHD_WebSocket_DecodeStep_Start;
1221 ws->payload_index = 0; 1530 ws->payload_index = 0;
1222 ws->frame_header_size = 0; 1531 ws->frame_header_size = 0;
1223 return ws->data_type | 0x10; /* mark as fragment */ 1532 if (0 != is_continue)
1533 return ws->data_type | 0x20; /* mark as middle fragment */
1534 else
1535 return ws->data_type | 0x10; /* mark as first fragment */
1224 } 1536 }
1225 else 1537 else
1226 { 1538 {
@@ -1233,7 +1545,10 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1233 ws->decode_step = MHD_WebSocket_DecodeStep_Start; 1545 ws->decode_step = MHD_WebSocket_DecodeStep_Start;
1234 ws->payload_index = 0; 1546 ws->payload_index = 0;
1235 ws->frame_header_size = 0; 1547 ws->frame_header_size = 0;
1236 return ws->data_type | 0x10; /* mark as fragment */ 1548 if (0 != is_continue)
1549 return ws->data_type | 0x20; /* mark as middle fragment */
1550 else
1551 return ws->data_type | 0x10; /* mark as first fragment */
1237 } 1552 }
1238 } 1553 }
1239 else 1554 else
@@ -1251,7 +1566,7 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
1251/** 1566/**
1252 * Splits the received close reason 1567 * Splits the received close reason
1253 */ 1568 */
1254_MHD_EXTERN int 1569_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1255MHD_websocket_split_close_reason (const char*payload, 1570MHD_websocket_split_close_reason (const char*payload,
1256 size_t payload_len, 1571 size_t payload_len,
1257 unsigned short*reason_code, 1572 unsigned short*reason_code,
@@ -1283,7 +1598,7 @@ MHD_websocket_split_close_reason (const char*payload,
1283 else 1598 else
1284 { 1599 {
1285 if (NULL != reason_code) 1600 if (NULL != reason_code)
1286 *reason_code = htons (*((unsigned short*) payload)); 1601 *reason_code = MHD_htons (*((uint16_t*) payload));
1287 } 1602 }
1288 1603
1289 /* decode reason text */ 1604 /* decode reason text */
@@ -1309,7 +1624,7 @@ MHD_websocket_split_close_reason (const char*payload,
1309/** 1624/**
1310 * Encodes a text into a websocket text frame 1625 * Encodes a text into a websocket text frame
1311 */ 1626 */
1312_MHD_EXTERN int 1627_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1313MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, 1628MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
1314 const char*payload_utf8, 1629 const char*payload_utf8,
1315 size_t payload_utf8_len, 1630 size_t payload_utf8_len,
@@ -1333,13 +1648,13 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
1333 1648
1334 /* validate parameters */ 1649 /* validate parameters */
1335 if ((NULL == ws) || 1650 if ((NULL == ws) ||
1336 (0 != payload_utf8_len) && (NULL == payload_utf8) || 1651 ((0 != payload_utf8_len) && (NULL == payload_utf8)) ||
1337 (NULL == frame) || 1652 (NULL == frame) ||
1338 (NULL == frame_len) || 1653 (NULL == frame_len) ||
1339 (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || 1654 (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
1340 (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) || 1655 (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) ||
1341 (MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) && (NULL == 1656 ((MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) &&
1342 utf8_step) ) 1657 (NULL == utf8_step)) )
1343 { 1658 {
1344 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; 1659 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
1345 } 1660 }
@@ -1356,8 +1671,8 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
1356 utf8_step, 1671 utf8_step,
1357 NULL); 1672 NULL);
1358 if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) || 1673 if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) ||
1359 (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) && 1674 ((MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
1360 (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) ) 1675 (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation)) )
1361 { 1676 {
1362 return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; 1677 return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
1363 } 1678 }
@@ -1376,7 +1691,7 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
1376/** 1691/**
1377 * Encodes binary data into a websocket binary frame 1692 * Encodes binary data into a websocket binary frame
1378 */ 1693 */
1379_MHD_EXTERN int 1694_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1380MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, 1695MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
1381 const char*payload, 1696 const char*payload,
1382 size_t payload_len, 1697 size_t payload_len,
@@ -1392,7 +1707,7 @@ MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
1392 1707
1393 /* validate parameters */ 1708 /* validate parameters */
1394 if ((NULL == ws) || 1709 if ((NULL == ws) ||
1395 (0 != payload_len) && (NULL == payload) || 1710 ((0 != payload_len) && (NULL == payload)) ||
1396 (NULL == frame) || 1711 (NULL == frame) ||
1397 (NULL == frame_len) || 1712 (NULL == frame_len) ||
1398 (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || 1713 (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
@@ -1420,7 +1735,7 @@ MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
1420/** 1735/**
1421 * Internal function for encoding text/binary data into a websocket frame 1736 * Internal function for encoding text/binary data into a websocket frame
1422 */ 1737 */
1423static int 1738static enum MHD_WEBSOCKET_STATUS
1424MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, 1739MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
1425 const char*payload, 1740 const char*payload,
1426 size_t payload_len, 1741 size_t payload_len,
@@ -1433,7 +1748,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
1433 char is_masked = MHD_websocket_encode_is_masked (ws); 1748 char is_masked = MHD_websocket_encode_is_masked (ws);
1434 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); 1749 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
1435 size_t total_len = overhead_len + payload_len; 1750 size_t total_len = overhead_len + payload_len;
1436 uint32_t mask = 0 != is_masked ? MHD_websocket_generate_mask () : 0; 1751 uint32_t mask = 0 != is_masked ? MHD_websocket_generate_mask (ws) : 0;
1437 1752
1438 /* allocate memory */ 1753 /* allocate memory */
1439 char*result = ws->malloc (total_len + 1); 1754 char*result = ws->malloc (total_len + 1);
@@ -1468,13 +1783,13 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
1468 else if (65536 > payload_len) 1783 else if (65536 > payload_len)
1469 { 1784 {
1470 *(result++) = is_masked | 126; 1785 *(result++) = is_masked | 126;
1471 *((unsigned short *) result) = htons ((unsigned short) payload_len); 1786 *((uint16_t *) result) = MHD_htons ((uint16_t) payload_len);
1472 result += 2; 1787 result += 2;
1473 } 1788 }
1474 else 1789 else
1475 { 1790 {
1476 *(result++) = is_masked | 127; 1791 *(result++) = is_masked | 127;
1477 *((uint64_t *) result) = htonll ((uint64_t) payload_len); 1792 *((uint64_t *) result) = MHD_htonll ((uint64_t) payload_len);
1478 result += 8; 1793 result += 8;
1479 1794
1480 } 1795 }
@@ -1505,7 +1820,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
1505/** 1820/**
1506 * Encodes a websocket ping frame 1821 * Encodes a websocket ping frame
1507 */ 1822 */
1508_MHD_EXTERN int 1823_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1509MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, 1824MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
1510 const char*payload, 1825 const char*payload,
1511 size_t payload_len, 1826 size_t payload_len,
@@ -1525,7 +1840,7 @@ MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
1525/** 1840/**
1526 * Encodes a websocket pong frame 1841 * Encodes a websocket pong frame
1527 */ 1842 */
1528_MHD_EXTERN int 1843_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1529MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, 1844MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
1530 const char*payload, 1845 const char*payload,
1531 size_t payload_len, 1846 size_t payload_len,
@@ -1545,7 +1860,7 @@ MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
1545/** 1860/**
1546 * Internal function for encoding ping/pong frames 1861 * Internal function for encoding ping/pong frames
1547 */ 1862 */
1548static int 1863static enum MHD_WEBSOCKET_STATUS
1549MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, 1864MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
1550 const char*payload, 1865 const char*payload,
1551 size_t payload_len, 1866 size_t payload_len,
@@ -1561,7 +1876,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
1561 1876
1562 /* validate the parameters */ 1877 /* validate the parameters */
1563 if ((NULL == ws) || 1878 if ((NULL == ws) ||
1564 (0 != payload_len) && (NULL == payload) || 1879 ((0 != payload_len) && (NULL == payload)) ||
1565 (NULL == frame) || 1880 (NULL == frame) ||
1566 (NULL == frame_len) ) 1881 (NULL == frame_len) )
1567 { 1882 {
@@ -1576,7 +1891,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
1576 char is_masked = MHD_websocket_encode_is_masked (ws); 1891 char is_masked = MHD_websocket_encode_is_masked (ws);
1577 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); 1892 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
1578 size_t total_len = overhead_len + payload_len; 1893 size_t total_len = overhead_len + payload_len;
1579 uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; 1894 uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
1580 1895
1581 /* allocate memory */ 1896 /* allocate memory */
1582 char*result = ws->malloc (total_len + 1); 1897 char*result = ws->malloc (total_len + 1);
@@ -1618,7 +1933,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
1618/** 1933/**
1619 * Encodes a websocket close frame 1934 * Encodes a websocket close frame
1620 */ 1935 */
1621_MHD_EXTERN int 1936_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
1622MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, 1937MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
1623 unsigned short reason_code, 1938 unsigned short reason_code,
1624 const char*reason_utf8, 1939 const char*reason_utf8,
@@ -1634,13 +1949,13 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
1634 1949
1635 /* validate the parameters */ 1950 /* validate the parameters */
1636 if ((NULL == ws) || 1951 if ((NULL == ws) ||
1637 (0 != reason_utf8_len) && (NULL == reason_utf8) || 1952 ((0 != reason_utf8_len) && (NULL == reason_utf8)) ||
1638 (NULL == frame) || 1953 (NULL == frame) ||
1639 (NULL == frame_len) || 1954 (NULL == frame_len) ||
1640 (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) && (1000 > 1955 ((MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) &&
1641 reason_code) || 1956 (1000 > reason_code)) ||
1642 (0 != reason_utf8_len) && (MHD_WEBSOCKET_CLOSEREASON_NO_REASON == 1957 ((0 != reason_utf8_len) &&
1643 reason_code) ) 1958 (MHD_WEBSOCKET_CLOSEREASON_NO_REASON == reason_code)) )
1644 { 1959 {
1645 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; 1960 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
1646 } 1961 }
@@ -1668,7 +1983,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
1668 2 + reason_utf8_len : 0); 1983 2 + reason_utf8_len : 0);
1669 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); 1984 size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
1670 size_t total_len = overhead_len + payload_len; 1985 size_t total_len = overhead_len + payload_len;
1671 uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; 1986 uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
1672 1987
1673 /* allocate memory */ 1988 /* allocate memory */
1674 char*result = ws->malloc (total_len + 1); 1989 char*result = ws->malloc (total_len + 1);
@@ -1697,7 +2012,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
1697 if (0 != reason_code) 2012 if (0 != reason_code)
1698 { 2013 {
1699 /* close reason code */ 2014 /* close reason code */
1700 unsigned short reason_code_nb = htons (reason_code); 2015 uint16_t reason_code_nb = MHD_htons (reason_code);
1701 MHD_websocket_copy_payload (result, 2016 MHD_websocket_copy_payload (result,
1702 (const char*) &reason_code_nb, 2017 (const char*) &reason_code_nb,
1703 2, 2018 2,
@@ -1815,8 +2130,8 @@ MHD_websocket_check_utf8 (const char*buf,
1815 /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0x80-0x9F */ 2130 /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0x80-0x9F */
1816 utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2; 2131 utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2;
1817 } 2132 }
1818 else if ((0xE1 <= character) && (0xEC >= character) || 2133 else if (((0xE1 <= character) && (0xEC >= character)) ||
1819 (0xEE <= character) && (0xEF >= character) ) 2134 ((0xEE <= character) && (0xEF >= character)) )
1820 { 2135 {
1821 /* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 0x80-0xBF */ 2136 /* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 0x80-0xBF */
1822 utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2; 2137 utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2;
@@ -1991,30 +2306,32 @@ MHD_websocket_check_utf8 (const char*buf,
1991 2306
1992 2307
1993/** 2308/**
1994 * Calls srand in the scope of MHD to set the seed
1995 * for the random number generator used for masking.
1996 */
1997_MHD_EXTERN int
1998MHD_websocket_srand (unsigned long seed)
1999{
2000 srand (seed);
2001
2002 return MHD_WEBSOCKET_STATUS_OK;
2003}
2004
2005
2006/**
2007 * Generates a mask for masking by calling 2309 * Generates a mask for masking by calling
2008 * a random number generator. 2310 * a random number generator.
2009 */ 2311 */
2010static uint32_t 2312static uint32_t
2011MHD_websocket_generate_mask () 2313MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws)
2012{ 2314{
2013 unsigned char mask_[4]; 2315 unsigned char mask_[4];
2014 mask_ [0] = (unsigned char) (rand () & 0xFF); 2316 if (NULL != ws->rng)
2015 mask_ [1] = (unsigned char) (rand () & 0xFF); 2317 {
2016 mask_ [2] = (unsigned char) (rand () & 0xFF); 2318 size_t offset = 0;
2017 mask_ [3] = (unsigned char) (rand () & 0xFF); 2319 while (offset < 4)
2320 {
2321 size_t encoded = ws->rng (ws->cls_rng,
2322 mask_ + offset,
2323 4 - offset);
2324 offset += encoded;
2325 }
2326 }
2327 else
2328 {
2329 /* this case should never happen */
2330 mask_ [0] = 0;
2331 mask_ [1] = 0;
2332 mask_ [2] = 0;
2333 mask_ [3] = 0;
2334 }
2018 2335
2019 return *((uint32_t *) mask_); 2336 return *((uint32_t *) mask_);
2020} 2337}
@@ -2024,15 +2341,15 @@ MHD_websocket_generate_mask ()
2024 * Calls the malloc function associated with the websocket steam 2341 * Calls the malloc function associated with the websocket steam
2025 */ 2342 */
2026_MHD_EXTERN void* 2343_MHD_EXTERN void*
2027MHD_websocket_malloc (struct MHD_WebSocketStream*ws, 2344MHD_websocket_malloc (struct MHD_WebSocketStream* ws,
2028 size_t len) 2345 size_t buf_len)
2029{ 2346{
2030 if (NULL == ws) 2347 if (NULL == ws)
2031 { 2348 {
2032 return NULL; 2349 return NULL;
2033 } 2350 }
2034 2351
2035 return ws->malloc (len); 2352 return ws->malloc (buf_len);
2036} 2353}
2037 2354
2038 2355
@@ -2040,16 +2357,16 @@ MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
2040 * Calls the realloc function associated with the websocket steam 2357 * Calls the realloc function associated with the websocket steam
2041 */ 2358 */
2042_MHD_EXTERN void* 2359_MHD_EXTERN void*
2043MHD_websocket_realloc (struct MHD_WebSocketStream*ws, 2360MHD_websocket_realloc (struct MHD_WebSocketStream* ws,
2044 void*cls, 2361 void* buf,
2045 size_t len) 2362 size_t new_buf_len)
2046{ 2363{
2047 if (NULL == ws) 2364 if (NULL == ws)
2048 { 2365 {
2049 return NULL; 2366 return NULL;
2050 } 2367 }
2051 2368
2052 return ws->realloc (cls, len); 2369 return ws->realloc (buf, new_buf_len);
2053} 2370}
2054 2371
2055 2372
@@ -2057,15 +2374,67 @@ MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
2057 * Calls the free function associated with the websocket steam 2374 * Calls the free function associated with the websocket steam
2058 */ 2375 */
2059_MHD_EXTERN int 2376_MHD_EXTERN int
2060MHD_websocket_free (struct MHD_WebSocketStream*ws, 2377MHD_websocket_free (struct MHD_WebSocketStream* ws,
2061 void*cls) 2378 void* buf)
2062{ 2379{
2063 if (NULL == ws) 2380 if (NULL == ws)
2064 { 2381 {
2065 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; 2382 return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
2066 } 2383 }
2067 2384
2068 ws->free (cls); 2385 ws->free (buf);
2069 2386
2070 return MHD_WEBSOCKET_STATUS_OK; 2387 return MHD_WEBSOCKET_STATUS_OK;
2071} 2388}
2389
2390/**
2391 * Converts a 16 bit value into network byte order (MSB first)
2392 * in dependence of the host system
2393 */
2394static uint16_t
2395MHD_htons (uint16_t value)
2396{
2397 uint16_t endian = 0x0001;
2398
2399 if (((char *) &endian)[0] == 0x01)
2400 {
2401 /* least significant byte first */
2402 ((char *) &endian)[0] = ((char *) &value)[1];
2403 ((char *) &endian)[1] = ((char *) &value)[0];
2404 return endian;
2405 }
2406 else
2407 {
2408 /* most significant byte first */
2409 return value;
2410 }
2411}
2412
2413/**
2414 * Converts a 64 bit value into network byte order (MSB first)
2415 * in dependence of the host system
2416 */
2417static uint64_t
2418MHD_htonll (uint64_t value)
2419{
2420 uint64_t endian = 0x0000000000000001;
2421
2422 if (((char *) &endian)[0] == 0x01)
2423 {
2424 /* least significant byte first */
2425 ((char *) &endian)[0] = ((char *) &value)[7];
2426 ((char *) &endian)[1] = ((char *) &value)[6];
2427 ((char *) &endian)[2] = ((char *) &value)[5];
2428 ((char *) &endian)[3] = ((char *) &value)[4];
2429 ((char *) &endian)[4] = ((char *) &value)[3];
2430 ((char *) &endian)[5] = ((char *) &value)[2];
2431 ((char *) &endian)[6] = ((char *) &value)[1];
2432 ((char *) &endian)[7] = ((char *) &value)[0];
2433 return endian;
2434 }
2435 else
2436 {
2437 /* most significant byte first */
2438 return value;
2439 }
2440}
diff --git a/src/microhttpd_ws/sha1.c b/src/microhttpd_ws/sha1.c
index 910c1bdb..9888cbfe 100644
--- a/src/microhttpd_ws/sha1.c
+++ b/src/microhttpd_ws/sha1.c
@@ -1,420 +1,378 @@
1/* sha1.c - Functions to compute SHA1 message digest of files or 1/*
2 memory blocks according to the NIST specification FIPS-180-1. 2 This file is part of libmicrohttpd
3 3 Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
4 Copyright (C) 2000-2021 Free Software Foundation, Inc. 4
5 5 libmicrohttpd is free software; you can redistribute it and/or
6 This program is free software; you can redistribute it and/or modify it 6 modify it under the terms of the GNU Lesser General Public
7 under the terms of the GNU General Public License as published by the 7 License as published by the Free Software Foundation; either
8 Free Software Foundation; either version 2, or (at your option) any 8 version 2.1 of the License, or (at your option) any later version.
9 later version. 9
10 10 This library is distributed in the hope that it will be useful,
11 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 Lesser General Public License for more details.
14 GNU General Public License for more details. 14
15 15 You should have received a copy of the GNU Lesser General Public
16 You should have received a copy of the GNU General Public License 16 License along with this library.
17 along with this program; if not, write to the Free Software Foundation, 17 If not, see <http://www.gnu.org/licenses/>.
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20/* Written by Scott G. Miller
21 Credits:
22 Robert Klep <robert@ilse.nl> -- Expansion function fix
23*/ 18*/
24 19
25/*#include <config.h>*/ 20/**
21 * @file microhttpd/sha1.c
22 * @brief Calculation of SHA-1 digest as defined in FIPS PUB 180-4 (2015)
23 * @author Karlson2k (Evgeny Grin)
24 */
26 25
27#include "sha1.h" 26#include "sha1.h"
28 27
29#include <stddef.h>
30#include <string.h> 28#include <string.h>
31 29#ifdef HAVE_MEMORY_H
32#if USE_UNLOCKED_IO 30#include <memory.h>
33# include "unlocked-io.h" 31#endif /* HAVE_MEMORY_H */
34#endif 32#include "mhd_bithelpers.h"
35 33#include "mhd_assert.h"
36#ifdef WORDS_BIGENDIAN 34
37# define SWAP(n) (n) 35/**
38#else 36 * Initialise structure for SHA-1 calculation.
39# define SWAP(n) \ 37 *
40 (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) 38 * @param ctx_ must be a `struct sha1_ctx *`
41#endif 39 */
42
43#define BLOCKSIZE 4096
44#if BLOCKSIZE % 64 != 0
45# error "invalid BLOCKSIZE"
46#endif
47
48/* This array contains the bytes used to pad the buffer to the next
49 64-byte boundary. (RFC 1321, 3.1: Step 1) */
50static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
51
52
53/* Take a pointer to a 160 bit block of data (five 32 bit ints) and
54 initialize it to the start constants of the SHA1 algorithm. This
55 must be called before using hash in the call to sha1_hash. */
56void 40void
57sha1_init_ctx (struct sha1_ctx *ctx) 41MHD_SHA1_init (void *ctx_)
58{ 42{
59 ctx->A = 0x67452301; 43 struct sha1_ctx *const ctx = ctx_;
60 ctx->B = 0xefcdab89; 44 /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.1 */
61 ctx->C = 0x98badcfe; 45 /* Just some "magic" numbers defined by standard */
62 ctx->D = 0x10325476; 46 ctx->H[0] = UINT32_C (0x67452301);
63 ctx->E = 0xc3d2e1f0; 47 ctx->H[1] = UINT32_C (0xefcdab89);
64 48 ctx->H[2] = UINT32_C (0x98badcfe);
65 ctx->total[0] = ctx->total[1] = 0; 49 ctx->H[3] = UINT32_C (0x10325476);
66 ctx->buflen = 0; 50 ctx->H[4] = UINT32_C (0xc3d2e1f0);
51
52 /* Initialise number of bytes. */
53 ctx->count = 0;
67} 54}
68 55
69 56
70/* Put result from CTX in first 20 bytes following RESBUF. The result 57/**
71 must be in little endian byte order. 58 * Base of SHA-1 transformation.
72 59 * Gets full 512 bits / 64 bytes block of data and updates hash values;
73 IMPORTANT: On some systems it is required that RESBUF is correctly 60 * @param H hash values
74 aligned for a 32-bit value. */ 61 * @param data data, must be exactly 64 bytes long
75void * 62 */
76sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) 63static void
64sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH],
65 const uint8_t data[SHA1_BLOCK_SIZE])
77{ 66{
78 ((sha1_uint32 *) resbuf)[0] = SWAP (ctx->A); 67 /* Working variables,
79 ((sha1_uint32 *) resbuf)[1] = SWAP (ctx->B); 68 see FIPS PUB 180-4 paragraph 6.1.3 */
80 ((sha1_uint32 *) resbuf)[2] = SWAP (ctx->C); 69 uint32_t a = H[0];
81 ((sha1_uint32 *) resbuf)[3] = SWAP (ctx->D); 70 uint32_t b = H[1];
82 ((sha1_uint32 *) resbuf)[4] = SWAP (ctx->E); 71 uint32_t c = H[2];
83 72 uint32_t d = H[3];
84 return resbuf; 73 uint32_t e = H[4];
74
75 /* Data buffer, used as cyclic buffer.
76 See FIPS PUB 180-4 paragraphs 5.2.1, 6.1.3 */
77 uint32_t W[16];
78
79 /* 'Ch' and 'Maj' macro functions are defined with
80 widely-used optimization.
81 See FIPS PUB 180-4 formulae 4.1. */
82#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
83#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
84 /* Unoptimized (original) versions: */
85/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
86/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
87#define Par(x,y,z) ( (x) ^ (y) ^ (z) )
88
89 /* Single step of SHA-1 computation,
90 see FIPS PUB 180-4 paragraph 6.1.3 step 3.
91 * Note: instead of reassigning all working variables on each step,
92 variables are rotated for each step:
93 SHA1STEP32 (a, b, c, d, e, func, K00, W[0]);
94 SHA1STEP32 (e, a, b, c, d, func, K00, W[1]);
95 so current 'vC' will be used as 'vD' on the next step,
96 current 'vE' will be used as 'vA' on the next step.
97 * Note: 'wt' must be used exactly one time in this macro as it change other data as well
98 every time when used. */
99
100#define SHA1STEP32(vA,vB,vC,vD,vE,ft,kt,wt) do { \
101 (vE) += _MHD_ROTL32 ((vA), 5) + ft ((vB), (vC), (vD)) + (kt) + (wt); \
102 (vB) = _MHD_ROTL32 ((vB), 30); } while (0)
103
104 /* Get value of W(t) from input data buffer,
105 See FIPS PUB 180-4 paragraph 6.1.3.
106 Input data must be read in big-endian bytes order,
107 see FIPS PUB 180-4 paragraph 3.1.2. */
108#define GET_W_FROM_DATA(buf,t) \
109 _MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD)
110
111#ifndef _MHD_GET_32BIT_BE_UNALIGNED
112 if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
113 {
114 /* Copy the unaligned input data to the aligned buffer */
115 memcpy (W, data, SHA1_BLOCK_SIZE);
116 /* The W[] buffer itself will be used as the source of the data,
117 * but data will be reloaded in correct bytes order during
118 * the next steps */
119 data = (uint8_t*) W;
120 }
121#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
122
123/* SHA-1 values of Kt for t=0..19, see FIPS PUB 180-4 paragraph 4.2.1. */
124#define K00 UINT32_C(0x5a827999)
125/* SHA-1 values of Kt for t=20..39, see FIPS PUB 180-4 paragraph 4.2.1.*/
126#define K20 UINT32_C(0x6ed9eba1)
127/* SHA-1 values of Kt for t=40..59, see FIPS PUB 180-4 paragraph 4.2.1.*/
128#define K40 UINT32_C(0x8f1bbcdc)
129/* SHA-1 values of Kt for t=60..79, see FIPS PUB 180-4 paragraph 4.2.1.*/
130#define K60 UINT32_C(0xca62c1d6)
131
132 /* During first 16 steps, before making any calculations on each step,
133 the W element is read from input data buffer as big-endian value and
134 stored in array of W elements. */
135 /* Note: instead of using K constants as array, all K values are specified
136 individually for each step. */
137 SHA1STEP32 (a, b, c, d, e, Ch, K00, W[0] = GET_W_FROM_DATA (data, 0));
138 SHA1STEP32 (e, a, b, c, d, Ch, K00, W[1] = GET_W_FROM_DATA (data, 1));
139 SHA1STEP32 (d, e, a, b, c, Ch, K00, W[2] = GET_W_FROM_DATA (data, 2));
140 SHA1STEP32 (c, d, e, a, b, Ch, K00, W[3] = GET_W_FROM_DATA (data, 3));
141 SHA1STEP32 (b, c, d, e, a, Ch, K00, W[4] = GET_W_FROM_DATA (data, 4));
142 SHA1STEP32 (a, b, c, d, e, Ch, K00, W[5] = GET_W_FROM_DATA (data, 5));
143 SHA1STEP32 (e, a, b, c, d, Ch, K00, W[6] = GET_W_FROM_DATA (data, 6));
144 SHA1STEP32 (d, e, a, b, c, Ch, K00, W[7] = GET_W_FROM_DATA (data, 7));
145 SHA1STEP32 (c, d, e, a, b, Ch, K00, W[8] = GET_W_FROM_DATA (data, 8));
146 SHA1STEP32 (b, c, d, e, a, Ch, K00, W[9] = GET_W_FROM_DATA (data, 9));
147 SHA1STEP32 (a, b, c, d, e, Ch, K00, W[10] = GET_W_FROM_DATA (data, 10));
148 SHA1STEP32 (e, a, b, c, d, Ch, K00, W[11] = GET_W_FROM_DATA (data, 11));
149 SHA1STEP32 (d, e, a, b, c, Ch, K00, W[12] = GET_W_FROM_DATA (data, 12));
150 SHA1STEP32 (c, d, e, a, b, Ch, K00, W[13] = GET_W_FROM_DATA (data, 13));
151 SHA1STEP32 (b, c, d, e, a, Ch, K00, W[14] = GET_W_FROM_DATA (data, 14));
152 SHA1STEP32 (a, b, c, d, e, Ch, K00, W[15] = GET_W_FROM_DATA (data, 15));
153
154 /* 'W' generation and assignment for 16 <= t <= 79.
155 See FIPS PUB 180-4 paragraph 6.1.3.
156 As only last 16 'W' are used in calculations, it is possible to
157 use 16 elements array of W as cyclic buffer. */
158#define Wgen(w,t) _MHD_ROTL32((w)[(t + 13) & 0xf] ^ (w)[(t + 8) & 0xf] \
159 ^ (w)[(t + 2) & 0xf] ^ (w)[t & 0xf], 1)
160
161 /* During last 60 steps, before making any calculations on each step,
162 W element is generated from W elements of cyclic buffer and generated value
163 stored back in cyclic buffer. */
164 /* Note: instead of using K constants as array, all K values are specified
165 individually for each step, see FIPS PUB 180-4 paragraph 4.2.1. */
166 SHA1STEP32 (e, a, b, c, d, Ch, K00, W[16 & 0xf] = Wgen (W, 16));
167 SHA1STEP32 (d, e, a, b, c, Ch, K00, W[17 & 0xf] = Wgen (W, 17));
168 SHA1STEP32 (c, d, e, a, b, Ch, K00, W[18 & 0xf] = Wgen (W, 18));
169 SHA1STEP32 (b, c, d, e, a, Ch, K00, W[19 & 0xf] = Wgen (W, 19));
170 SHA1STEP32 (a, b, c, d, e, Par, K20, W[20 & 0xf] = Wgen (W, 20));
171 SHA1STEP32 (e, a, b, c, d, Par, K20, W[21 & 0xf] = Wgen (W, 21));
172 SHA1STEP32 (d, e, a, b, c, Par, K20, W[22 & 0xf] = Wgen (W, 22));
173 SHA1STEP32 (c, d, e, a, b, Par, K20, W[23 & 0xf] = Wgen (W, 23));
174 SHA1STEP32 (b, c, d, e, a, Par, K20, W[24 & 0xf] = Wgen (W, 24));
175 SHA1STEP32 (a, b, c, d, e, Par, K20, W[25 & 0xf] = Wgen (W, 25));
176 SHA1STEP32 (e, a, b, c, d, Par, K20, W[26 & 0xf] = Wgen (W, 26));
177 SHA1STEP32 (d, e, a, b, c, Par, K20, W[27 & 0xf] = Wgen (W, 27));
178 SHA1STEP32 (c, d, e, a, b, Par, K20, W[28 & 0xf] = Wgen (W, 28));
179 SHA1STEP32 (b, c, d, e, a, Par, K20, W[29 & 0xf] = Wgen (W, 29));
180 SHA1STEP32 (a, b, c, d, e, Par, K20, W[30 & 0xf] = Wgen (W, 30));
181 SHA1STEP32 (e, a, b, c, d, Par, K20, W[31 & 0xf] = Wgen (W, 31));
182 SHA1STEP32 (d, e, a, b, c, Par, K20, W[32 & 0xf] = Wgen (W, 32));
183 SHA1STEP32 (c, d, e, a, b, Par, K20, W[33 & 0xf] = Wgen (W, 33));
184 SHA1STEP32 (b, c, d, e, a, Par, K20, W[34 & 0xf] = Wgen (W, 34));
185 SHA1STEP32 (a, b, c, d, e, Par, K20, W[35 & 0xf] = Wgen (W, 35));
186 SHA1STEP32 (e, a, b, c, d, Par, K20, W[36 & 0xf] = Wgen (W, 36));
187 SHA1STEP32 (d, e, a, b, c, Par, K20, W[37 & 0xf] = Wgen (W, 37));
188 SHA1STEP32 (c, d, e, a, b, Par, K20, W[38 & 0xf] = Wgen (W, 38));
189 SHA1STEP32 (b, c, d, e, a, Par, K20, W[39 & 0xf] = Wgen (W, 39));
190 SHA1STEP32 (a, b, c, d, e, Maj, K40, W[40 & 0xf] = Wgen (W, 40));
191 SHA1STEP32 (e, a, b, c, d, Maj, K40, W[41 & 0xf] = Wgen (W, 41));
192 SHA1STEP32 (d, e, a, b, c, Maj, K40, W[42 & 0xf] = Wgen (W, 42));
193 SHA1STEP32 (c, d, e, a, b, Maj, K40, W[43 & 0xf] = Wgen (W, 43));
194 SHA1STEP32 (b, c, d, e, a, Maj, K40, W[44 & 0xf] = Wgen (W, 44));
195 SHA1STEP32 (a, b, c, d, e, Maj, K40, W[45 & 0xf] = Wgen (W, 45));
196 SHA1STEP32 (e, a, b, c, d, Maj, K40, W[46 & 0xf] = Wgen (W, 46));
197 SHA1STEP32 (d, e, a, b, c, Maj, K40, W[47 & 0xf] = Wgen (W, 47));
198 SHA1STEP32 (c, d, e, a, b, Maj, K40, W[48 & 0xf] = Wgen (W, 48));
199 SHA1STEP32 (b, c, d, e, a, Maj, K40, W[49 & 0xf] = Wgen (W, 49));
200 SHA1STEP32 (a, b, c, d, e, Maj, K40, W[50 & 0xf] = Wgen (W, 50));
201 SHA1STEP32 (e, a, b, c, d, Maj, K40, W[51 & 0xf] = Wgen (W, 51));
202 SHA1STEP32 (d, e, a, b, c, Maj, K40, W[52 & 0xf] = Wgen (W, 52));
203 SHA1STEP32 (c, d, e, a, b, Maj, K40, W[53 & 0xf] = Wgen (W, 53));
204 SHA1STEP32 (b, c, d, e, a, Maj, K40, W[54 & 0xf] = Wgen (W, 54));
205 SHA1STEP32 (a, b, c, d, e, Maj, K40, W[55 & 0xf] = Wgen (W, 55));
206 SHA1STEP32 (e, a, b, c, d, Maj, K40, W[56 & 0xf] = Wgen (W, 56));
207 SHA1STEP32 (d, e, a, b, c, Maj, K40, W[57 & 0xf] = Wgen (W, 57));
208 SHA1STEP32 (c, d, e, a, b, Maj, K40, W[58 & 0xf] = Wgen (W, 58));
209 SHA1STEP32 (b, c, d, e, a, Maj, K40, W[59 & 0xf] = Wgen (W, 59));
210 SHA1STEP32 (a, b, c, d, e, Par, K60, W[60 & 0xf] = Wgen (W, 60));
211 SHA1STEP32 (e, a, b, c, d, Par, K60, W[61 & 0xf] = Wgen (W, 61));
212 SHA1STEP32 (d, e, a, b, c, Par, K60, W[62 & 0xf] = Wgen (W, 62));
213 SHA1STEP32 (c, d, e, a, b, Par, K60, W[63 & 0xf] = Wgen (W, 63));
214 SHA1STEP32 (b, c, d, e, a, Par, K60, W[64 & 0xf] = Wgen (W, 64));
215 SHA1STEP32 (a, b, c, d, e, Par, K60, W[65 & 0xf] = Wgen (W, 65));
216 SHA1STEP32 (e, a, b, c, d, Par, K60, W[66 & 0xf] = Wgen (W, 66));
217 SHA1STEP32 (d, e, a, b, c, Par, K60, W[67 & 0xf] = Wgen (W, 67));
218 SHA1STEP32 (c, d, e, a, b, Par, K60, W[68 & 0xf] = Wgen (W, 68));
219 SHA1STEP32 (b, c, d, e, a, Par, K60, W[69 & 0xf] = Wgen (W, 69));
220 SHA1STEP32 (a, b, c, d, e, Par, K60, W[70 & 0xf] = Wgen (W, 70));
221 SHA1STEP32 (e, a, b, c, d, Par, K60, W[71 & 0xf] = Wgen (W, 71));
222 SHA1STEP32 (d, e, a, b, c, Par, K60, W[72 & 0xf] = Wgen (W, 72));
223 SHA1STEP32 (c, d, e, a, b, Par, K60, W[73 & 0xf] = Wgen (W, 73));
224 SHA1STEP32 (b, c, d, e, a, Par, K60, W[74 & 0xf] = Wgen (W, 74));
225 SHA1STEP32 (a, b, c, d, e, Par, K60, W[75 & 0xf] = Wgen (W, 75));
226 SHA1STEP32 (e, a, b, c, d, Par, K60, W[76 & 0xf] = Wgen (W, 76));
227 SHA1STEP32 (d, e, a, b, c, Par, K60, W[77 & 0xf] = Wgen (W, 77));
228 SHA1STEP32 (c, d, e, a, b, Par, K60, W[78 & 0xf] = Wgen (W, 78));
229 SHA1STEP32 (b, c, d, e, a, Par, K60, W[79 & 0xf] = Wgen (W, 79));
230
231 /* Compute intermediate hash.
232 See FIPS PUB 180-4 paragraph 6.1.3 step 4. */
233 H[0] += a;
234 H[1] += b;
235 H[2] += c;
236 H[3] += d;
237 H[4] += e;
85} 238}
86 239
87 240
88/* Process the remaining bytes in the internal buffer and the usual 241/**
89 prolog according to the standard and write the result to RESBUF. 242 * Process portion of bytes.
90 243 *
91 IMPORTANT: On some systems it is required that RESBUF is correctly 244 * @param ctx_ must be a `struct sha1_ctx *`
92 aligned for a 32-bit value. */ 245 * @param data bytes to add to hash
93void * 246 * @param length number of bytes in @a data
94sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) 247 */
248void
249MHD_SHA1_update (void *ctx_,
250 const uint8_t *data,
251 size_t length)
95{ 252{
96 /* Take yet unprocessed bytes into account. */ 253 struct sha1_ctx *const ctx = ctx_;
97 sha1_uint32 bytes = ctx->buflen; 254 unsigned bytes_have; /**< Number of bytes in buffer */
98 size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
99
100 /* Now count remaining bytes. */
101 ctx->total[0] += bytes;
102 if (ctx->total[0] < bytes)
103 ++ctx->total[1];
104
105 /* Put the 64-bit file length in *bits* at the end of the buffer. */
106 ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29));
107 ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3);
108
109 memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
110
111 /* Process last bytes. */
112 sha1_process_block (ctx->buffer, size * 4, ctx);
113
114 return sha1_read_ctx (ctx, resbuf);
115}
116 255
256 mhd_assert ((data != NULL) || (length == 0));
117 257
118/* Compute SHA1 message digest for bytes read from STREAM. The 258 if (0 == length)
119 resulting message digest number will be written into the 16 bytes 259 return; /* Do nothing */
120 beginning at RESBLOCK. */
121int
122sha1_stream (FILE *stream, void *resblock)
123{
124 struct sha1_ctx ctx;
125 char buffer[BLOCKSIZE + 72];
126 size_t sum;
127 260
128 /* Initialize the computation context. */ 261 /* Note: (count & (SHA1_BLOCK_SIZE-1))
129 sha1_init_ctx (&ctx); 262 equal (count % SHA1_BLOCK_SIZE) for this block size. */
263 bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
264 ctx->count += length;
130 265
131 /* Iterate over full file contents. */ 266 if (0 != bytes_have)
132 while (1)
133 { 267 {
134 /* We read the file in blocks of BLOCKSIZE bytes. One call of the 268 unsigned bytes_left = SHA1_BLOCK_SIZE - bytes_have;
135 computation function processes the whole buffer so that with the 269 if (length >= bytes_left)
136 next round of the loop another block can be read. */ 270 { /* Combine new data with the data in the buffer and
137 size_t n; 271 process the full block. */
138 sum = 0; 272 memcpy (ctx->buffer + bytes_have,
139 273 data,
140 /* Read block. Take care for partial reads. */ 274 bytes_left);
141 while (1) 275 data += bytes_left;
142 { 276 length -= bytes_left;
143 n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); 277 sha1_transform (ctx->H, ctx->buffer);
144 278 bytes_have = 0;
145 sum += n;
146
147 if (sum == BLOCKSIZE)
148 break;
149
150 if (n == 0)
151 {
152 /* Check for the error flag IFF N == 0, so that we don't
153 exit the loop after a partial read due to e.g., EAGAIN
154 or EWOULDBLOCK. */
155 if (ferror (stream))
156 return 1;
157 goto process_partial_block;
158 }
159
160 /* We've read at least one byte, so ignore errors. But always
161 check for EOF, since feof may be true even though N > 0.
162 Otherwise, we could end up calling fread after EOF. */
163 if (feof (stream))
164 goto process_partial_block;
165 } 279 }
166
167 /* Process buffer with BLOCKSIZE bytes. Note that
168 BLOCKSIZE % 64 == 0
169 */
170 sha1_process_block (buffer, BLOCKSIZE, &ctx);
171 } 280 }
172 281
173process_partial_block:; 282 while (SHA1_BLOCK_SIZE <= length)
174 283 { /* Process any full blocks of new data directly,
175 /* Process any remaining bytes. */ 284 without copying to the buffer. */
176 if (sum > 0) 285 sha1_transform (ctx->H, data);
177 sha1_process_bytes (buffer, sum, &ctx); 286 data += SHA1_BLOCK_SIZE;
287 length -= SHA1_BLOCK_SIZE;
288 }
178 289
179 /* Construct result in desired memory. */ 290 if (0 != length)
180 sha1_finish_ctx (&ctx, resblock); 291 { /* Copy incomplete block of new data (if any)
181 return 0; 292 to the buffer. */
293 memcpy (ctx->buffer + bytes_have, data, length);
294 }
182} 295}
183 296
184 297
185/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The 298/**
186 result is always in little endian byte order, so that a byte-wise 299 * Size of "length" padding addition in bytes.
187 output yields to the wanted ASCII representation of the message 300 * See FIPS PUB 180-4 paragraph 5.1.1.
188 digest. */ 301 */
189void * 302#define SHA1_SIZE_OF_LEN_ADD (64 / 8)
190sha1_buffer (const char *buffer, size_t len, void *resblock)
191{
192 struct sha1_ctx ctx;
193
194 /* Initialize the computation context. */
195 sha1_init_ctx (&ctx);
196
197 /* Process whole buffer but last len % 64 bytes. */
198 sha1_process_bytes (buffer, len, &ctx);
199
200 /* Put result in desired memory area. */
201 return sha1_finish_ctx (&ctx, resblock);
202}
203
204 303
304/**
305 * Finalise SHA-1 calculation, return digest.
306 *
307 * @param ctx_ must be a `struct sha1_ctx *`
308 * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
309 */
205void 310void
206sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) 311MHD_SHA1_finish (void *ctx_,
312 uint8_t digest[SHA1_DIGEST_SIZE])
207{ 313{
208 /* When we already have some bits in our internal buffer concatenate 314 struct sha1_ctx *const ctx = ctx_;
209 both inputs first. */ 315 uint64_t num_bits; /**< Number of processed bits */
210 if (ctx->buflen != 0) 316 unsigned bytes_have; /**< Number of bytes in buffer */
211 { 317
212 size_t left_over = ctx->buflen; 318 num_bits = ctx->count << 3;
213 size_t add = 128 - left_over > len ? len : 128 - left_over; 319 /* Note: (count & (SHA1_BLOCK_SIZE-1))
214 320 equals (count % SHA1_BLOCK_SIZE) for this block size. */
215 memcpy (&((char *) ctx->buffer)[left_over], buffer, add); 321 bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
216 ctx->buflen += add; 322
217 323 /* Input data must be padded with bit "1" and with length of data in bits.
218 if (ctx->buflen > 64) 324 See FIPS PUB 180-4 paragraph 5.1.1. */
219 { 325 /* Data is always processed in form of bytes (not by individual bits),
220 sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); 326 therefore position of first padding bit in byte is always predefined (0x80). */
221 327 /* Buffer always have space at least for one byte (as full buffers are
222 ctx->buflen &= 63; 328 processed immediately). */
223 /* The regions in the following copy operation cannot overlap. */ 329 ctx->buffer[bytes_have++] = 0x80;
224 memcpy (ctx->buffer, 330
225 &((char *) ctx->buffer)[(left_over + add) & ~63], 331 if (SHA1_BLOCK_SIZE - bytes_have < SHA1_SIZE_OF_LEN_ADD)
226 ctx->buflen); 332 { /* No space in current block to put total length of message.
227 } 333 Pad current block with zeros and process it. */
228 334 if (SHA1_BLOCK_SIZE > bytes_have)
229 buffer = (const char *) buffer + add; 335 memset (ctx->buffer + bytes_have, 0, SHA1_BLOCK_SIZE - bytes_have);
230 len -= add; 336 /* Process full block. */
337 sha1_transform (ctx->H, ctx->buffer);
338 /* Start new block. */
339 bytes_have = 0;
231 } 340 }
232 341
233 /* Process available complete blocks. */ 342 /* Pad the rest of the buffer with zeros. */
234 if (len >= 64) 343 memset (ctx->buffer + bytes_have, 0,
344 SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD - bytes_have);
345 /* Put the number of bits in the processed message as a big-endian value. */
346 _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD,
347 num_bits);
348 /* Process the full final block. */
349 sha1_transform (ctx->H, ctx->buffer);
350
351 /* Put final hash/digest in BE mode */
352#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
353 if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
235 { 354 {
236#if ! _STRING_ARCH_unaligned 355 uint32_t alig_dgst[_SHA1_DIGEST_LENGTH];
237# define alignof(type) offsetof (struct { char c; type x; }, x) 356 _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
238# define UNALIGNED_P(p) (((size_t) p) % alignof (sha1_uint32) != 0) 357 _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
239 if (UNALIGNED_P (buffer)) 358 _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
240 while (len > 64) 359 _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
241 { 360 _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
242 sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); 361 /* Copy result to unaligned destination address */
243 buffer = (const char *) buffer + 64; 362 memcpy (digest, alig_dgst, SHA1_DIGEST_SIZE);
244 len -= 64;
245 }
246 else
247#endif
248 {
249 sha1_process_block (buffer, len & ~63, ctx);
250 buffer = (const char *) buffer + (len & ~63);
251 len &= 63;
252 }
253 } 363 }
254 364 else
255 /* Move remaining bytes in internal buffer. */ 365#else /* _MHD_PUT_32BIT_BE_UNALIGNED */
256 if (len > 0) 366 if (1)
367#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
257 { 368 {
258 size_t left_over = ctx->buflen; 369 _MHD_PUT_32BIT_BE (digest + 0 * SHA1_BYTES_IN_WORD, ctx->H[0]);
259 370 _MHD_PUT_32BIT_BE (digest + 1 * SHA1_BYTES_IN_WORD, ctx->H[1]);
260 memcpy (&((char *) ctx->buffer)[left_over], buffer, len); 371 _MHD_PUT_32BIT_BE (digest + 2 * SHA1_BYTES_IN_WORD, ctx->H[2]);
261 left_over += len; 372 _MHD_PUT_32BIT_BE (digest + 3 * SHA1_BYTES_IN_WORD, ctx->H[3]);
262 if (left_over >= 64) 373 _MHD_PUT_32BIT_BE (digest + 4 * SHA1_BYTES_IN_WORD, ctx->H[4]);
263 {
264 sha1_process_block (ctx->buffer, 64, ctx);
265 left_over -= 64;
266 memmove (ctx->buffer, &ctx->buffer[16], left_over);
267 }
268 ctx->buflen = left_over;
269 } 374 }
270}
271
272
273/* --- Code below is the primary difference between md5.c and sha1.c --- */
274
275/* SHA1 round constants */
276#define K1 0x5a827999
277#define K2 0x6ed9eba1
278#define K3 0x8f1bbcdc
279#define K4 0xca62c1d6
280
281/* Round functions. Note that F2 is the same as F4. */
282#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) )
283#define F2(B,C,D) (B ^ C ^ D)
284#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) )
285#define F4(B,C,D) (B ^ C ^ D)
286
287/* Process LEN bytes of BUFFER, accumulating context into CTX.
288 It is assumed that LEN % 64 == 0.
289 Most of this code comes from GnuPG's cipher/sha1.c. */
290 375
291void 376 /* Erase potentially sensitive data. */
292sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) 377 memset (ctx, 0, sizeof(struct sha1_ctx));
293{
294 const sha1_uint32 *words = (const sha1_uint32*) buffer;
295 size_t nwords = len / sizeof (sha1_uint32);
296 const sha1_uint32 *endp = words + nwords;
297 sha1_uint32 x[16];
298 sha1_uint32 a = ctx->A;
299 sha1_uint32 b = ctx->B;
300 sha1_uint32 c = ctx->C;
301 sha1_uint32 d = ctx->D;
302 sha1_uint32 e = ctx->E;
303
304 /* First increment the byte count. RFC 1321 specifies the possible
305 length of the file up to 2^64 bits. Here we only compute the
306 number of bytes. Do a double word increment. */
307 ctx->total[0] += len;
308 ctx->total[1] += ((len >> 31) >> 1) + (ctx->total[0] < len);
309
310#define rol(x, n) (((x) << (n)) | ((sha1_uint32) (x) >> (32 - (n))))
311
312#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \
313 ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \
314 , (x[I&0x0f] = rol(tm, 1)) )
315
316#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \
317 + F( B, C, D ) \
318 + K \
319 + M; \
320 B = rol( B, 30 ); \
321 } while(0)
322
323 while (words < endp)
324 {
325 sha1_uint32 tm;
326 int t;
327 for (t = 0; t < 16; t++)
328 {
329 x[t] = SWAP (*words);
330 words++;
331 }
332
333 R (a, b, c, d, e, F1, K1, x[ 0]);
334 R (e, a, b, c, d, F1, K1, x[ 1]);
335 R (d, e, a, b, c, F1, K1, x[ 2]);
336 R (c, d, e, a, b, F1, K1, x[ 3]);
337 R (b, c, d, e, a, F1, K1, x[ 4]);
338 R (a, b, c, d, e, F1, K1, x[ 5]);
339 R (e, a, b, c, d, F1, K1, x[ 6]);
340 R (d, e, a, b, c, F1, K1, x[ 7]);
341 R (c, d, e, a, b, F1, K1, x[ 8]);
342 R (b, c, d, e, a, F1, K1, x[ 9]);
343 R (a, b, c, d, e, F1, K1, x[10]);
344 R (e, a, b, c, d, F1, K1, x[11]);
345 R (d, e, a, b, c, F1, K1, x[12]);
346 R (c, d, e, a, b, F1, K1, x[13]);
347 R (b, c, d, e, a, F1, K1, x[14]);
348 R (a, b, c, d, e, F1, K1, x[15]);
349 R (e, a, b, c, d, F1, K1, M (16) );
350 R (d, e, a, b, c, F1, K1, M (17) );
351 R (c, d, e, a, b, F1, K1, M (18) );
352 R (b, c, d, e, a, F1, K1, M (19) );
353 R (a, b, c, d, e, F2, K2, M (20) );
354 R (e, a, b, c, d, F2, K2, M (21) );
355 R (d, e, a, b, c, F2, K2, M (22) );
356 R (c, d, e, a, b, F2, K2, M (23) );
357 R (b, c, d, e, a, F2, K2, M (24) );
358 R (a, b, c, d, e, F2, K2, M (25) );
359 R (e, a, b, c, d, F2, K2, M (26) );
360 R (d, e, a, b, c, F2, K2, M (27) );
361 R (c, d, e, a, b, F2, K2, M (28) );
362 R (b, c, d, e, a, F2, K2, M (29) );
363 R (a, b, c, d, e, F2, K2, M (30) );
364 R (e, a, b, c, d, F2, K2, M (31) );
365 R (d, e, a, b, c, F2, K2, M (32) );
366 R (c, d, e, a, b, F2, K2, M (33) );
367 R (b, c, d, e, a, F2, K2, M (34) );
368 R (a, b, c, d, e, F2, K2, M (35) );
369 R (e, a, b, c, d, F2, K2, M (36) );
370 R (d, e, a, b, c, F2, K2, M (37) );
371 R (c, d, e, a, b, F2, K2, M (38) );
372 R (b, c, d, e, a, F2, K2, M (39) );
373 R (a, b, c, d, e, F3, K3, M (40) );
374 R (e, a, b, c, d, F3, K3, M (41) );
375 R (d, e, a, b, c, F3, K3, M (42) );
376 R (c, d, e, a, b, F3, K3, M (43) );
377 R (b, c, d, e, a, F3, K3, M (44) );
378 R (a, b, c, d, e, F3, K3, M (45) );
379 R (e, a, b, c, d, F3, K3, M (46) );
380 R (d, e, a, b, c, F3, K3, M (47) );
381 R (c, d, e, a, b, F3, K3, M (48) );
382 R (b, c, d, e, a, F3, K3, M (49) );
383 R (a, b, c, d, e, F3, K3, M (50) );
384 R (e, a, b, c, d, F3, K3, M (51) );
385 R (d, e, a, b, c, F3, K3, M (52) );
386 R (c, d, e, a, b, F3, K3, M (53) );
387 R (b, c, d, e, a, F3, K3, M (54) );
388 R (a, b, c, d, e, F3, K3, M (55) );
389 R (e, a, b, c, d, F3, K3, M (56) );
390 R (d, e, a, b, c, F3, K3, M (57) );
391 R (c, d, e, a, b, F3, K3, M (58) );
392 R (b, c, d, e, a, F3, K3, M (59) );
393 R (a, b, c, d, e, F4, K4, M (60) );
394 R (e, a, b, c, d, F4, K4, M (61) );
395 R (d, e, a, b, c, F4, K4, M (62) );
396 R (c, d, e, a, b, F4, K4, M (63) );
397 R (b, c, d, e, a, F4, K4, M (64) );
398 R (a, b, c, d, e, F4, K4, M (65) );
399 R (e, a, b, c, d, F4, K4, M (66) );
400 R (d, e, a, b, c, F4, K4, M (67) );
401 R (c, d, e, a, b, F4, K4, M (68) );
402 R (b, c, d, e, a, F4, K4, M (69) );
403 R (a, b, c, d, e, F4, K4, M (70) );
404 R (e, a, b, c, d, F4, K4, M (71) );
405 R (d, e, a, b, c, F4, K4, M (72) );
406 R (c, d, e, a, b, F4, K4, M (73) );
407 R (b, c, d, e, a, F4, K4, M (74) );
408 R (a, b, c, d, e, F4, K4, M (75) );
409 R (e, a, b, c, d, F4, K4, M (76) );
410 R (d, e, a, b, c, F4, K4, M (77) );
411 R (c, d, e, a, b, F4, K4, M (78) );
412 R (b, c, d, e, a, F4, K4, M (79) );
413
414 a = ctx->A += a;
415 b = ctx->B += b;
416 c = ctx->C += c;
417 d = ctx->D += d;
418 e = ctx->E += e;
419 }
420} 378}
diff --git a/src/microhttpd_ws/sha1.h b/src/microhttpd_ws/sha1.h
index be0d190b..851a4429 100644
--- a/src/microhttpd_ws/sha1.h
+++ b/src/microhttpd_ws/sha1.h
@@ -1,145 +1,110 @@
1/* Declarations of functions and data types used for SHA1 sum 1/*
2 library functions. 2 This file is part of libmicrohttpd
3 Copyright (C) 2000-2021 Free Software Foundation, Inc. 3 Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
4 4
5 This program is free software; you can redistribute it and/or modify it 5 This library is free software; you can redistribute it and/or
6 under the terms of the GNU General Public License as published by the 6 modify it under the terms of the GNU Lesser General Public
7 Free Software Foundation; either version 3, or (at your option) any 7 License as published by the Free Software Foundation; either
8 later version. 8 version 2.1 of the License, or (at your option) any later version.
9 9
10 This program is distributed in the hope that it will be useful, 10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 GNU General Public License for more details. 13 Lesser General Public License for more details.
14 14
15 You should have received a copy of the GNU General Public License 15 You should have received a copy of the GNU Lesser General Public
16 along with this program; if not, write to the Free Software Foundation, 16 License along with this library.
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 17 If not, see <http://www.gnu.org/licenses/>.
18 18*/
19#ifndef SHA1_H 19
20# define SHA1_H 1 20/**
21 21 * @file microhttpd/sha1.h
22#include <stdio.h> 22 * @brief Calculation of SHA-1 digest
23 23 * @author Karlson2k (Evgeny Grin)
24#if defined HAVE_LIMITS_H || _LIBC 24 */
25# include <limits.h> 25
26#endif 26#ifndef MHD_SHA1_H
27 27#define MHD_SHA1_H 1
28/*#include "ansidecl.h"*/ 28
29 29#include "mhd_options.h"
30/* The following contortions are an attempt to use the C preprocessor
31 to determine an unsigned integral type that is 32 bits wide. An
32 alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
33 doing that would require that the configure script compile and *run*
34 the resulting executable. Locally running cross-compiled executables
35 is usually not possible. */
36
37#ifdef _LIBC
38# include <sys/types.h>
39typedef u_int32_t sha1_uint32;
40typedef uintptr_t sha1_uintptr;
41#elif defined (HAVE_SYS_TYPES_H) && defined (HAVE_STDINT_H)
42#include <stdint.h> 30#include <stdint.h>
43#include <sys/types.h> 31#ifdef HAVE_STDDEF_H
44typedef uint32_t sha1_uint32; 32#include <stddef.h> /* for size_t */
45typedef uintptr_t sha1_uintptr; 33#endif /* HAVE_STDDEF_H */
46#else 34
47# define INT_MAX_32_BITS 2147483647 35/**
48 36 * SHA-1 digest is kept internally as 5 32-bit words.
49/* If UINT_MAX isn't defined, assume it's a 32-bit type. 37 */
50 This should be valid for all systems GNU cares about because 38#define _SHA1_DIGEST_LENGTH 5
51 that doesn't include 16-bit systems, and only modern systems 39
52 (that certainly have <limits.h>) have 64+-bit integral types. */ 40/**
53 41 * Number of bits in single SHA-1 word
54# ifndef INT_MAX 42 */
55# define INT_MAX INT_MAX_32_BITS 43#define SHA1_WORD_SIZE_BITS 32
56# endif 44
57 45/**
58# if INT_MAX == INT_MAX_32_BITS 46 * Number of bytes in single SHA-1 word
59typedef unsigned int sha1_uint32; 47 */
60# else 48#define SHA1_BYTES_IN_WORD (SHA1_WORD_SIZE_BITS / 8)
61# if SHRT_MAX == INT_MAX_32_BITS 49
62typedef unsigned short sha1_uint32; 50/**
63# else 51 * Size of SHA-1 digest in bytes
64# if LONG_MAX == INT_MAX_32_BITS 52 */
65typedef unsigned long sha1_uint32; 53#define SHA1_DIGEST_SIZE (_SHA1_DIGEST_LENGTH * SHA1_BYTES_IN_WORD)
66# else 54
67/* The following line is intended to evoke an error. 55/**
68 Using #error is not portable enough. */ 56 * Size of SHA-1 digest string in chars including termination NUL
69"Cannot determine unsigned 32-bit data type." 57 */
70# endif 58#define SHA1_DIGEST_STRING_SIZE ((SHA1_DIGEST_SIZE) * 2 + 1)
71# endif 59
72# endif 60/**
73#endif 61 * Size of single processing block in bits
74 62 */
75#ifdef __cplusplus 63#define SHA1_BLOCK_SIZE_BITS 512
76extern "C" { 64
77#endif 65/**
78 66 * Size of single processing block in bytes
79/* Structure to save state of computation between the single steps. */ 67 */
68#define SHA1_BLOCK_SIZE (SHA1_BLOCK_SIZE_BITS / 8)
69
70
80struct sha1_ctx 71struct sha1_ctx
81{ 72{
82 sha1_uint32 A; 73 uint32_t H[_SHA1_DIGEST_LENGTH]; /**< Intermediate hash value / digest at end of calculation */
83 sha1_uint32 B; 74 uint8_t buffer[SHA1_BLOCK_SIZE]; /**< SHA256 input data buffer */
84 sha1_uint32 C; 75 uint64_t count; /**< number of bytes, mod 2^64 */
85 sha1_uint32 D;
86 sha1_uint32 E;
87
88 sha1_uint32 total[2];
89 sha1_uint32 buflen;
90 sha1_uint32 buffer[32];
91}; 76};
92 77
93 78/**
94/* Initialize structure containing state of computation. */ 79 * Initialise structure for SHA-1 calculation.
95extern void sha1_init_ctx (struct sha1_ctx *ctx); 80 *
96 81 * @param ctx must be a `struct sha1_ctx *`
97/* Starting with the result of former calls of this function (or the 82 */
98 initialization function update the context for the next LEN bytes 83void
99 starting at BUFFER. 84MHD_SHA1_init (void *ctx_);
100 It is necessary that LEN is a multiple of 64!!! */ 85
101extern void sha1_process_block (const void *buffer, size_t len, 86
102 struct sha1_ctx *ctx); 87/**
103 88 * Process portion of bytes.
104/* Starting with the result of former calls of this function (or the 89 *
105 initialization function update the context for the next LEN bytes 90 * @param ctx_ must be a `struct sha1_ctx *`
106 starting at BUFFER. 91 * @param data bytes to add to hash
107 It is NOT required that LEN is a multiple of 64. */ 92 * @param length number of bytes in @a data
108extern void sha1_process_bytes (const void *buffer, size_t len, 93 */
109 struct sha1_ctx *ctx); 94void
110 95MHD_SHA1_update (void *ctx_,
111/* Process the remaining bytes in the buffer and put result from CTX 96 const uint8_t *data,
112 in first 20 bytes following RESBUF. The result is always in little 97 size_t length);
113 endian byte order, so that a byte-wise output yields to the wanted 98
114 ASCII representation of the message digest. 99
115 100/**
116 IMPORTANT: On some systems it is required that RESBUF be correctly 101 * Finalise SHA-1 calculation, return digest.
117 aligned for a 32 bits value. */ 102 *
118extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf); 103 * @param ctx_ must be a `struct sha1_ctx *`
119 104 * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
120 105 */
121/* Put result from CTX in first 20 bytes following RESBUF. The result is 106void
122 always in little endian byte order, so that a byte-wise output yields 107MHD_SHA1_finish (void *ctx_,
123 to the wanted ASCII representation of the message digest. 108 uint8_t digest[SHA1_DIGEST_SIZE]);
124 109
125 IMPORTANT: On some systems it is required that RESBUF is correctly 110#endif /* MHD_SHA1_H */
126 aligned for a 32 bits value. */
127extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf);
128
129
130/* Compute SHA1 message digest for bytes read from STREAM. The
131 resulting message digest number will be written into the 20 bytes
132 beginning at RESBLOCK. */
133extern int sha1_stream (FILE *stream, void *resblock);
134
135/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The
136 result is always in little endian byte order, so that a byte-wise
137 output yields to the wanted ASCII representation of the message
138 digest. */
139extern void *sha1_buffer (const char *buffer, size_t len, void *resblock);
140
141#ifdef __cplusplus
142}
143#endif
144
145#endif
diff --git a/src/microhttpd_ws/test_websocket.c b/src/microhttpd_ws/test_websocket.c
index 0034eb4c..29a4661a 100644
--- a/src/microhttpd_ws/test_websocket.c
+++ b/src/microhttpd_ws/test_websocket.c
@@ -27,8 +27,13 @@
27#include <stdlib.h> 27#include <stdlib.h>
28#include <string.h> 28#include <string.h>
29#include <stdio.h> 29#include <stdio.h>
30#include <stdint.h>
30#include <time.h> 31#include <time.h>
31 32
33#if SIZE_MAX >= 0x100000000
34 #define ENABLE_64BIT_TESTS 1
35#endif
36
32int disable_alloc = 0; 37int disable_alloc = 0;
33size_t open_allocs = 0; 38size_t open_allocs = 0;
34 39
@@ -73,6 +78,20 @@ test_free (void*buf)
73 free (buf); 78 free (buf);
74} 79}
75 80
81/**
82 * Custom `rng()` function used for client mode tests
83 */
84static size_t
85test_rng (void*cls, void*buf, size_t buf_len)
86{
87 for (size_t i = 0; i < buf_len; ++i)
88 {
89 ((char*) buf) [i] = (char) (rand () % 0xFF);
90 }
91
92 return buf_len;
93}
94
76 95
77/** 96/**
78 * Helper function which allocates a big amount of data 97 * Helper function which allocates a big amount of data
@@ -126,7 +145,14 @@ test_decode_single (unsigned int test_line,
126 int ret = MHD_WEBSOCKET_STATUS_OK; 145 int ret = MHD_WEBSOCKET_STATUS_OK;
127 146
128 /* initialize stream */ 147 /* initialize stream */
129 ret = MHD_websocket_stream_init (&ws, flags, max_payload_size); 148 ret = MHD_websocket_stream_init2 (&ws,
149 flags,
150 max_payload_size,
151 malloc,
152 realloc,
153 free,
154 NULL,
155 test_rng);
130 if (MHD_WEBSOCKET_STATUS_OK != ret) 156 if (MHD_WEBSOCKET_STATUS_OK != ret)
131 { 157 {
132 fprintf (stderr, 158 fprintf (stderr,
@@ -267,7 +293,7 @@ test_decode_single (unsigned int test_line,
267 293
268/** 294/**
269 * Test procedure for `MHD_websocket_stream_init()` and 295 * Test procedure for `MHD_websocket_stream_init()` and
270 * `MHD_websocket_stream_init()2` 296 * `MHD_websocket_stream_init2()`
271 */ 297 */
272int 298int
273test_inits () 299test_inits ()
@@ -281,15 +307,19 @@ test_inits ()
281 All valid flags 307 All valid flags
282 ------------------------------------------------------------------------------ 308 ------------------------------------------------------------------------------
283 */ 309 */
284 /* Regular test: all valid flags for init */ 310 /* Regular test: all valid flags for init (only the even ones work) */
285 for (int i = 0; i < 7; ++i) 311 for (int i = 0; i < 7; ++i)
286 { 312 {
287 ws = NULL; 313 ws = NULL;
288 ret = MHD_websocket_stream_init (&ws, 314 ret = MHD_websocket_stream_init (&ws,
289 i, 315 i,
290 0); 316 0);
291 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 317 if (((0 == (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
292 (NULL == ws) ) 318 ((MHD_WEBSOCKET_STATUS_OK != ret) ||
319 (NULL == ws))) ||
320 ((0 != (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
321 ((MHD_WEBSOCKET_STATUS_OK == ret) ||
322 (NULL != ws))))
293 { 323 {
294 fprintf (stderr, 324 fprintf (stderr,
295 "Init test failed in line %u for flags %d.\n", 325 "Init test failed in line %u for flags %d.\n",
@@ -312,7 +342,9 @@ test_inits ()
312 0, 342 0,
313 test_malloc, 343 test_malloc,
314 test_realloc, 344 test_realloc,
315 test_free); 345 test_free,
346 NULL,
347 test_rng);
316 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 348 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
317 (NULL == ws) ) 349 (NULL == ws) )
318 { 350 {
@@ -361,7 +393,9 @@ test_inits ()
361 0, 393 0,
362 test_malloc, 394 test_malloc,
363 test_realloc, 395 test_realloc,
364 test_free); 396 test_free,
397 NULL,
398 NULL);
365 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 399 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
366 (NULL != ws) ) 400 (NULL != ws) )
367 { 401 {
@@ -410,7 +444,9 @@ test_inits ()
410 0, 444 0,
411 test_malloc, 445 test_malloc,
412 test_realloc, 446 test_realloc,
413 test_free); 447 test_free,
448 NULL,
449 NULL);
414 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 450 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
415 (NULL == ws) ) 451 (NULL == ws) )
416 { 452 {
@@ -451,7 +487,9 @@ test_inits ()
451 1, 487 1,
452 test_malloc, 488 test_malloc,
453 test_realloc, 489 test_realloc,
454 test_free); 490 test_free,
491 NULL,
492 NULL);
455 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 493 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
456 (NULL == ws) ) 494 (NULL == ws) )
457 { 495 {
@@ -492,7 +530,9 @@ test_inits ()
492 1000, 530 1000,
493 test_malloc, 531 test_malloc,
494 test_realloc, 532 test_realloc,
495 test_free); 533 test_free,
534 NULL,
535 NULL);
496 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 536 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
497 (NULL == ws) ) 537 (NULL == ws) )
498 { 538 {
@@ -506,6 +546,7 @@ test_inits ()
506 MHD_websocket_stream_free (ws); 546 MHD_websocket_stream_free (ws);
507 ws = NULL; 547 ws = NULL;
508 } 548 }
549#ifdef ENABLE_64BIT_TESTS
509 /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init */ 550 /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init */
510 ws = NULL; 551 ws = NULL;
511 ret = MHD_websocket_stream_init (&ws, 552 ret = MHD_websocket_stream_init (&ws,
@@ -533,7 +574,9 @@ test_inits ()
533 (uint64_t) 0x7FFFFFFFFFFFFFFF, 574 (uint64_t) 0x7FFFFFFFFFFFFFFF,
534 test_malloc, 575 test_malloc,
535 test_realloc, 576 test_realloc,
536 test_free); 577 test_free,
578 NULL,
579 NULL);
537 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 580 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
538 (NULL == ws) ) 581 (NULL == ws) )
539 { 582 {
@@ -574,7 +617,9 @@ test_inits ()
574 (uint64_t) 0x8000000000000000, 617 (uint64_t) 0x8000000000000000,
575 test_malloc, 618 test_malloc,
576 test_realloc, 619 test_realloc,
577 test_free); 620 test_free,
621 NULL,
622 NULL);
578 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 623 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
579 (NULL != ws) ) 624 (NULL != ws) )
580 { 625 {
@@ -588,6 +633,7 @@ test_inits ()
588 MHD_websocket_stream_free (ws); 633 MHD_websocket_stream_free (ws);
589 ws = NULL; 634 ws = NULL;
590 } 635 }
636#endif
591 637
592 /* 638 /*
593 ------------------------------------------------------------------------------ 639 ------------------------------------------------------------------------------
@@ -621,7 +667,9 @@ test_inits ()
621 0, 667 0,
622 test_malloc, 668 test_malloc,
623 test_realloc, 669 test_realloc,
624 test_free); 670 test_free,
671 NULL,
672 NULL);
625 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 673 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
626 (NULL != ws) ) 674 (NULL != ws) )
627 { 675 {
@@ -643,7 +691,9 @@ test_inits ()
643 0, 691 0,
644 NULL, 692 NULL,
645 test_realloc, 693 test_realloc,
646 test_free); 694 test_free,
695 NULL,
696 NULL);
647 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 697 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
648 (NULL != ws) ) 698 (NULL != ws) )
649 { 699 {
@@ -665,7 +715,9 @@ test_inits ()
665 0, 715 0,
666 test_malloc, 716 test_malloc,
667 NULL, 717 NULL,
668 test_free); 718 test_free,
719 NULL,
720 NULL);
669 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 721 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
670 (NULL != ws) ) 722 (NULL != ws) )
671 { 723 {
@@ -687,6 +739,8 @@ test_inits ()
687 0, 739 0,
688 test_malloc, 740 test_malloc,
689 test_realloc, 741 test_realloc,
742 NULL,
743 NULL,
690 NULL); 744 NULL);
691 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || 745 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
692 (NULL != ws) ) 746 (NULL != ws) )
@@ -701,13 +755,109 @@ test_inits ()
701 MHD_websocket_stream_free (ws); 755 MHD_websocket_stream_free (ws);
702 ws = NULL; 756 ws = NULL;
703 } 757 }
758 /* Regular test: rng given for server mode (will be ignored) */
759 ws = NULL;
760 ret = MHD_websocket_stream_init2 (&ws,
761 MHD_WEBSOCKET_FLAG_SERVER
762 | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
763 0,
764 test_malloc,
765 test_realloc,
766 test_free,
767 NULL,
768 test_rng);
769 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
770 (NULL == ws) )
771 {
772 fprintf (stderr,
773 "Init test failed in line %u.\n",
774 (unsigned int) __LINE__);
775 ++failed;
776 }
777 if (NULL != ws)
778 {
779 MHD_websocket_stream_free (ws);
780 ws = NULL;
781 }
782 /* Regular test: cls_rng given for server mode (will be ignored) */
783 ws = NULL;
784 ret = MHD_websocket_stream_init2 (&ws,
785 MHD_WEBSOCKET_FLAG_SERVER
786 | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
787 0,
788 test_malloc,
789 test_realloc,
790 test_free,
791 (void*) 12345,
792 test_rng);
793 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
794 (NULL == ws) )
795 {
796 fprintf (stderr,
797 "Init test failed in line %u.\n",
798 (unsigned int) __LINE__);
799 ++failed;
800 }
801 if (NULL != ws)
802 {
803 MHD_websocket_stream_free (ws);
804 ws = NULL;
805 }
806 /* Regular test: rng given for client mode */
807 ws = NULL;
808 ret = MHD_websocket_stream_init2 (&ws,
809 MHD_WEBSOCKET_FLAG_CLIENT
810 | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
811 0,
812 test_malloc,
813 test_realloc,
814 test_free,
815 NULL,
816 test_rng);
817 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
818 (NULL == ws) )
819 {
820 fprintf (stderr,
821 "Init test failed in line %u.\n",
822 (unsigned int) __LINE__);
823 ++failed;
824 }
825 if (NULL != ws)
826 {
827 MHD_websocket_stream_free (ws);
828 ws = NULL;
829 }
830 /* Fail test: rng not given for client mode */
831 ws = NULL;
832 ret = MHD_websocket_stream_init2 (&ws,
833 MHD_WEBSOCKET_FLAG_CLIENT
834 | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
835 0,
836 test_malloc,
837 test_realloc,
838 test_free,
839 NULL,
840 NULL);
841 if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
842 (NULL != ws) )
843 {
844 fprintf (stderr,
845 "Init test failed in line %u %u.\n",
846 (unsigned int) __LINE__, ret);
847 ++failed;
848 }
849 if (NULL != ws)
850 {
851 MHD_websocket_stream_free (ws);
852 ws = NULL;
853 }
704 854
705 return failed != 0 ? 0x01 : 0x00; 855 return failed != 0 ? 0x01 : 0x00;
706} 856}
707 857
708 858
709/** 859/**
710 * Test procedure for `MHD_websocket_create_accept()` 860 * Test procedure for `MHD_websocket_create_accept_header()`
711 */ 861 */
712int 862int
713test_accept () 863test_accept ()
@@ -723,8 +873,8 @@ test_accept ()
723 */ 873 */
724 /* Regular test: Test case from RFC6455 4.2.2 */ 874 /* Regular test: Test case from RFC6455 4.2.2 */
725 memset (accept_key, 0, 29); 875 memset (accept_key, 0, 29);
726 ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==", 876 ret = MHD_websocket_create_accept_header ("dGhlIHNhbXBsZSBub25jZQ==",
727 accept_key); 877 accept_key);
728 if ((MHD_WEBSOCKET_STATUS_OK != ret) || 878 if ((MHD_WEBSOCKET_STATUS_OK != ret) ||
729 (0 != memcmp (accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", 29))) 879 (0 != memcmp (accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", 29)))
730 { 880 {
@@ -741,9 +891,9 @@ test_accept ()
741 */ 891 */
742 /* Fail test: missing sec-key value */ 892 /* Fail test: missing sec-key value */
743 memset (accept_key, 0, 29); 893 memset (accept_key, 0, 29);
744 ret = MHD_websocket_create_accept (NULL, 894 ret = MHD_websocket_create_accept_header (NULL,
745 accept_key); 895 accept_key);
746 if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) 896 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
747 { 897 {
748 fprintf (stderr, 898 fprintf (stderr,
749 "Accept test failed in line %u.\n", 899 "Accept test failed in line %u.\n",
@@ -752,8 +902,8 @@ test_accept ()
752 } 902 }
753 /* Fail test: missing accept variable */ 903 /* Fail test: missing accept variable */
754 memset (accept_key, 0, 29); 904 memset (accept_key, 0, 29);
755 ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==", 905 ret = MHD_websocket_create_accept_header ("dGhlIHNhbXBsZSBub25jZQ==",
756 NULL); 906 NULL);
757 if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) 907 if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret)
758 { 908 {
759 fprintf (stderr, 909 fprintf (stderr,
@@ -1038,7 +1188,7 @@ test_decodes ()
1038 12, 1188 12,
1039 NULL, 1189 NULL,
1040 0, 1190 0,
1041 MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, 1191 MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
1042 MHD_WEBSOCKET_VALIDITY_VALID, 1192 MHD_WEBSOCKET_VALIDITY_VALID,
1043 6); 1193 6);
1044 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */ 1194 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */
@@ -1080,7 +1230,7 @@ test_decodes ()
1080 18, 1230 18,
1081 "\x01\x02\x03", 1231 "\x01\x02\x03",
1082 3, 1232 3,
1083 MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, 1233 MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
1084 MHD_WEBSOCKET_VALIDITY_VALID, 1234 MHD_WEBSOCKET_VALIDITY_VALID,
1085 9); 1235 9);
1086 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */ 1236 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */
@@ -1097,6 +1247,62 @@ test_decodes ()
1097 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT, 1247 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
1098 MHD_WEBSOCKET_VALIDITY_VALID, 1248 MHD_WEBSOCKET_VALIDITY_VALID,
1099 18); 1249 18);
1250 /* Regular test: Fragmented binary frame with payload, fragments to the caller, 1st call */
1251 failed += test_decode_single (__LINE__,
1252 MHD_WEBSOCKET_FLAG_SERVER
1253 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
1254 0,
1255 1,
1256 0,
1257 "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
1258 36,
1259 "\x01\x02\x03",
1260 3,
1261 MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
1262 MHD_WEBSOCKET_VALIDITY_VALID,
1263 9);
1264 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */
1265 failed += test_decode_single (__LINE__,
1266 MHD_WEBSOCKET_FLAG_SERVER
1267 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
1268 0,
1269 2,
1270 0,
1271 "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
1272 36,
1273 "\x04\x05\x06",
1274 3,
1275 MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
1276 MHD_WEBSOCKET_VALIDITY_VALID,
1277 18);
1278 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 3rd call */
1279 failed += test_decode_single (__LINE__,
1280 MHD_WEBSOCKET_FLAG_SERVER
1281 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
1282 0,
1283 3,
1284 0,
1285 "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
1286 36,
1287 "\x07\x08\x09",
1288 3,
1289 MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
1290 MHD_WEBSOCKET_VALIDITY_VALID,
1291 27);
1292 /* Regular test: Fragmented binary frame without payload, fragments to the caller, 4th call */
1293 failed += test_decode_single (__LINE__,
1294 MHD_WEBSOCKET_FLAG_SERVER
1295 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
1296 0,
1297 4,
1298 0,
1299 "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
1300 36,
1301 "\x0A\x0B\x0C",
1302 3,
1303 MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
1304 MHD_WEBSOCKET_VALIDITY_VALID,
1305 36);
1100 /* Regular test: Binary frame with bytes which look like invalid UTF-8 character */ 1306 /* Regular test: Binary frame with bytes which look like invalid UTF-8 character */
1101 failed += test_decode_single (__LINE__, 1307 failed += test_decode_single (__LINE__,
1102 MHD_WEBSOCKET_FLAG_SERVER 1308 MHD_WEBSOCKET_FLAG_SERVER
@@ -1167,7 +1373,7 @@ test_decodes ()
1167 17, 1373 17,
1168 "H\xC3", 1374 "H\xC3",
1169 2, 1375 2,
1170 MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, 1376 MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT,
1171 MHD_WEBSOCKET_VALIDITY_VALID, 1377 MHD_WEBSOCKET_VALIDITY_VALID,
1172 8); 1378 8);
1173 /* Regular test: Fragmented binary frame with bytes which look like valid UTF-8 sequence, 1379 /* Regular test: Fragmented binary frame with bytes which look like valid UTF-8 sequence,
@@ -1802,7 +2008,7 @@ test_decodes ()
1802 17, 2008 17,
1803 "Hel", 2009 "Hel",
1804 3, 2010 3,
1805 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 2011 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
1806 MHD_WEBSOCKET_VALIDITY_VALID, 2012 MHD_WEBSOCKET_VALIDITY_VALID,
1807 9); 2013 9);
1808 /* Regular test: Fragmented, masked text frame, we are the server and want fragments, second call */ 2014 /* Regular test: Fragmented, masked text frame, we are the server and want fragments, second call */
@@ -1833,6 +2039,48 @@ test_decodes ()
1833 MHD_WEBSOCKET_STATUS_OK, 2039 MHD_WEBSOCKET_STATUS_OK,
1834 MHD_WEBSOCKET_VALIDITY_VALID, 2040 MHD_WEBSOCKET_VALIDITY_VALID,
1835 17); 2041 17);
2042 /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 1st call */
2043 failed += test_decode_single (__LINE__,
2044 MHD_WEBSOCKET_FLAG_SERVER
2045 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
2046 0,
2047 1,
2048 0,
2049 "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
2050 23,
2051 "Hel",
2052 3,
2053 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
2054 MHD_WEBSOCKET_VALIDITY_VALID,
2055 9);
2056 /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 2nd call */
2057 failed += test_decode_single (__LINE__,
2058 MHD_WEBSOCKET_FLAG_SERVER
2059 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
2060 0,
2061 2,
2062 0,
2063 "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
2064 23,
2065 "l",
2066 1,
2067 MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT,
2068 MHD_WEBSOCKET_VALIDITY_VALID,
2069 16);
2070 /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 3rd call */
2071 failed += test_decode_single (__LINE__,
2072 MHD_WEBSOCKET_FLAG_SERVER
2073 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
2074 0,
2075 3,
2076 0,
2077 "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
2078 23,
2079 "o",
2080 1,
2081 MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
2082 MHD_WEBSOCKET_VALIDITY_VALID,
2083 23);
1836 2084
1837 2085
1838 /* 2086 /*
@@ -2255,6 +2503,7 @@ test_decodes ()
2255 free (buf2); 2503 free (buf2);
2256 buf2 = NULL; 2504 buf2 = NULL;
2257 } 2505 }
2506#ifdef ENABLE_64BIT_TESTS
2258 /* Edge test (success): Maximum allowed length (here is only the header checked) */ 2507 /* Edge test (success): Maximum allowed length (here is only the header checked) */
2259 failed += test_decode_single (__LINE__, 2508 failed += test_decode_single (__LINE__,
2260 MHD_WEBSOCKET_FLAG_SERVER 2509 MHD_WEBSOCKET_FLAG_SERVER
@@ -2269,6 +2518,23 @@ test_decodes ()
2269 MHD_WEBSOCKET_STATUS_OK, 2518 MHD_WEBSOCKET_STATUS_OK,
2270 MHD_WEBSOCKET_VALIDITY_VALID, 2519 MHD_WEBSOCKET_VALIDITY_VALID,
2271 10); 2520 10);
2521#else
2522 /* Edge test (fail): Maximum allowed length
2523 (the size is allowed, but the system cannot handle this amount of memory) */
2524 failed += test_decode_single (__LINE__,
2525 MHD_WEBSOCKET_FLAG_SERVER
2526 | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
2527 0,
2528 1,
2529 0,
2530 "\x81\xff\x7f\xff\xff\xff\xff\xff\xff\xff",
2531 10,
2532 NULL,
2533 0,
2534 MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED,
2535 MHD_WEBSOCKET_VALIDITY_INVALID,
2536 10);
2537#endif
2272 /* Edge test (fail): Too big payload length */ 2538 /* Edge test (fail): Too big payload length */
2273 failed += test_decode_single (__LINE__, 2539 failed += test_decode_single (__LINE__,
2274 MHD_WEBSOCKET_FLAG_SERVER 2540 MHD_WEBSOCKET_FLAG_SERVER
@@ -2447,7 +2713,7 @@ test_decodes ()
2447 17, 2713 17,
2448 "Hel", 2714 "Hel",
2449 3, 2715 3,
2450 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 2716 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
2451 MHD_WEBSOCKET_VALIDITY_VALID, 2717 MHD_WEBSOCKET_VALIDITY_VALID,
2452 9); 2718 9);
2453 /* Edge test (success): Fragmented frames with the sum of payload greater than 2719 /* Edge test (success): Fragmented frames with the sum of payload greater than
@@ -3247,7 +3513,7 @@ test_decodes ()
3247 28, 3513 28,
3248 "This is my n", 3514 "This is my n",
3249 12, 3515 12,
3250 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 3516 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
3251 MHD_WEBSOCKET_VALIDITY_VALID, 3517 MHD_WEBSOCKET_VALIDITY_VALID,
3252 19); 3518 19);
3253 /* Regular test: UTF-8 sequence between fragments, fragmentation for the caller, 2nd call */ 3519 /* Regular test: UTF-8 sequence between fragments, fragmentation for the caller, 2nd call */
@@ -3276,7 +3542,7 @@ test_decodes ()
3276 14, 3542 14,
3277 NULL, 3543 NULL,
3278 0, 3544 0,
3279 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 3545 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
3280 MHD_WEBSOCKET_VALIDITY_VALID, 3546 MHD_WEBSOCKET_VALIDITY_VALID,
3281 7); 3547 7);
3282 /* Edge test (success): UTF-8 sequence between fragments, but nothing before, fragmentation for the caller, 2nd call */ 3548 /* Edge test (success): UTF-8 sequence between fragments, but nothing before, fragmentation for the caller, 2nd call */
@@ -3664,7 +3930,7 @@ test_decodes ()
3664 35, 3930 35,
3665 "This ", 3931 "This ",
3666 5, 3932 5,
3667 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 3933 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
3668 MHD_WEBSOCKET_VALIDITY_VALID, 3934 MHD_WEBSOCKET_VALIDITY_VALID,
3669 11); 3935 11);
3670 /* Regular test: Fragmented text frame mixed with one ping frame, the caller wants fragments (2nd call) */ 3936 /* Regular test: Fragmented text frame mixed with one ping frame, the caller wants fragments (2nd call) */
@@ -3734,7 +4000,7 @@ test_decodes ()
3734 36, 4000 36,
3735 "This ", 4001 "This ",
3736 5, 4002 5,
3737 MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, 4003 MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
3738 MHD_WEBSOCKET_VALIDITY_VALID, 4004 MHD_WEBSOCKET_VALIDITY_VALID,
3739 11); 4005 11);
3740 /* Fail test: Fragmented text frame mixed with one non-fragmented binary frame; the caller wants fragments; 2nd call */ 4006 /* Fail test: Fragmented text frame mixed with one non-fragmented binary frame; the caller wants fragments; 2nd call */
@@ -4106,7 +4372,7 @@ test_decodes ()
4106 &streambuf_read_len, 4372 &streambuf_read_len,
4107 &payload, 4373 &payload,
4108 &payload_len); 4374 &payload_len);
4109 if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) || 4375 if ((MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT != ret) ||
4110 (3 != payload_len) || 4376 (3 != payload_len) ||
4111 (NULL == payload) || 4377 (NULL == payload) ||
4112 (0 != memcmp ("Hel", payload, 3 + 1))) 4378 (0 != memcmp ("Hel", payload, 3 + 1)))
@@ -4155,7 +4421,7 @@ test_decodes ()
4155 &streambuf_read_len, 4421 &streambuf_read_len,
4156 &payload, 4422 &payload,
4157 &payload_len); 4423 &payload_len);
4158 if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) || 4424 if ((MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT != ret) ||
4159 (2 != payload_len) || 4425 (2 != payload_len) ||
4160 (NULL == payload) || 4426 (NULL == payload) ||
4161 (0 != memcmp ("He", payload, 2 + 1))) 4427 (0 != memcmp ("He", payload, 2 + 1)))
@@ -4195,6 +4461,8 @@ test_decodes ()
4195 MHD_websocket_free (ws, payload); 4461 MHD_websocket_free (ws, payload);
4196 payload = NULL; 4462 payload = NULL;
4197 } 4463 }
4464
4465 MHD_websocket_stream_free (ws);
4198 } 4466 }
4199 else 4467 else
4200 { 4468 {
@@ -4439,7 +4707,9 @@ test_decodes ()
4439 0, 4707 0,
4440 test_malloc, 4708 test_malloc,
4441 test_realloc, 4709 test_realloc,
4442 test_free)) 4710 test_free,
4711 NULL,
4712 NULL))
4443 { 4713 {
4444 size_t streambuf_read_len = 0; 4714 size_t streambuf_read_len = 0;
4445 char*payload = NULL; 4715 char*payload = NULL;
@@ -4484,7 +4754,9 @@ test_decodes ()
4484 0, 4754 0,
4485 test_malloc, 4755 test_malloc,
4486 test_realloc, 4756 test_realloc,
4487 test_free)) 4757 test_free,
4758 NULL,
4759 NULL))
4488 { 4760 {
4489 /* Failure test: No memory allocation after fragmented frame */ 4761 /* Failure test: No memory allocation after fragmented frame */
4490 disable_alloc = 0; 4762 disable_alloc = 0;
@@ -4623,7 +4895,9 @@ test_decodes ()
4623 0, 4895 0,
4624 test_malloc, 4896 test_malloc,
4625 test_realloc, 4897 test_realloc,
4626 test_free)) 4898 test_free,
4899 NULL,
4900 NULL))
4627 { 4901 {
4628 ret = MHD_websocket_decode (ws, 4902 ret = MHD_websocket_decode (ws,
4629 "\x81\x85\x00\x00\x00\x00Hel", 4903 "\x81\x85\x00\x00\x00\x00Hel",
@@ -4674,7 +4948,9 @@ test_decodes ()
4674 0, 4948 0,
4675 test_malloc, 4949 test_malloc,
4676 test_realloc, 4950 test_realloc,
4677 test_free)) 4951 test_free,
4952 NULL,
4953 NULL))
4678 { 4954 {
4679 ret = MHD_websocket_decode (ws, 4955 ret = MHD_websocket_decode (ws,
4680 "\x88\x85\x00\x00\x00\x00Hel", 4956 "\x88\x85\x00\x00\x00\x00Hel",
@@ -4725,7 +5001,9 @@ test_decodes ()
4725 0, 5001 0,
4726 test_malloc, 5002 test_malloc,
4727 test_realloc, 5003 test_realloc,
4728 test_free)) 5004 test_free,
5005 NULL,
5006 NULL))
4729 { 5007 {
4730 ret = MHD_websocket_decode (ws, 5008 ret = MHD_websocket_decode (ws,
4731 "\x01\x85\x00\x00\x00\x00Hello", 5009 "\x01\x85\x00\x00\x00\x00Hello",
@@ -4775,7 +5053,9 @@ test_decodes ()
4775 0, 5053 0,
4776 test_malloc, 5054 test_malloc,
4777 test_realloc, 5055 test_realloc,
4778 test_free)) 5056 test_free,
5057 NULL,
5058 NULL))
4779 { 5059 {
4780 ret = MHD_websocket_decode (ws, 5060 ret = MHD_websocket_decode (ws,
4781 "\x01\x85\x00\x00\x00\x00Hello", 5061 "\x01\x85\x00\x00\x00\x00Hello",
@@ -4841,7 +5121,9 @@ test_decodes ()
4841 0, 5121 0,
4842 test_malloc, 5122 test_malloc,
4843 test_realloc, 5123 test_realloc,
4844 test_free)) 5124 test_free,
5125 NULL,
5126 NULL))
4845 { 5127 {
4846 ret = MHD_websocket_decode (ws, 5128 ret = MHD_websocket_decode (ws,
4847 "\x01\x85\x00\x00\x00\x00Hello", 5129 "\x01\x85\x00\x00\x00\x00Hello",
@@ -4929,9 +5211,14 @@ test_encodes_text ()
4929 size_t frame_len = 0; 5211 size_t frame_len = 0;
4930 int utf8_step = 0; 5212 int utf8_step = 0;
4931 5213
4932 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, 5214 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
4933 MHD_WEBSOCKET_FLAG_CLIENT, 5215 MHD_WEBSOCKET_FLAG_CLIENT,
4934 0)) 5216 0,
5217 malloc,
5218 realloc,
5219 free,
5220 NULL,
5221 test_rng))
4935 { 5222 {
4936 fprintf (stderr, 5223 fprintf (stderr,
4937 "No encode text tests possible due to failed stream init in line %u\n", 5224 "No encode text tests possible due to failed stream init in line %u\n",
@@ -5742,6 +6029,7 @@ test_encodes_text ()
5742 free (buf2); 6029 free (buf2);
5743 buf2 = NULL; 6030 buf2 = NULL;
5744 } 6031 }
6032#ifdef ENABLE_64BIT_TESTS
5745 /* Fail test: frame_len is greater than 0x7FFFFFFFFFFFFFFF 6033 /* Fail test: frame_len is greater than 0x7FFFFFFFFFFFFFFF
5746 (this is the maximum allowed payload size) */ 6034 (this is the maximum allowed payload size) */
5747 frame_len = 0; 6035 frame_len = 0;
@@ -5766,6 +6054,7 @@ test_encodes_text ()
5766 MHD_websocket_free (wss, frame); 6054 MHD_websocket_free (wss, frame);
5767 frame = NULL; 6055 frame = NULL;
5768 } 6056 }
6057#endif
5769 6058
5770 /* 6059 /*
5771 ------------------------------------------------------------------------------ 6060 ------------------------------------------------------------------------------
@@ -6188,7 +6477,9 @@ test_encodes_text ()
6188 0, 6477 0,
6189 test_malloc, 6478 test_malloc,
6190 test_realloc, 6479 test_realloc,
6191 test_free)) 6480 test_free,
6481 NULL,
6482 NULL))
6192 { 6483 {
6193 /* Fail test: allocation while no memory available */ 6484 /* Fail test: allocation while no memory available */
6194 disable_alloc = 1; 6485 disable_alloc = 1;
@@ -6276,9 +6567,14 @@ test_encodes_binary ()
6276 char*frame = NULL; 6567 char*frame = NULL;
6277 size_t frame_len = 0; 6568 size_t frame_len = 0;
6278 6569
6279 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, 6570 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
6280 MHD_WEBSOCKET_FLAG_CLIENT, 6571 MHD_WEBSOCKET_FLAG_CLIENT,
6281 0)) 6572 0,
6573 malloc,
6574 realloc,
6575 free,
6576 NULL,
6577 test_rng))
6282 { 6578 {
6283 fprintf (stderr, 6579 fprintf (stderr,
6284 "No encode binary tests possible due to failed stream init in line %u\n", 6580 "No encode binary tests possible due to failed stream init in line %u\n",
@@ -6688,6 +6984,7 @@ test_encodes_binary ()
6688 free (buf2); 6984 free (buf2);
6689 buf2 = NULL; 6985 buf2 = NULL;
6690 } 6986 }
6987#ifdef ENABLE_64BIT_TESTS
6691 /* Fail test: `frame_len` is greater than 0x7FFFFFFFFFFFFFFF 6988 /* Fail test: `frame_len` is greater than 0x7FFFFFFFFFFFFFFF
6692 (this is the maximum allowed payload size) */ 6989 (this is the maximum allowed payload size) */
6693 frame_len = 0; 6990 frame_len = 0;
@@ -6711,6 +7008,7 @@ test_encodes_binary ()
6711 MHD_websocket_free (wss, frame); 7008 MHD_websocket_free (wss, frame);
6712 frame = NULL; 7009 frame = NULL;
6713 } 7010 }
7011#endif
6714 7012
6715 /* 7013 /*
6716 ------------------------------------------------------------------------------ 7014 ------------------------------------------------------------------------------
@@ -6882,7 +7180,9 @@ test_encodes_binary ()
6882 0, 7180 0,
6883 test_malloc, 7181 test_malloc,
6884 test_realloc, 7182 test_realloc,
6885 test_free)) 7183 test_free,
7184 NULL,
7185 NULL))
6886 { 7186 {
6887 /* Fail test: allocation while no memory available */ 7187 /* Fail test: allocation while no memory available */
6888 disable_alloc = 1; 7188 disable_alloc = 1;
@@ -6968,18 +7268,28 @@ test_encodes_close ()
6968 char*frame = NULL; 7268 char*frame = NULL;
6969 size_t frame_len = 0; 7269 size_t frame_len = 0;
6970 7270
6971 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, 7271 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
6972 MHD_WEBSOCKET_FLAG_CLIENT, 7272 MHD_WEBSOCKET_FLAG_CLIENT,
6973 0)) 7273 0,
7274 malloc,
7275 realloc,
7276 free,
7277 NULL,
7278 test_rng))
6974 { 7279 {
6975 fprintf (stderr, 7280 fprintf (stderr,
6976 "No encode close tests possible due to failed stream init in line %u\n", 7281 "No encode close tests possible due to failed stream init in line %u\n",
6977 (unsigned int) __LINE__); 7282 (unsigned int) __LINE__);
6978 return 0x10; 7283 return 0x10;
6979 } 7284 }
6980 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, 7285 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wss,
6981 MHD_WEBSOCKET_FLAG_SERVER, 7286 MHD_WEBSOCKET_FLAG_SERVER,
6982 0)) 7287 0,
7288 malloc,
7289 realloc,
7290 free,
7291 NULL,
7292 test_rng))
6983 { 7293 {
6984 fprintf (stderr, 7294 fprintf (stderr,
6985 "No encode close tests possible due to failed stream init in line %u\n", 7295 "No encode close tests possible due to failed stream init in line %u\n",
@@ -7623,7 +7933,9 @@ test_encodes_close ()
7623 0, 7933 0,
7624 test_malloc, 7934 test_malloc,
7625 test_realloc, 7935 test_realloc,
7626 test_free)) 7936 test_free,
7937 NULL,
7938 NULL))
7627 { 7939 {
7628 /* Fail test: allocation while no memory available */ 7940 /* Fail test: allocation while no memory available */
7629 disable_alloc = 1; 7941 disable_alloc = 1;
@@ -7709,9 +8021,14 @@ test_encodes_ping ()
7709 char*frame = NULL; 8021 char*frame = NULL;
7710 size_t frame_len = 0; 8022 size_t frame_len = 0;
7711 8023
7712 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, 8024 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
7713 MHD_WEBSOCKET_FLAG_CLIENT, 8025 MHD_WEBSOCKET_FLAG_CLIENT,
7714 0)) 8026 0,
8027 malloc,
8028 realloc,
8029 free,
8030 NULL,
8031 test_rng))
7715 { 8032 {
7716 fprintf (stderr, 8033 fprintf (stderr,
7717 "No encode ping tests possible due to failed stream init in line %u\n", 8034 "No encode ping tests possible due to failed stream init in line %u\n",
@@ -8156,7 +8473,9 @@ test_encodes_ping ()
8156 0, 8473 0,
8157 test_malloc, 8474 test_malloc,
8158 test_realloc, 8475 test_realloc,
8159 test_free)) 8476 test_free,
8477 NULL,
8478 NULL))
8160 { 8479 {
8161 /* Fail test: allocation while no memory available */ 8480 /* Fail test: allocation while no memory available */
8162 disable_alloc = 1; 8481 disable_alloc = 1;
@@ -8240,9 +8559,14 @@ test_encodes_pong ()
8240 char*frame = NULL; 8559 char*frame = NULL;
8241 size_t frame_len = 0; 8560 size_t frame_len = 0;
8242 8561
8243 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, 8562 if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init2 (&wsc,
8244 MHD_WEBSOCKET_FLAG_CLIENT, 8563 MHD_WEBSOCKET_FLAG_CLIENT,
8245 0)) 8564 0,
8565 malloc,
8566 realloc,
8567 free,
8568 NULL,
8569 test_rng))
8246 { 8570 {
8247 fprintf (stderr, 8571 fprintf (stderr,
8248 "No encode pong tests possible due to failed stream init in line %u\n", 8572 "No encode pong tests possible due to failed stream init in line %u\n",
@@ -8687,7 +9011,9 @@ test_encodes_pong ()
8687 0, 9011 0,
8688 test_malloc, 9012 test_malloc,
8689 test_realloc, 9013 test_realloc,
8690 test_free)) 9014 test_free,
9015 NULL,
9016 NULL))
8691 { 9017 {
8692 /* Fail test: allocation while no memory available */ 9018 /* Fail test: allocation while no memory available */
8693 disable_alloc = 1; 9019 disable_alloc = 1;
@@ -8955,6 +9281,793 @@ test_split_close_reason ()
8955} 9281}
8956 9282
8957 9283
9284/**
9285 * Test procedure for `MHD_websocket_check_http_version()`
9286 */
9287int
9288test_check_http_version ()
9289{
9290 int failed = 0;
9291 int ret;
9292
9293 /*
9294 ------------------------------------------------------------------------------
9295 Version check with valid HTTP version syntax
9296 ------------------------------------------------------------------------------
9297 */
9298 /* Regular test: HTTP/1.1 */
9299 ret = MHD_websocket_check_http_version ("HTTP/1.1");
9300 if (MHD_WEBSOCKET_STATUS_OK != ret)
9301 {
9302 fprintf (stderr,
9303 "check_http_version test failed in line %u.\n",
9304 (unsigned int) __LINE__);
9305 ++failed;
9306 }
9307 /* Regular test: HTTP/1.2 */
9308 ret = MHD_websocket_check_http_version ("HTTP/1.2");
9309 if (MHD_WEBSOCKET_STATUS_OK != ret)
9310 {
9311 fprintf (stderr,
9312 "check_http_version test failed in line %u.\n",
9313 (unsigned int) __LINE__);
9314 ++failed;
9315 }
9316 /* Regular test: HTTP/1.10 */
9317 ret = MHD_websocket_check_http_version ("HTTP/1.10");
9318 if (MHD_WEBSOCKET_STATUS_OK != ret)
9319 {
9320 fprintf (stderr,
9321 "check_http_version test failed in line %u.\n",
9322 (unsigned int) __LINE__);
9323 ++failed;
9324 }
9325 /* Regular test: HTTP/2.0 */
9326 ret = MHD_websocket_check_http_version ("HTTP/2.0");
9327 if (MHD_WEBSOCKET_STATUS_OK != ret)
9328 {
9329 fprintf (stderr,
9330 "check_http_version test failed in line %u.\n",
9331 (unsigned int) __LINE__);
9332 ++failed;
9333 }
9334 /* Regular test: HTTP/3.0 */
9335 ret = MHD_websocket_check_http_version ("HTTP/3.0");
9336 if (MHD_WEBSOCKET_STATUS_OK != ret)
9337 {
9338 fprintf (stderr,
9339 "check_http_version test failed in line %u.\n",
9340 (unsigned int) __LINE__);
9341 ++failed;
9342 }
9343 /* Fail test: HTTP/1.0 */
9344 ret = MHD_websocket_check_http_version ("HTTP/1.0");
9345 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9346 {
9347 fprintf (stderr,
9348 "check_http_version test failed in line %u.\n",
9349 (unsigned int) __LINE__);
9350 ++failed;
9351 }
9352 /* Fail test: HTTP/0.9 */
9353 ret = MHD_websocket_check_http_version ("HTTP/0.9");
9354 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9355 {
9356 fprintf (stderr,
9357 "check_http_version test failed in line %u.\n",
9358 (unsigned int) __LINE__);
9359 ++failed;
9360 }
9361
9362 /*
9363 ------------------------------------------------------------------------------
9364 Version check edge cases
9365 ------------------------------------------------------------------------------
9366 */
9367 /* Edge test (success): HTTP/123.45 */
9368 ret = MHD_websocket_check_http_version ("HTTP/123.45");
9369 if (MHD_WEBSOCKET_STATUS_OK != ret)
9370 {
9371 fprintf (stderr,
9372 "check_http_version test failed in line %u.\n",
9373 (unsigned int) __LINE__);
9374 ++failed;
9375 }
9376 /* Edge test (success): HTTP/1.45 */
9377 ret = MHD_websocket_check_http_version ("HTTP/1.45");
9378 if (MHD_WEBSOCKET_STATUS_OK != ret)
9379 {
9380 fprintf (stderr,
9381 "check_http_version test failed in line %u.\n",
9382 (unsigned int) __LINE__);
9383 ++failed;
9384 }
9385 /* Edge test (success): HTTP/01.1 */
9386 ret = MHD_websocket_check_http_version ("HTTP/01.1");
9387 if (MHD_WEBSOCKET_STATUS_OK != ret)
9388 {
9389 fprintf (stderr,
9390 "check_http_version test failed in line %u.\n",
9391 (unsigned int) __LINE__);
9392 ++failed;
9393 }
9394 /* Edge test (success): HTTP/0001.1 */
9395 ret = MHD_websocket_check_http_version ("HTTP/0001.1");
9396 if (MHD_WEBSOCKET_STATUS_OK != ret)
9397 {
9398 fprintf (stderr,
9399 "check_http_version test failed in line %u.\n",
9400 (unsigned int) __LINE__);
9401 ++failed;
9402 }
9403 /* Edge test (success): HTTP/1.01 */
9404 ret = MHD_websocket_check_http_version ("HTTP/1.01");
9405 if (MHD_WEBSOCKET_STATUS_OK != ret)
9406 {
9407 fprintf (stderr,
9408 "check_http_version test failed in line %u.\n",
9409 (unsigned int) __LINE__);
9410 ++failed;
9411 }
9412 /* Edge test (success): HTTP/1.0001 */
9413 ret = MHD_websocket_check_http_version ("HTTP/1.0001");
9414 if (MHD_WEBSOCKET_STATUS_OK != ret)
9415 {
9416 fprintf (stderr,
9417 "check_http_version test failed in line %u.\n",
9418 (unsigned int) __LINE__);
9419 ++failed;
9420 }
9421 /* Edge test (success): HTTP/0001.0001 */
9422 ret = MHD_websocket_check_http_version ("HTTP/0001.0001");
9423 if (MHD_WEBSOCKET_STATUS_OK != ret)
9424 {
9425 fprintf (stderr,
9426 "check_http_version test failed in line %u.\n",
9427 (unsigned int) __LINE__);
9428 ++failed;
9429 }
9430 /* Edge test (success): HTTP/2.000 */
9431 ret = MHD_websocket_check_http_version ("HTTP/2.000");
9432 if (MHD_WEBSOCKET_STATUS_OK != ret)
9433 {
9434 fprintf (stderr,
9435 "check_http_version test failed in line %u.\n",
9436 (unsigned int) __LINE__);
9437 ++failed;
9438 }
9439 /* Edge test (fail): HTTP/0.0 */
9440 ret = MHD_websocket_check_http_version ("HTTP/0.0");
9441 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9442 {
9443 fprintf (stderr,
9444 "check_http_version test failed in line %u.\n",
9445 (unsigned int) __LINE__);
9446 ++failed;
9447 }
9448 /* Edge test (fail): HTTP/00.0 */
9449 ret = MHD_websocket_check_http_version ("HTTP/00.0");
9450 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9451 {
9452 fprintf (stderr,
9453 "check_http_version test failed in line %u.\n",
9454 (unsigned int) __LINE__);
9455 ++failed;
9456 }
9457 /* Edge test (fail): HTTP/00.0 */
9458 ret = MHD_websocket_check_http_version ("HTTP/0.00");
9459 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9460 {
9461 fprintf (stderr,
9462 "check_http_version test failed in line %u.\n",
9463 (unsigned int) __LINE__);
9464 ++failed;
9465 }
9466
9467 /*
9468 ------------------------------------------------------------------------------
9469 Invalid version syntax
9470 ------------------------------------------------------------------------------
9471 */
9472 /* Fail test: (empty string) */
9473 ret = MHD_websocket_check_http_version ("");
9474 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9475 {
9476 fprintf (stderr,
9477 "check_http_version test failed in line %u.\n",
9478 (unsigned int) __LINE__);
9479 ++failed;
9480 }
9481 /* Fail test: http/1.1 */
9482 ret = MHD_websocket_check_http_version ("http/1.1");
9483 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9484 {
9485 fprintf (stderr,
9486 "check_http_version test failed in line %u.\n",
9487 (unsigned int) __LINE__);
9488 ++failed;
9489 }
9490 /* Fail test: "HTTP / 1.1" */
9491 ret = MHD_websocket_check_http_version ("HTTP / 1.1");
9492 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9493 {
9494 fprintf (stderr,
9495 "check_http_version test failed in line %u.\n",
9496 (unsigned int) __LINE__);
9497 ++failed;
9498 }
9499
9500 /*
9501 ------------------------------------------------------------------------------
9502 Missing parameters
9503 ------------------------------------------------------------------------------
9504 */
9505 /* Fail test: NULL as version */
9506 ret = MHD_websocket_check_http_version (NULL);
9507 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9508 {
9509 fprintf (stderr,
9510 "check_http_version test failed in line %u.\n",
9511 (unsigned int) __LINE__);
9512 ++failed;
9513 }
9514
9515 return failed != 0 ? 0x200 : 0x00;
9516}
9517
9518
9519/**
9520 * Test procedure for `MHD_websocket_check_connection_header()`
9521 */
9522int
9523test_check_connection_header ()
9524{
9525 int failed = 0;
9526 int ret;
9527
9528 /*
9529 ------------------------------------------------------------------------------
9530 Check with valid Connection header syntax
9531 ------------------------------------------------------------------------------
9532 */
9533 /* Regular test: Upgrade */
9534 ret = MHD_websocket_check_connection_header ("Upgrade");
9535 if (MHD_WEBSOCKET_STATUS_OK != ret)
9536 {
9537 fprintf (stderr,
9538 "check_connection_header test failed in line %u.\n",
9539 (unsigned int) __LINE__);
9540 ++failed;
9541 }
9542 /* Regular test: keep-alive, Upgrade */
9543 ret = MHD_websocket_check_connection_header ("keep-alive, Upgrade");
9544 if (MHD_WEBSOCKET_STATUS_OK != ret)
9545 {
9546 fprintf (stderr,
9547 "check_connection_header test failed in line %u.\n",
9548 (unsigned int) __LINE__);
9549 ++failed;
9550 }
9551 /* Fail test: keep-alive */
9552 ret = MHD_websocket_check_connection_header ("keep-alive");
9553 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9554 {
9555 fprintf (stderr,
9556 "check_connection_header test failed in line %u.\n",
9557 (unsigned int) __LINE__);
9558 ++failed;
9559 }
9560 /* Fail test: close */
9561 ret = MHD_websocket_check_connection_header ("close");
9562 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9563 {
9564 fprintf (stderr,
9565 "check_connection_header test failed in line %u.\n",
9566 (unsigned int) __LINE__);
9567 ++failed;
9568 }
9569
9570 /*
9571 ------------------------------------------------------------------------------
9572 Connection check edge cases
9573 ------------------------------------------------------------------------------
9574 */
9575 /* Edge test (success): keep-alive,Upgrade */
9576 ret = MHD_websocket_check_connection_header ("keep-alive,Upgrade");
9577 if (MHD_WEBSOCKET_STATUS_OK != ret)
9578 {
9579 fprintf (stderr,
9580 "check_connection_header test failed in line %u.\n",
9581 (unsigned int) __LINE__);
9582 ++failed;
9583 }
9584 /* Edge test (success): Upgrade, keep-alive */
9585 ret = MHD_websocket_check_connection_header ("Upgrade, keep-alive");
9586 if (MHD_WEBSOCKET_STATUS_OK != ret)
9587 {
9588 fprintf (stderr,
9589 "check_connection_header test failed in line %u.\n",
9590 (unsigned int) __LINE__);
9591 ++failed;
9592 }
9593 /* Edge test (success): Upgrade,keep-alive */
9594 ret = MHD_websocket_check_connection_header ("Upgrade,keep-alive");
9595 if (MHD_WEBSOCKET_STATUS_OK != ret)
9596 {
9597 fprintf (stderr,
9598 "check_connection_header test failed in line %u.\n",
9599 (unsigned int) __LINE__);
9600 ++failed;
9601 }
9602 /* Edge test (success): Transfer-Encoding,Upgrade,keep-alive */
9603 ret = MHD_websocket_check_connection_header ("Transfer-Encoding,Upgrade,keep-alive");
9604 if (MHD_WEBSOCKET_STATUS_OK != ret)
9605 {
9606 fprintf (stderr,
9607 "check_connection_header test failed in line %u.\n",
9608 (unsigned int) __LINE__);
9609 ++failed;
9610 }
9611 /* Edge test (success): Transfer-Encoding , Upgrade , keep-alive */
9612 ret = MHD_websocket_check_connection_header ("Transfer-Encoding , Upgrade , keep-alive");
9613 if (MHD_WEBSOCKET_STATUS_OK != ret)
9614 {
9615 fprintf (stderr,
9616 "check_connection_header test failed in line %u.\n",
9617 (unsigned int) __LINE__);
9618 ++failed;
9619 }
9620 /* Edge test (success): upgrade */
9621 ret = MHD_websocket_check_connection_header ("upgrade");
9622 if (MHD_WEBSOCKET_STATUS_OK != ret)
9623 {
9624 fprintf (stderr,
9625 "check_connection_header test failed in line %u.\n",
9626 (unsigned int) __LINE__);
9627 ++failed;
9628 }
9629 /* Edge test (success): UPGRADE */
9630 ret = MHD_websocket_check_connection_header ("UPGRADE");
9631 if (MHD_WEBSOCKET_STATUS_OK != ret)
9632 {
9633 fprintf (stderr,
9634 "check_connection_header test failed in line %u.\n",
9635 (unsigned int) __LINE__);
9636 ++failed;
9637 }
9638 /* Edge test (success): All allowed token characters, then upgrade token */
9639 ret = MHD_websocket_check_connection_header ("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,Upgrade");
9640 if (MHD_WEBSOCKET_STATUS_OK != ret)
9641 {
9642 fprintf (stderr,
9643 "check_connection_header test failed in line %u.\n",
9644 (unsigned int) __LINE__);
9645 ++failed;
9646 }
9647 /* Edge test (success): Different, allowed whitespaces */
9648 ret = MHD_websocket_check_connection_header (" \tUpgrade \t");
9649 if (MHD_WEBSOCKET_STATUS_OK != ret)
9650 {
9651 fprintf (stderr,
9652 "check_connection_header test failed in line %u.\n",
9653 (unsigned int) __LINE__);
9654 ++failed;
9655 }
9656 /* Edge test (fail): Different, disallowed whitespaces */
9657 ret = MHD_websocket_check_connection_header ("\rUpgrade");
9658 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9659 {
9660 fprintf (stderr,
9661 "check_connection_header test failed in line %u.\n",
9662 (unsigned int) __LINE__);
9663 ++failed;
9664 }
9665 /* Edge test (fail): Different, disallowed whitespaces */
9666 ret = MHD_websocket_check_connection_header ("\nUpgrade");
9667 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9668 {
9669 fprintf (stderr,
9670 "check_connection_header test failed in line %u.\n",
9671 (unsigned int) __LINE__);
9672 ++failed;
9673 }
9674 /* Edge test (fail): Different, disallowed whitespaces */
9675 ret = MHD_websocket_check_connection_header ("\vUpgrade");
9676 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9677 {
9678 fprintf (stderr,
9679 "check_connection_header test failed in line %u.\n",
9680 (unsigned int) __LINE__);
9681 ++failed;
9682 }
9683 /* Edge test (fail): Different, disallowed whitespaces */
9684 ret = MHD_websocket_check_connection_header ("\fUpgrade");
9685 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9686 {
9687 fprintf (stderr,
9688 "check_connection_header test failed in line %u.\n",
9689 (unsigned int) __LINE__);
9690 ++failed;
9691 }
9692
9693 /*
9694 ------------------------------------------------------------------------------
9695 Invalid header syntax
9696 ------------------------------------------------------------------------------
9697 */
9698 /* Fail test: (empty string) */
9699 ret = MHD_websocket_check_connection_header ("");
9700 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9701 {
9702 fprintf (stderr,
9703 "check_connection_header test failed in line %u.\n",
9704 (unsigned int) __LINE__);
9705 ++failed;
9706 }
9707 /* Fail test: (Disallowed) multiple word token with the term "Upgrade" in it */
9708 ret = MHD_websocket_check_connection_header ("Upgrade or Downgrade");
9709 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9710 {
9711 fprintf (stderr,
9712 "check_connection_header test failed in line %u.\n",
9713 (unsigned int) __LINE__);
9714 ++failed;
9715 }
9716 /* Fail test: Invalid characters */
9717 ret = MHD_websocket_check_connection_header ("\"Upgrade\"");
9718 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9719 {
9720 fprintf (stderr,
9721 "check_connection_header test failed in line %u.\n",
9722 (unsigned int) __LINE__);
9723 ++failed;
9724 }
9725
9726 /*
9727 ------------------------------------------------------------------------------
9728 Missing parameters
9729 ------------------------------------------------------------------------------
9730 */
9731 /* Fail test: NULL as connection */
9732 ret = MHD_websocket_check_connection_header (NULL);
9733 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9734 {
9735 fprintf (stderr,
9736 "check_connection_header test failed in line %u.\n",
9737 (unsigned int) __LINE__);
9738 ++failed;
9739 }
9740
9741 return failed != 0 ? 0x400 : 0x00;
9742}
9743
9744
9745/**
9746 * Test procedure for `MHD_websocket_check_upgrade_header()`
9747 */
9748int
9749test_check_upgrade_header ()
9750{
9751 int failed = 0;
9752 int ret;
9753
9754 /*
9755 ------------------------------------------------------------------------------
9756 Check with valid Upgrade header syntax
9757 ------------------------------------------------------------------------------
9758 */
9759 /* Regular test: websocket */
9760 ret = MHD_websocket_check_upgrade_header ("websocket");
9761 if (MHD_WEBSOCKET_STATUS_OK != ret)
9762 {
9763 fprintf (stderr,
9764 "check_upgrade_header test failed in line %u.\n",
9765 (unsigned int) __LINE__);
9766 ++failed;
9767 }
9768 /* Fail test: HTTP/2.0 */
9769 ret = MHD_websocket_check_upgrade_header ("HTTP/2.0");
9770 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9771 {
9772 fprintf (stderr,
9773 "check_upgrade_header test failed in line %u.\n",
9774 (unsigned int) __LINE__);
9775 ++failed;
9776 }
9777
9778 /*
9779 ------------------------------------------------------------------------------
9780 Upgrade check edge cases
9781 ------------------------------------------------------------------------------
9782 */
9783 /* Edge test (success): websocket,HTTP/2.0 */
9784 ret = MHD_websocket_check_upgrade_header ("websocket,HTTP/2.0");
9785 if (MHD_WEBSOCKET_STATUS_OK != ret)
9786 {
9787 fprintf (stderr,
9788 "check_upgrade_header test failed in line %u.\n",
9789 (unsigned int) __LINE__);
9790 ++failed;
9791 }
9792 /* Edge test (success): websocket ,HTTP/2.0 */
9793 ret = MHD_websocket_check_upgrade_header (" websocket ,HTTP/2.0");
9794 if (MHD_WEBSOCKET_STATUS_OK != ret)
9795 {
9796 fprintf (stderr,
9797 "check_upgrade_header test failed in line %u.\n",
9798 (unsigned int) __LINE__);
9799 ++failed;
9800 }
9801 /* Edge test (success): HTTP/2.0, websocket */
9802 ret = MHD_websocket_check_upgrade_header ("HTTP/2.0, websocket ");
9803 if (MHD_WEBSOCKET_STATUS_OK != ret)
9804 {
9805 fprintf (stderr,
9806 "check_upgrade_header test failed in line %u.\n",
9807 (unsigned int) __LINE__);
9808 ++failed;
9809 }
9810 /* Edge test (fail): websocket/13 */
9811 ret = MHD_websocket_check_upgrade_header ("websocket/13");
9812 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9813 {
9814 fprintf (stderr,
9815 "check_upgrade_header test failed in line %u.\n",
9816 (unsigned int) __LINE__);
9817 ++failed;
9818 }
9819 /* Edge test (success): WeBsOcKeT */
9820 ret = MHD_websocket_check_upgrade_header ("WeBsOcKeT");
9821 if (MHD_WEBSOCKET_STATUS_OK != ret)
9822 {
9823 fprintf (stderr,
9824 "check_upgrade_header test failed in line %u.\n",
9825 (unsigned int) __LINE__);
9826 ++failed;
9827 }
9828 /* Edge test (success): WEBSOCKET */
9829 ret = MHD_websocket_check_upgrade_header ("WEBSOCKET");
9830 if (MHD_WEBSOCKET_STATUS_OK != ret)
9831 {
9832 fprintf (stderr,
9833 "check_upgrade_header test failed in line %u.\n",
9834 (unsigned int) __LINE__);
9835 ++failed;
9836 }
9837 /* Edge test (success): All allowed token characters plus /, then websocket keyowrd */
9838 ret = MHD_websocket_check_upgrade_header ("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/,websocket");
9839 if (MHD_WEBSOCKET_STATUS_OK != ret)
9840 {
9841 fprintf (stderr,
9842 "check_upgrade_header test failed in line %u.\n",
9843 (unsigned int) __LINE__);
9844 ++failed;
9845 }
9846 /* Edge test (success): Different, allowed whitespaces */
9847 ret = MHD_websocket_check_upgrade_header (" \twebsocket \t");
9848 if (MHD_WEBSOCKET_STATUS_OK != ret)
9849 {
9850 fprintf (stderr,
9851 "check_upgrade_header test failed in line %u.\n",
9852 (unsigned int) __LINE__);
9853 ++failed;
9854 }
9855 /* Edge test (fail): Different, disallowed whitespaces */
9856 ret = MHD_websocket_check_upgrade_header ("\rwebsocket");
9857 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9858 {
9859 fprintf (stderr,
9860 "check_upgrade_header test failed in line %u.\n",
9861 (unsigned int) __LINE__);
9862 ++failed;
9863 }
9864 /* Edge test (fail): Different, disallowed whitespaces */
9865 ret = MHD_websocket_check_upgrade_header ("\nwebsocket");
9866 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9867 {
9868 fprintf (stderr,
9869 "check_upgrade_header test failed in line %u.\n",
9870 (unsigned int) __LINE__);
9871 ++failed;
9872 }
9873 /* Edge test (fail): Different, disallowed whitespaces */
9874 ret = MHD_websocket_check_upgrade_header ("\vwebsocket");
9875 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9876 {
9877 fprintf (stderr,
9878 "check_upgrade_header test failed in line %u.\n",
9879 (unsigned int) __LINE__);
9880 ++failed;
9881 }
9882 /* Edge test (fail): Different, disallowed whitespaces */
9883 ret = MHD_websocket_check_upgrade_header ("\fwebsocket");
9884 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9885 {
9886 fprintf (stderr,
9887 "check_upgrade_header test failed in line %u.\n",
9888 (unsigned int) __LINE__);
9889 ++failed;
9890 }
9891
9892 /*
9893 ------------------------------------------------------------------------------
9894 Invalid header syntax
9895 ------------------------------------------------------------------------------
9896 */
9897 /* Fail test: (empty string) */
9898 ret = MHD_websocket_check_upgrade_header ("");
9899 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9900 {
9901 fprintf (stderr,
9902 "check_upgrade_header test failed in line %u.\n",
9903 (unsigned int) __LINE__);
9904 ++failed;
9905 }
9906 /* Fail test: (Disallowed) multiple word token with the term "websocket" in it */
9907 ret = MHD_websocket_check_upgrade_header ("websocket or something");
9908 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9909 {
9910 fprintf (stderr,
9911 "check_upgrade_header test failed in line %u.\n",
9912 (unsigned int) __LINE__);
9913 ++failed;
9914 }
9915 /* Fail test: Invalid characters */
9916 ret = MHD_websocket_check_upgrade_header ("\"websocket\"");
9917 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9918 {
9919 fprintf (stderr,
9920 "check_upgrade_header test failed in line %u.\n",
9921 (unsigned int) __LINE__);
9922 ++failed;
9923 }
9924
9925 /*
9926 ------------------------------------------------------------------------------
9927 Missing parameters
9928 ------------------------------------------------------------------------------
9929 */
9930 /* Fail test: NULL as upgrade */
9931 ret = MHD_websocket_check_upgrade_header (NULL);
9932 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9933 {
9934 fprintf (stderr,
9935 "check_upgrade_header test failed in line %u.\n",
9936 (unsigned int) __LINE__);
9937 ++failed;
9938 }
9939
9940 return failed != 0 ? 0x800 : 0x00;
9941}
9942
9943
9944/**
9945 * Test procedure for `MHD_websocket_check_version_header()`
9946 */
9947int
9948test_check_version_header ()
9949{
9950 int failed = 0;
9951 int ret;
9952
9953 /*
9954 ------------------------------------------------------------------------------
9955 Check with valid Upgrade header syntax
9956 ------------------------------------------------------------------------------
9957 */
9958 /* Regular test: 13 */
9959 ret = MHD_websocket_check_version_header ("13");
9960 if (MHD_WEBSOCKET_STATUS_OK != ret)
9961 {
9962 fprintf (stderr,
9963 "check_version_header test failed in line %u.\n",
9964 (unsigned int) __LINE__);
9965 ++failed;
9966 }
9967
9968 /*
9969 ------------------------------------------------------------------------------
9970 Version check edge cases
9971 ------------------------------------------------------------------------------
9972 */
9973 /* Edge test (fail): 14 */
9974 ret = MHD_websocket_check_version_header ("14");
9975 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9976 {
9977 fprintf (stderr,
9978 "check_version_header test failed in line %u.\n",
9979 (unsigned int) __LINE__);
9980 ++failed;
9981 }
9982 /* Edge test (fail): 12 */
9983 ret = MHD_websocket_check_version_header ("12");
9984 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9985 {
9986 fprintf (stderr,
9987 "check_version_header test failed in line %u.\n",
9988 (unsigned int) __LINE__);
9989 ++failed;
9990 }
9991 /* Edge test (fail): 0 */
9992 ret = MHD_websocket_check_version_header ("1");
9993 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
9994 {
9995 fprintf (stderr,
9996 "check_version_header test failed in line %u.\n",
9997 (unsigned int) __LINE__);
9998 ++failed;
9999 }
10000 /* Edge test (fail): 1 */
10001 ret = MHD_websocket_check_version_header ("1");
10002 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10003 {
10004 fprintf (stderr,
10005 "check_version_header test failed in line %u.\n",
10006 (unsigned int) __LINE__);
10007 ++failed;
10008 }
10009 /* Edge test (fail): 130 */
10010 ret = MHD_websocket_check_version_header ("130");
10011 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10012 {
10013 fprintf (stderr,
10014 "check_version_header test failed in line %u.\n",
10015 (unsigned int) __LINE__);
10016 ++failed;
10017 }
10018 /* Edge test (fail): " 13" */
10019 ret = MHD_websocket_check_version_header (" 13");
10020 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10021 {
10022 fprintf (stderr,
10023 "check_version_header test failed in line %u.\n",
10024 (unsigned int) __LINE__);
10025 ++failed;
10026 }
10027
10028 /*
10029 ------------------------------------------------------------------------------
10030 Invalid header syntax
10031 ------------------------------------------------------------------------------
10032 */
10033 /* Fail test: (empty string) */
10034 ret = MHD_websocket_check_version_header ("");
10035 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10036 {
10037 fprintf (stderr,
10038 "check_version_header test failed in line %u.\n",
10039 (unsigned int) __LINE__);
10040 ++failed;
10041 }
10042 /* Fail test: Invalid characters */
10043 ret = MHD_websocket_check_version_header ("abc");
10044 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10045 {
10046 fprintf (stderr,
10047 "check_version_header test failed in line %u.\n",
10048 (unsigned int) __LINE__);
10049 ++failed;
10050 }
10051
10052 /*
10053 ------------------------------------------------------------------------------
10054 Missing parameters
10055 ------------------------------------------------------------------------------
10056 */
10057 /* Fail test: NULL as version */
10058 ret = MHD_websocket_check_version_header (NULL);
10059 if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
10060 {
10061 fprintf (stderr,
10062 "check_version_header test failed in line %u.\n",
10063 (unsigned int) __LINE__);
10064 ++failed;
10065 }
10066
10067 return failed != 0 ? 0x1000 : 0x00;
10068}
10069
10070
8958int 10071int
8959main (int argc, char *const *argv) 10072main (int argc, char *const *argv)
8960{ 10073{
@@ -8962,7 +10075,7 @@ main (int argc, char *const *argv)
8962 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 10075 (void) argc; (void) argv; /* Unused. Silent compiler warning. */
8963 10076
8964 /* seed random number generator */ 10077 /* seed random number generator */
8965 MHD_websocket_srand ((unsigned long) time (NULL)); 10078 srand ((unsigned long) time (NULL));
8966 10079
8967 /* perform tests */ 10080 /* perform tests */
8968 errorCount += test_inits (); 10081 errorCount += test_inits ();
@@ -8974,6 +10087,10 @@ main (int argc, char *const *argv)
8974 errorCount += test_encodes_ping (); 10087 errorCount += test_encodes_ping ();
8975 errorCount += test_encodes_pong (); 10088 errorCount += test_encodes_pong ();
8976 errorCount += test_split_close_reason (); 10089 errorCount += test_split_close_reason ();
10090 errorCount += test_check_http_version ();
10091 errorCount += test_check_connection_header ();
10092 errorCount += test_check_upgrade_header ();
10093 errorCount += test_check_version_header ();
8977 10094
8978 /* output result */ 10095 /* output result */
8979 if (errorCount != 0) 10096 if (errorCount != 0)
diff --git a/src/microhttpd_ws/test_websocket_browser.c b/src/microhttpd_ws/test_websocket_browser.c
new file mode 100644
index 00000000..dfbcd116
--- /dev/null
+++ b/src/microhttpd_ws/test_websocket_browser.c
@@ -0,0 +1,563 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2021 David Gausmann
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20/**
21 * @file test_websocket_browser.c
22 * @brief Testcase for WebSocket decoding/encoding with external browser
23 * @author David Gausmann
24 */
25#include <sys/types.h>
26#ifndef _WIN32
27#include <sys/select.h>
28#include <sys/socket.h>
29#include <fcntl.h>
30#else
31#include <winsock2.h>
32#endif
33#include "microhttpd.h"
34#include "microhttpd_ws.h"
35#include <stdlib.h>
36#include <string.h>
37#include <stdio.h>
38#include <stdint.h>
39#include <time.h>
40#include <errno.h>
41
42#define PORT 80
43
44#define PAGE \
45 "<!DOCTYPE html>\n" \
46 "<html>\n" \
47 "<head>\n" \
48 "<meta charset=\"UTF-8\">\n" \
49 "<title>Websocket External Test with Webbrowser</title>\n" \
50 "<script>\n" \
51 "\n" \
52 "let current_mode = 0;\n" \
53 "let current_step = 0;\n" \
54 "let sent_payload = null;\n" \
55 "let charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_!@%&/\\\\';\n" \
56 "let step_to_bytes = [ 0, 1, 2, 3, 122, 123, 124, 125, 126, 127, 128, 32766, 32767, 32768, 65534, 65535, 65536, 65537, 1048576, 10485760 ];\n" \
57 "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
58 " + '://' +\n" \
59 " window.location.host + '/websocket';\n" \
60 "let socket = null;\n" \
61 "\n" \
62 "window.onload = function (event) {\n" \
63 " if (!window.WebSocket) {\n" \
64 " document.write ('ERROR: The WebSocket class is not supported by your browser.<br>');\n" \
65 " }\n" \
66 " if (!window.fetch) {\n" \
67 " document.write ('ERROR: The fetch-API is not supported by your browser.<br>');\n" \
68 " }\n" \
69 " document.write ('Starting tests.<br>');\n" \
70 " runTest ();\n" \
71 "}\n" \
72 "\n" \
73 "function runTest () {\n" \
74 " switch (current_mode) {\n" \
75 " case 0:\n" \
76 " document.write ('TEXT');\n" \
77 " break;\n" \
78 " case 1:\n" \
79 " document.write ('BINARY');\n" \
80 " break;\n" \
81 " }\n" \
82 " document.write (', ' + step_to_bytes[current_step] + ' Bytes: ');\n" \
83 " socket = new WebSocket(url);\n" \
84 " socket.binaryType = 'arraybuffer';\n" \
85 " socket.onopen = function (event) {\n" \
86 " switch (current_mode) {\n" \
87 " case 0:\n" \
88 " sent_payload = randomText (step_to_bytes[current_step]);\n" \
89 " socket.send (sent_payload);\n" \
90 " break;\n" \
91 " case 1:\n" \
92 " sent_payload = randomBinary (step_to_bytes[current_step]);\n" \
93 " socket.send (sent_payload);\n" \
94 " break;\n" \
95 " }\n" \
96 " }\n" \
97 "\n" \
98 " socket.onclose = function (event) {\n" \
99 " socket.onmessage = null;\n" \
100 " socket.onclose = null;\n" \
101 " socket.onerror = null;\n" \
102 " document.write ('CLOSED unexpectedly.<br>');\n" \
103 " notifyError ();\n" \
104 " }\n" \
105 "\n" \
106 " socket.onerror = function (event) {\n" \
107 " socket.onmessage = null;\n" \
108 " socket.onclose = null;\n" \
109 " socket.onerror = null;\n" \
110 " document.write ('ERROR.<br>');\n" \
111 " notifyError ();\n" \
112 " }\n" \
113 "\n" \
114 " socket.onmessage = async function (event) {\n" \
115 " if (compareData (event.data, sent_payload)) {\n" \
116 " document.write ('SUCCESS.<br>');\n" \
117 " socket.onmessage = null;\n" \
118 " socket.onclose = null;\n" \
119 " socket.onerror = null;\n" \
120 " socket.close();\n" \
121 " socket = null;\n" \
122 " if (step_to_bytes.length <= ++current_step) {\n" \
123 " current_step = 0;\n" \
124 " if (1 < ++current_mode) {\n" \
125 " document.write ('FINISHED ALL TESTS.<br>');\n" \
126 " return;\n" \
127 " }\n" \
128 " }\n" \
129 " runTest ();\n" \
130 " }" \
131 " }\n" \
132 "}\n" \
133 "\n" \
134 "function compareData (data, data2) {\n" \
135 " if (typeof (data) === 'string' && typeof (data2) === 'string') {\n" \
136 " return (data === data2); \n" \
137 " } \n" \
138 " else if ((data instanceof ArrayBuffer) && (data2 instanceof ArrayBuffer)) {\n" \
139 " let view1 = new Uint8Array (data);\n" \
140 " let view2 = new Uint8Array (data2);\n" \
141 " if (view1.length != view2.length)\n" \
142 " return false;\n" \
143 " for (let i = 0; i < view1.length; ++i) {\n" \
144 " if (view1[i] !== view2[i])\n" \
145 " return false;\n" \
146 " }\n" \
147 " return true;\n" \
148 " }\n" \
149 " else\n" \
150 " {\n" \
151 " return false;\n" \
152 " }\n" \
153 "}\n" \
154 "\n" \
155 "function randomText (length) {\n" \
156 " let result = new Array (length);\n" \
157 " for (let i = 0; i < length; ++i)\n" \
158 " result [i] = charset [~~(Math.random () * charset.length)];\n" \
159 " return result.join ('');\n" \
160 "}\n" \
161 "\n" \
162 "function randomBinary (length) {\n" \
163 " let buffer = new ArrayBuffer (length);\n" \
164 " let view = new Uint8Array (buffer);\n" \
165 " for (let i = 0; i < length; ++i)\n" \
166 " view [i] = ~~(Math.random () * 256);\n" \
167 " return buffer;\n" \
168 "}\n" \
169 "\n" \
170 "function notifyError () {\n" \
171 " fetch('error/' + (0 == current_mode ? 'text' : 'binary') + '/' + step_to_bytes[current_step]);\n" \
172 "}\n" \
173 "\n" \
174 "</script>\n" \
175 "</head>\n" \
176 "<body>\n" \
177 "</body>\n" \
178 "</html>"
179
180#define PAGE_NOT_FOUND \
181 "404 Not Found"
182
183#define PAGE_INVALID_WEBSOCKET_REQUEST \
184 "Invalid WebSocket request!"
185
186static void
187send_all (MHD_socket fd,
188 const char *buf,
189 size_t len);
190static void
191make_blocking (MHD_socket fd);
192
193static void
194upgrade_handler (void *cls,
195 struct MHD_Connection *connection,
196 void *con_cls,
197 const char *extra_in,
198 size_t extra_in_size,
199 MHD_socket fd,
200 struct MHD_UpgradeResponseHandle *urh)
201{
202 /* make the socket blocking (operating-system-dependent code) */
203 make_blocking (fd);
204
205 /* create a websocket stream for this connection */
206 struct MHD_WebSocketStream* ws;
207 int result = MHD_websocket_stream_init (&ws,
208 0,
209 0);
210 if (0 != result)
211 {
212 /* Couldn't create the websocket stream.
213 * So we close the socket and leave
214 */
215 MHD_upgrade_action (urh,
216 MHD_UPGRADE_ACTION_CLOSE);
217 return;
218 }
219
220 /* Let's wait for incoming data */
221 const size_t buf_len = 256;
222 char buf[buf_len];
223 ssize_t got;
224 while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
225 {
226 got = recv (fd,
227 buf,
228 sizeof (buf),
229 0);
230 if (0 >= got)
231 {
232 /* the TCP/IP socket has been closed */
233 fprintf (stderr,
234 "Error (The socket has been closed unexpectedly)\n");
235 break;
236 }
237
238 /* parse the entire received data */
239 size_t buf_offset = 0;
240 while (buf_offset < (size_t) got)
241 {
242 size_t new_offset = 0;
243 char *payload_data = NULL;
244 size_t payload_len = 0;
245 char *frame_data = NULL;
246 size_t frame_len = 0;
247 int status = MHD_websocket_decode (ws,
248 buf + buf_offset,
249 ((size_t) got) - buf_offset,
250 &new_offset,
251 &payload_data,
252 &payload_len);
253 if (0 > status)
254 {
255 /* an error occurred and the connection must be closed */
256 printf ("Decoding failed: status=%d, passed=%u\n", status, ((size_t) got) - buf_offset);
257 if (NULL != payload_data)
258 {
259 MHD_websocket_free (ws, payload_data);
260 }
261 break;
262 }
263 else
264 {
265 buf_offset += new_offset;
266 if (0 < status)
267 {
268 /* the frame is complete */
269 printf ("Decoding succeeded: type=%d, passed=%u, parsed=%u, payload_len=%d\n", status, ((size_t) got) - buf_offset, new_offset, payload_len);
270 switch (status)
271 {
272 case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
273 case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
274 /* The client has sent some data. */
275 if (NULL != payload_data || 0 == payload_len)
276 {
277 /* Send the received data back to the client */
278 if (MHD_WEBSOCKET_STATUS_TEXT_FRAME == status)
279 {
280 result = MHD_websocket_encode_text (ws,
281 payload_data,
282 payload_len,
283 0,
284 &frame_data,
285 &frame_len,
286 NULL);
287 }
288 else
289 {
290 result = MHD_websocket_encode_binary (ws,
291 payload_data,
292 payload_len,
293 0,
294 &frame_data,
295 &frame_len);
296 }
297 if (0 == result)
298 {
299 send_all (fd,
300 frame_data,
301 frame_len);
302 }
303 }
304 else
305 {
306 /* should never happen */
307 fprintf (stderr,
308 "Error (Empty buffer with payload_len != 0)\n");
309 }
310 break;
311
312 default:
313 /* Other frame types are ignored
314 * in this test script.
315 */
316 break;
317 }
318 }
319 if (NULL != payload_data)
320 {
321 MHD_websocket_free (ws, payload_data);
322 }
323 if (NULL != frame_data)
324 {
325 MHD_websocket_free (ws, frame_data);
326 }
327 }
328 }
329 }
330
331 /* free the websocket stream */
332 MHD_websocket_stream_free (ws);
333
334 /* close the socket when it is not needed anymore */
335 MHD_upgrade_action (urh,
336 MHD_UPGRADE_ACTION_CLOSE);
337}
338
339/* This helper function is used for the case that
340 * we need to resend some data
341 */
342static void
343send_all (MHD_socket fd,
344 const char *buf,
345 size_t len)
346{
347 ssize_t ret;
348 size_t off;
349
350 for (off = 0; off < len; off += ret)
351 {
352 ret = send (fd,
353 &buf[off],
354 (int) (len - off),
355 0);
356 if (0 > ret)
357 {
358 if (EAGAIN == errno)
359 {
360 ret = 0;
361 continue;
362 }
363 break;
364 }
365 if (0 == ret)
366 break;
367 }
368}
369
370/* This helper function contains operating-system-dependent code and
371 * is used to make a socket blocking.
372 */
373static void
374make_blocking (MHD_socket fd)
375{
376#ifndef _WIN32
377 int flags;
378
379 flags = fcntl (fd, F_GETFL);
380 if (-1 == flags)
381 return;
382 if ((flags & ~O_NONBLOCK) != flags)
383 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
384 abort ();
385#else
386 unsigned long flags = 0;
387
388 ioctlsocket (fd, FIONBIO, &flags);
389#endif
390}
391
392static enum MHD_Result
393access_handler (void *cls,
394 struct MHD_Connection *connection,
395 const char *url,
396 const char *method,
397 const char *version,
398 const char *upload_data,
399 size_t *upload_data_size,
400 void **ptr)
401{
402 static int aptr;
403 struct MHD_Response *response;
404 int ret;
405
406 (void) cls; /* Unused. Silent compiler warning. */
407 (void) upload_data; /* Unused. Silent compiler warning. */
408 (void) upload_data_size; /* Unused. Silent compiler warning. */
409
410 if (0 != strcmp (method, "GET"))
411 return MHD_NO; /* unexpected method */
412 if (&aptr != *ptr)
413 {
414 /* do never respond on first call */
415 *ptr = &aptr;
416 return MHD_YES;
417 }
418 *ptr = NULL; /* reset when done */
419
420 if (0 == strcmp (url, "/"))
421 {
422 /* Default page for visiting the server */
423 struct MHD_Response *response = MHD_create_response_from_buffer (
424 strlen (PAGE),
425 PAGE,
426 MHD_RESPMEM_PERSISTENT);
427 ret = MHD_queue_response (connection,
428 MHD_HTTP_OK,
429 response);
430 MHD_destroy_response (response);
431 }
432 else if (0 == strncmp (url, "/error/", 7))
433 {
434 /* Report error */
435 fprintf (stderr, "Error in test (%s)\n", url + 7);
436
437 struct MHD_Response *response = MHD_create_response_from_buffer (
438 0,
439 "",
440 MHD_RESPMEM_PERSISTENT);
441 ret = MHD_queue_response (connection,
442 MHD_HTTP_OK,
443 response);
444 MHD_destroy_response (response);
445 }
446 else if (0 == strcmp (url, "/websocket"))
447 {
448 char is_valid = 1;
449 const char* value = NULL;
450 char sec_websocket_accept[29];
451
452 if (0 != MHD_websocket_check_http_version (version))
453 {
454 is_valid = 0;
455 }
456 value = MHD_lookup_connection_value (connection,
457 MHD_HEADER_KIND,
458 MHD_HTTP_HEADER_CONNECTION);
459 if (0 != MHD_websocket_check_connection_header (value))
460 {
461 is_valid = 0;
462 }
463 value = MHD_lookup_connection_value (connection,
464 MHD_HEADER_KIND,
465 MHD_HTTP_HEADER_UPGRADE);
466 if (0 != MHD_websocket_check_upgrade_header (value))
467 {
468 is_valid = 0;
469 }
470 value = MHD_lookup_connection_value (connection,
471 MHD_HEADER_KIND,
472 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
473 if (0 != MHD_websocket_check_version_header (value))
474 {
475 is_valid = 0;
476 }
477 value = MHD_lookup_connection_value (connection,
478 MHD_HEADER_KIND,
479 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
480 if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
481 {
482 is_valid = 0;
483 }
484
485 if (1 == is_valid)
486 {
487 /* upgrade the connection */
488 response = MHD_create_response_for_upgrade (&upgrade_handler,
489 NULL);
490 MHD_add_response_header (response,
491 MHD_HTTP_HEADER_CONNECTION,
492 "Upgrade");
493 MHD_add_response_header (response,
494 MHD_HTTP_HEADER_UPGRADE,
495 "websocket");
496 MHD_add_response_header (response,
497 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
498 sec_websocket_accept);
499 ret = MHD_queue_response (connection,
500 MHD_HTTP_SWITCHING_PROTOCOLS,
501 response);
502 MHD_destroy_response (response);
503 }
504 else
505 {
506 /* return error page */
507 struct MHD_Response*response = MHD_create_response_from_buffer (
508 strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
509 PAGE_INVALID_WEBSOCKET_REQUEST,
510 MHD_RESPMEM_PERSISTENT);
511 ret = MHD_queue_response (connection,
512 MHD_HTTP_BAD_REQUEST,
513 response);
514 MHD_destroy_response (response);
515 }
516 }
517 else
518 {
519 struct MHD_Response*response = MHD_create_response_from_buffer (
520 strlen (PAGE_NOT_FOUND),
521 PAGE_NOT_FOUND,
522 MHD_RESPMEM_PERSISTENT);
523 ret = MHD_queue_response (connection,
524 MHD_HTTP_NOT_FOUND,
525 response);
526 MHD_destroy_response (response);
527 }
528
529 return ret;
530}
531
532int
533main (int argc,
534 char *const *argv)
535{
536 (void) argc; /* Unused. Silent compiler warning. */
537 (void) argv; /* Unused. Silent compiler warning. */
538 struct MHD_Daemon *daemon;
539
540 daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
541 MHD_USE_THREAD_PER_CONNECTION |
542 MHD_ALLOW_UPGRADE |
543 MHD_USE_ERROR_LOG,
544 PORT, NULL, NULL,
545 &access_handler, NULL,
546 MHD_OPTION_END);
547
548 if (NULL == daemon)
549 {
550 fprintf (stderr, "Error (Couldn't start daemon for testing)\n");
551 return 1;
552 }
553 printf("The server is listening now.\n");
554 printf("Access the server now with a websocket-capable webbrowser.\n\n");
555 printf("Press return to close.\n");
556
557 (void) getc (stdin);
558
559 MHD_stop_daemon (daemon);
560
561 return 0;
562}
563