diff options
author | David Gausmann <David.Gausmann@measX.com> | 2021-10-17 19:53:09 +0200 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2021-10-31 16:02:31 +0300 |
commit | ddad59c1e3a5d20b19003b1d47673501b0665b36 (patch) | |
tree | 8cd367e724477bc092d7dd38c83cd15f4c12c95f /src/microhttpd_ws/test_websocket_browser.c | |
parent | ffbb000f890f23e14d972a6660168aff4a97b66c (diff) | |
download | libmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.tar.gz libmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.zip |
websocket update
- added API documentation to libmicrohttpd.texi
- added websocket tutorial chapter to libmicrohttpd-tutorial and an much easier example for the tutorial
- added additional helper functions to ease the HTTP websocket handshake
- the code can now be compiled on Linux without errors
- changed sha1.c and sha1.h to the files provided by Evgeny (I replaced those files in src/microhttpd_ws/ with the files from src/microhttpd/ - maybe there is a smarter way...?)
- removed dependency for "htons" and "htonl" (these functions are now implemented in MHD_websocket.c; no need for OS-dependent files anymore)
- added an additional test script for testing of the library with any webbrowser (for manual practice test)
- several bugfixes
- parameters renamed
- special things clarified (fragmentation, RNG for client mode)
The new version of the API is at some points incompatible with the old version, but since it was in an experimental phase and it didn't compile on Linux, I guess this shouldn't bother anyone.
From my point of view, I am now finished with the library and it could go out of experimental.
Diffstat (limited to 'src/microhttpd_ws/test_websocket_browser.c')
-rw-r--r-- | src/microhttpd_ws/test_websocket_browser.c | 563 |
1 files changed, 563 insertions, 0 deletions
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 | |||