diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/examples/websocket_chatserver_example.c | 1051 | ||||
-rw-r--r-- | src/include/microhttpd_ws.h | 534 | ||||
-rw-r--r-- | src/microhttpd_ws/mhd_websocket.c | 587 | ||||
-rw-r--r-- | src/microhttpd_ws/sha1.c | 720 | ||||
-rw-r--r-- | src/microhttpd_ws/sha1.h | 245 | ||||
-rw-r--r-- | src/microhttpd_ws/test_websocket.c | 1247 | ||||
-rw-r--r-- | src/microhttpd_ws/test_websocket_browser.c | 563 |
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 | */ |
2135 | static int | 2134 | static enum MHD_Result |
2136 | access_handler (void *cls, | 2135 | access_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; | |||
51 | enum MHD_WEBSOCKET_FLAG | 51 | enum 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 | */ |
510 | typedef void* | 550 | typedef 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 | */ |
524 | typedef void* | 568 | typedef 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 | */ |
536 | typedef void | 584 | typedef 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 | */ | ||
602 | typedef 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 | ||
618 | MHD_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 | ||
637 | MHD_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 | ||
656 | MHD_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 | ||
676 | MHD_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 |
553 | MHD_websocket_create_accept (const char*sec_websocket_key, | 698 | MHD_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 |
569 | MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, | 714 | MHD_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 |
590 | MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, | 749 | MHD_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 |
606 | MHD_websocket_stream_free (struct MHD_WebSocketStream*ws); | 767 | MHD_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 |
619 | MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws); | 780 | MHD_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 |
631 | MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws); | 792 | MHD_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 |
678 | MHD_websocket_decode (struct MHD_WebSocketStream*ws, | 839 | MHD_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 |
713 | MHD_websocket_split_close_reason (const char*payload, | 874 | MHD_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 |
755 | MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, | 916 | MHD_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 |
790 | MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, | 951 | MHD_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 |
819 | MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, | 980 | MHD_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 |
852 | MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, | 1013 | MHD_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 |
894 | MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, | 1055 | MHD_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 | ||
922 | MHD_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* |
935 | MHD_websocket_malloc (struct MHD_WebSocketStream*ws, | 1073 | MHD_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* |
952 | MHD_websocket_realloc (struct MHD_WebSocketStream*ws, | 1090 | MHD_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 |
969 | MHD_websocket_free (struct MHD_WebSocketStream*ws, | 1107 | MHD_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 | |||
132 | static void | 129 | static void |
133 | MHD_websocket_copy_payload (char*dst, | 130 | MHD_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 | ||
145 | static int | 142 | static enum MHD_WEBSOCKET_STATUS |
146 | MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, | 143 | MHD_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 | ||
150 | static int | 147 | static enum MHD_WEBSOCKET_STATUS |
151 | MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, | 148 | MHD_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 | |||
158 | MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws, | 155 | MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws, |
159 | size_t payload_len); | 156 | size_t payload_len); |
160 | 157 | ||
161 | static int | 158 | static enum MHD_WEBSOCKET_STATUS |
162 | MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, | 159 | MHD_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 | ||
170 | static int | 167 | static enum MHD_WEBSOCKET_STATUS |
171 | MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, | 168 | MHD_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 | ||
178 | static uint32_t | 175 | static uint32_t |
179 | MHD_websocket_generate_mask (); | 176 | MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws); |
177 | |||
178 | static uint16_t | ||
179 | MHD_htons (uint16_t value); | ||
180 | |||
181 | static uint64_t | ||
182 | MHD_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 | ||
189 | MHD_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 | ||
282 | MHD_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 | ||
365 | MHD_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 | ||
453 | MHD_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 |
185 | MHD_websocket_create_accept (const char*sec_websocket_key, | 479 | MHD_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 |
238 | MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, | 538 | MHD_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 |
256 | MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, | 558 | MHD_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 |
304 | MHD_websocket_stream_free (struct MHD_WebSocketStream*ws) | 612 | MHD_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 |
327 | MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws) | 635 | MHD_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 |
344 | MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws) | 652 | MHD_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 |
358 | MHD_websocket_decode (struct MHD_WebSocketStream*ws, | 666 | MHD_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 | ||
1019 | static int | 1326 | static enum MHD_WEBSOCKET_STATUS |
1020 | MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, | 1327 | MHD_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 | ||
1122 | static int | 1429 | static enum MHD_WEBSOCKET_STATUS |
1123 | MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, | 1430 | MHD_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 |
1255 | MHD_websocket_split_close_reason (const char*payload, | 1570 | MHD_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 |
1313 | MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, | 1628 | MHD_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 |
1380 | MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, | 1695 | MHD_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 | */ |
1423 | static int | 1738 | static enum MHD_WEBSOCKET_STATUS |
1424 | MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, | 1739 | MHD_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 |
1509 | MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, | 1824 | MHD_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 |
1529 | MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, | 1844 | MHD_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 | */ |
1548 | static int | 1863 | static enum MHD_WEBSOCKET_STATUS |
1549 | MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, | 1864 | MHD_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 |
1622 | MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, | 1937 | MHD_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 | ||
1998 | MHD_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 | */ |
2010 | static uint32_t | 2312 | static uint32_t |
2011 | MHD_websocket_generate_mask () | 2313 | MHD_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* |
2027 | MHD_websocket_malloc (struct MHD_WebSocketStream*ws, | 2344 | MHD_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* |
2043 | MHD_websocket_realloc (struct MHD_WebSocketStream*ws, | 2360 | MHD_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 |
2060 | MHD_websocket_free (struct MHD_WebSocketStream*ws, | 2377 | MHD_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 | */ | ||
2394 | static uint16_t | ||
2395 | MHD_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 | */ | ||
2417 | static uint64_t | ||
2418 | MHD_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) */ | ||
50 | static 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. */ | ||
56 | void | 40 | void |
57 | sha1_init_ctx (struct sha1_ctx *ctx) | 41 | MHD_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 |
75 | void * | 62 | */ |
76 | sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) | 63 | static void |
64 | sha1_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 |
93 | void * | 246 | * @param length number of bytes in @a data |
94 | sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) | 247 | */ |
248 | void | ||
249 | MHD_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. */ | ||
121 | int | ||
122 | sha1_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 | ||
173 | process_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 | */ |
189 | void * | 302 | #define SHA1_SIZE_OF_LEN_ADD (64 / 8) |
190 | sha1_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 | */ | ||
205 | void | 310 | void |
206 | sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) | 311 | MHD_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 | ||
291 | void | 376 | /* Erase potentially sensitive data. */ |
292 | sha1_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> | ||
39 | typedef u_int32_t sha1_uint32; | ||
40 | typedef 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 |
44 | typedef uint32_t sha1_uint32; | 32 | #include <stddef.h> /* for size_t */ |
45 | typedef 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 |
59 | typedef 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 | |
62 | typedef unsigned short sha1_uint32; | 50 | /** |
63 | # else | 51 | * Size of SHA-1 digest in bytes |
64 | # if LONG_MAX == INT_MAX_32_BITS | 52 | */ |
65 | typedef 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 |
76 | extern "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 | |||
80 | struct sha1_ctx | 71 | struct 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. |
95 | extern 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 | 83 | void |
99 | starting at BUFFER. | 84 | MHD_SHA1_init (void *ctx_); |
100 | It is necessary that LEN is a multiple of 64!!! */ | 85 | |
101 | extern 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 |
108 | extern void sha1_process_bytes (const void *buffer, size_t len, | 93 | */ |
109 | struct sha1_ctx *ctx); | 94 | void |
110 | 95 | MHD_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 | * |
118 | extern 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 | 106 | void |
122 | always in little endian byte order, so that a byte-wise output yields | 107 | MHD_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. */ | ||
127 | extern 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. */ | ||
133 | extern 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. */ | ||
139 | extern 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 | |||
32 | int disable_alloc = 0; | 37 | int disable_alloc = 0; |
33 | size_t open_allocs = 0; | 38 | size_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 | */ | ||
84 | static size_t | ||
85 | test_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 | */ |
272 | int | 298 | int |
273 | test_inits () | 299 | test_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 | */ |
712 | int | 862 | int |
713 | test_accept () | 863 | test_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 | */ | ||
9287 | int | ||
9288 | test_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 | */ | ||
9522 | int | ||
9523 | test_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 | */ | ||
9748 | int | ||
9749 | test_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 | */ | ||
9947 | int | ||
9948 | test_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 | |||
8958 | int | 10071 | int |
8959 | main (int argc, char *const *argv) | 10072 | main (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 | |||
186 | static void | ||
187 | send_all (MHD_socket fd, | ||
188 | const char *buf, | ||
189 | size_t len); | ||
190 | static void | ||
191 | make_blocking (MHD_socket fd); | ||
192 | |||
193 | static void | ||
194 | upgrade_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 | */ | ||
342 | static void | ||
343 | send_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 | */ | ||
373 | static void | ||
374 | make_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 | |||
392 | static enum MHD_Result | ||
393 | access_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 | |||
532 | int | ||
533 | main (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 | |||