aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gausmann <David.Gausmann@measX.com>2021-10-17 19:53:09 +0200
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-31 16:02:31 +0300
commitddad59c1e3a5d20b19003b1d47673501b0665b36 (patch)
tree8cd367e724477bc092d7dd38c83cd15f4c12c95f
parentffbb000f890f23e14d972a6660168aff4a97b66c (diff)
downloadlibmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.tar.gz
libmicrohttpd-ddad59c1e3a5d20b19003b1d47673501b0665b36.zip
websocket update
- added API documentation to libmicrohttpd.texi - added websocket tutorial chapter to libmicrohttpd-tutorial and an much easier example for the tutorial - added additional helper functions to ease the HTTP websocket handshake - the code can now be compiled on Linux without errors - changed sha1.c and sha1.h to the files provided by Evgeny (I replaced those files in src/microhttpd_ws/ with the files from src/microhttpd/ - maybe there is a smarter way...?) - removed dependency for "htons" and "htonl" (these functions are now implemented in MHD_websocket.c; no need for OS-dependent files anymore) - added an additional test script for testing of the library with any webbrowser (for manual practice test) - several bugfixes - parameters renamed - special things clarified (fragmentation, RNG for client mode) The new version of the API is at some points incompatible with the old version, but since it was in an experimental phase and it didn't compile on Linux, I guess this shouldn't bother anyone. From my point of view, I am now finished with the library and it could go out of experimental.
-rw-r--r--doc/chapters/bibliography.inc3
-rw-r--r--doc/chapters/websocket.inc886
-rw-r--r--doc/examples/websocket.c446
-rw-r--r--doc/libmicrohttpd-tutorial.texi12
-rw-r--r--doc/libmicrohttpd.texi1281
-rw-r--r--src/examples/websocket_chatserver_example.c1051
-rw-r--r--src/include/microhttpd_ws.h534
-rw-r--r--src/microhttpd_ws/mhd_websocket.c587
-rw-r--r--src/microhttpd_ws/sha1.c720
-rw-r--r--src/microhttpd_ws/sha1.h245
-rw-r--r--src/microhttpd_ws/test_websocket.c1247
-rw-r--r--src/microhttpd_ws/test_websocket_browser.c563
12 files changed, 6141 insertions, 1434 deletions
diff --git a/doc/chapters/bibliography.inc b/doc/chapters/bibliography.inc
index cc288bc0..bdaa6187 100644
--- a/doc/chapters/bibliography.inc
+++ b/doc/chapters/bibliography.inc
@@ -16,6 +16,9 @@ All referenced RFCs can be found on the website of @emph{The Internet Engineerin
16@emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., 16@emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P.,
17Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Authentication", RFC 2617, June 1999. 17Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Authentication", RFC 2617, June 1999.
18 18
19@item
20@emph{RFC 6455}: Fette, I., Melnikov, A., "The WebSocket Protocol", RFC 6455, December 2011.
21
19 22
20@item 23@item
21A well--structured @emph{HTML} reference can be found on 24A well--structured @emph{HTML} reference can be found on
diff --git a/doc/chapters/websocket.inc b/doc/chapters/websocket.inc
new file mode 100644
index 00000000..a480fd13
--- /dev/null
+++ b/doc/chapters/websocket.inc
@@ -0,0 +1,886 @@
1Websockets are a genuine way to implement push notifications,
2where the server initiates the communication while the client can be idle.
3Usually a HTTP communication is half-duplex and always requested by the client,
4but websockets are full-duplex and only initialized by the client.
5In the further communication both sites can use the websocket at any time
6to send data to the other site.
7
8To initialize a websocket connection the client sends a special HTTP request
9to the server and initializes
10a handshake between client and server which switches from the HTTP protocol
11to the websocket protocol.
12Thus both the server as well as the client must support websockets.
13If proxys are used, they must support websockets too.
14In this chapter we take a look on server and client, but with a focus on
15the server with @emph{libmicrohttpd}.
16
17Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
18which is required for switching from the HTTP protocol.
19Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
20to support the websocket protocol.
21
22@heading Upgrading connections with libmicrohttpd
23
24To support websockets we need to enable upgrading of HTTP connections first.
25This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
26@code{MHD_start_daemon()}.
27
28
29@verbatim
30daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
31 MHD_USE_THREAD_PER_CONNECTION |
32 MHD_ALLOW_UPGRADE |
33 MHD_USE_ERROR_LOG,
34 PORT, NULL, NULL,
35 &access_handler, NULL,
36 MHD_OPTION_END);
37@end verbatim
38@noindent
39
40
41The next step is to turn a specific request into an upgraded connection.
42This done in our @code{access_handler} by calling
43@code{MHD_create_response_for_upgrade()}.
44An @code{upgrade_handler} will be passed to perform the low-level actions
45on the socket.
46
47@emph{Please note that the socket here is just a regular socket as provided
48by the operating system.
49To use it as a websocket, some more steps from the following
50chapters are required.}
51
52
53@verbatim
54static enum MHD_Result
55access_handler (void *cls,
56 struct MHD_Connection *connection,
57 const char *url,
58 const char *method,
59 const char *version,
60 const char *upload_data,
61 size_t *upload_data_size,
62 void **ptr)
63{
64 /* ... */
65 /* some code to decide whether to upgrade or not */
66 /* ... */
67
68 /* create the response for upgrade */
69 response = MHD_create_response_for_upgrade (&upgrade_handler,
70 NULL);
71
72 /* ... */
73 /* additional headers, etc. */
74 /* ... */
75
76 ret = MHD_queue_response (connection,
77 MHD_HTTP_SWITCHING_PROTOCOLS,
78 response);
79 MHD_destroy_response (response);
80
81 return ret;
82}
83@end verbatim
84@noindent
85
86
87In the @code{upgrade_handler} we receive the low-level socket,
88which is used for the communication with the specific client.
89In addition to the low-level socket we get:
90@itemize @bullet
91@item
92Some data, which has been read too much while @emph{libmicrohttpd} was
93switching the protocols.
94This value is usually empty, because it would mean that the client
95has sent data before the handshake was complete.
96
97@item
98A @code{struct MHD_UpgradeResponseHandle} which is used to perform
99special actions like closing, corking or uncorking the socket.
100These commands are executed by passing the handle
101to @code{MHD_upgrade_action()}.
102
103
104@end itemize
105
106Depending of the flags specified while calling @code{MHD_start_deamon()}
107our @code{upgrade_handler} is either executed in the same thread
108as our deamon or in a thread specific for each connection.
109If it is executed in the same thread then @code{upgrade_handler} is
110a blocking call for our webserver and
111we should finish it as fast as possible (i. e. by creating a thread and
112passing the information there).
113If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
114@code{MHD_start_daemon()} then a separate thread is used and
115thus our @code{upgrade_handler} needs not to start a separate thread.
116
117An @code{upgrade_handler}, which is called with a separate thread
118per connection, could look like this:
119
120
121@verbatim
122static void
123upgrade_handler (void *cls,
124 struct MHD_Connection *connection,
125 void *con_cls,
126 const char *extra_in,
127 size_t extra_in_size,
128 MHD_socket fd,
129 struct MHD_UpgradeResponseHandle *urh)
130{
131 /* ... */
132 /* do something with the socket `fd` like `recv()` or `send()` */
133 /* ... */
134
135 /* close the socket when it is not needed anymore */
136 MHD_upgrade_action (urh,
137 MHD_UPGRADE_ACTION_CLOSE);
138}
139@end verbatim
140@noindent
141
142
143This is all you need to know for upgrading connections
144with @emph{libmicrohttpd}.
145The next chapters focus on using the websocket protocol
146with @emph{libmicrohttpd_ws}.
147
148
149@heading Websocket handshake with libmicrohttpd_ws
150
151To request a websocket connection the client must send
152the following information with the HTTP request:
153
154@itemize @bullet
155@item
156A @code{GET} request must be sent.
157
158@item
159The version of the HTTP protocol must be 1.1 or higher.
160
161@item
162A @code{Host} header field must be sent
163
164@item
165A @code{Upgrade} header field containing the keyword "websocket"
166(case-insensitive).
167Please note that the client could pass multiple protocols separated by comma.
168
169@item
170A @code{Connection} header field that includes the token "Upgrade"
171(case-insensitive).
172Please note that the client could pass multiple tokens separated by comma.
173
174@item
175A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
176The decoded the value is 16 bytes long
177and has been generated randomly by the client.
178
179@item
180A @code{Sec-WebSocket-Version} header field with the value "13".
181
182@end itemize
183
184
185Optionally the client can also send the following information:
186
187
188@itemize @bullet
189@item
190A @code{Origin} header field can be used to determine the source
191of the client (i. e. the website).
192
193@item
194A @code{Sec-WebSocket-Protocol} header field can contain a list
195of supported protocols by the client, which can be sent over the websocket.
196
197@item
198A @code{Sec-WebSocket-Extensions} header field which may contain extensions
199to the websocket protocol. The extensions must be registered by IANA.
200
201@end itemize
202
203
204A valid example request from the client could look like this:
205
206
207@verbatim
208GET /chat HTTP/1.1
209Host: server.example.com
210Upgrade: websocket
211Connection: Upgrade
212Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
213Sec-WebSocket-Version: 13
214@end verbatim
215@noindent
216
217
218To complete the handshake the server must respond with
219some specific response headers:
220
221@itemize @bullet
222@item
223The HTTP response code @code{101 Switching Protocols} must be answered.
224
225@item
226An @code{Upgrade} header field containing the value "websocket" must be sent.
227
228@item
229A @code{Connection} header field containing the value "Upgrade" must be sent.
230
231@item
232A @code{Sec-WebSocket-Accept} header field containing a value, which
233has been calculated from the @code{Sec-WebSocket-Key} request header field,
234must be sent.
235
236@end itemize
237
238
239Optionally the server may send following headers:
240
241
242@itemize @bullet
243@item
244A @code{Sec-WebSocket-Protocol} header field containing a protocol
245of the list specified in the corresponding request header field.
246
247@item
248A @code{Sec-WebSocket-Extension} header field containing all used extensions
249of the list specified in the corresponding request header field.
250
251@end itemize
252
253
254A valid websocket HTTP response could look like this:
255
256@verbatim
257HTTP/1.1 101 Switching Protocols
258Upgrade: websocket
259Connection: Upgrade
260Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
261@end verbatim
262@noindent
263
264
265To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
266some helper functions for the @code{access_handler} callback function:
267
268@itemize @bullet
269@item
270@code{MHD_websocket_check_http_version()} checks whether the HTTP version
271is 1.1 or above.
272
273@item
274@code{MHD_websocket_check_connection_header()} checks whether the value
275of the @code{Connection} request header field contains
276an "Upgrade" token (case-insensitive).
277
278@item
279@code{MHD_websocket_check_upgrade_header()} checks whether the value
280of the @code{Upgrade} request header field contains
281the "websocket" keyword (case-insensitive).
282
283@item
284@code{MHD_websocket_check_version_header()} checks whether the value
285of the @code{Sec-WebSocket-Version} request header field is "13".
286
287@item
288@code{MHD_websocket_create_accept_header()} takes the value from
289the @code{Sec-WebSocket-Key} request header and calculates the value
290for the @code{Sec-WebSocket-Accept} response header field.
291
292@end itemize
293
294
295The @code{access_handler} example of the previous chapter can now be
296extended with these helper functions to perform the websocket handshake:
297
298@verbatim
299static enum MHD_Result
300access_handler (void *cls,
301 struct MHD_Connection *connection,
302 const char *url,
303 const char *method,
304 const char *version,
305 const char *upload_data,
306 size_t *upload_data_size,
307 void **ptr)
308{
309 static int aptr;
310 struct MHD_Response *response;
311 int ret;
312
313 (void) cls; /* Unused. Silent compiler warning. */
314 (void) upload_data; /* Unused. Silent compiler warning. */
315 (void) upload_data_size; /* Unused. Silent compiler warning. */
316
317 if (0 != strcmp (method, "GET"))
318 return MHD_NO; /* unexpected method */
319 if (&aptr != *ptr)
320 {
321 /* do never respond on first call */
322 *ptr = &aptr;
323 return MHD_YES;
324 }
325 *ptr = NULL; /* reset when done */
326
327 if (0 == strcmp (url, "/"))
328 {
329 /* Default page for visiting the server */
330 struct MHD_Response *response = MHD_create_response_from_buffer (
331 strlen (PAGE),
332 PAGE,
333 MHD_RESPMEM_PERSISTENT);
334 ret = MHD_queue_response (connection,
335 MHD_HTTP_OK,
336 response);
337 MHD_destroy_response (response);
338 }
339 else if (0 == strcmp (url, "/chat"))
340 {
341 char is_valid = 1;
342 const char* value = NULL;
343 char sec_websocket_accept[29];
344
345 if (0 != MHD_websocket_check_http_version (version))
346 {
347 is_valid = 0;
348 }
349 value = MHD_lookup_connection_value (connection,
350 MHD_HEADER_KIND,
351 MHD_HTTP_HEADER_CONNECTION);
352 if (0 != MHD_websocket_check_connection_header (value))
353 {
354 is_valid = 0;
355 }
356 value = MHD_lookup_connection_value (connection,
357 MHD_HEADER_KIND,
358 MHD_HTTP_HEADER_UPGRADE);
359 if (0 != MHD_websocket_check_upgrade_header (value))
360 {
361 is_valid = 0;
362 }
363 value = MHD_lookup_connection_value (connection,
364 MHD_HEADER_KIND,
365 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
366 if (0 != MHD_websocket_check_version_header (value))
367 {
368 is_valid = 0;
369 }
370 value = MHD_lookup_connection_value (connection,
371 MHD_HEADER_KIND,
372 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
373 if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
374 {
375 is_valid = 0;
376 }
377
378 if (1 == is_valid)
379 {
380 /* upgrade the connection */
381 response = MHD_create_response_for_upgrade (&upgrade_handler,
382 NULL);
383 MHD_add_response_header (response,
384 MHD_HTTP_HEADER_CONNECTION,
385 "Upgrade");
386 MHD_add_response_header (response,
387 MHD_HTTP_HEADER_UPGRADE,
388 "websocket");
389 MHD_add_response_header (response,
390 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
391 sec_websocket_accept);
392 ret = MHD_queue_response (connection,
393 MHD_HTTP_SWITCHING_PROTOCOLS,
394 response);
395 MHD_destroy_response (response);
396 }
397 else
398 {
399 /* return error page */
400 struct MHD_Response*response = MHD_create_response_from_buffer (
401 strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
402 PAGE_INVALID_WEBSOCKET_REQUEST,
403 MHD_RESPMEM_PERSISTENT);
404 ret = MHD_queue_response (connection,
405 MHD_HTTP_BAD_REQUEST,
406 response);
407 MHD_destroy_response (response);
408 }
409 }
410 else
411 {
412 struct MHD_Response*response = MHD_create_response_from_buffer (
413 strlen (PAGE_NOT_FOUND),
414 PAGE_NOT_FOUND,
415 MHD_RESPMEM_PERSISTENT);
416 ret = MHD_queue_response (connection,
417 MHD_HTTP_NOT_FOUND,
418 response);
419 MHD_destroy_response (response);
420 }
421
422 return ret;
423}
424@end verbatim
425@noindent
426
427Please note that we skipped the check of the Host header field here,
428because we don't know the host for this example.
429
430@heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
431
432Once the websocket connection is established you can receive/send frame data
433with the low-level socket functions @code{recv()} and @code{send()}.
434The frame data which goes over the low-level socket is encoded according
435to the websocket protocol.
436To use received payload data, you need to decode the frame data first.
437To send payload data, you need to encode it into frame data first.
438
439@emph{libmicrohttpd_ws} provides serveral functions for encoding of
440payload data and decoding of frame data:
441
442@itemize @bullet
443@item
444@code{MHD_websocket_decode()} decodes received frame data.
445The payload data may be of any kind, depending upon what the client has sent.
446So this decode function is used for all kind of frames and returns
447the frame type along with the payload data.
448
449@item
450@code{MHD_websocket_encode_text()} encodes text.
451The text must be encoded with UTF-8.
452
453@item
454@code{MHD_websocket_encode_binary()} encodes binary data.
455
456@item
457@code{MHD_websocket_encode_ping()} encodes a ping request to
458check whether the websocket is still valid and to test latency.
459
460@item
461@code{MHD_websocket_encode_ping()} encodes a pong response to
462answer a received ping request.
463
464@item
465@code{MHD_websocket_encode_close()} encodes a close request.
466
467@item
468@code{MHD_websocket_free()} frees data returned by the encode/decode functions.
469
470@end itemize
471
472Since you could receive or send fragmented data (i. e. due to a too
473small buffer passed to @code{recv}) all of these encode/decode
474functions require a pointer to a @code{struct MHD_WebSocketStream} passed
475as argument.
476In this structure @emph{libmicrohttpd_ws} stores information
477about encoding/decoding of the particular websocket.
478For each websocket you need a unique @code{struct MHD_WebSocketStream}
479to encode/decode with this library.
480
481To create or destroy @code{struct MHD_WebSocketStream}
482we have additional functions:
483
484@itemize @bullet
485@item
486@code{MHD_websocket_stream_init()} allocates and initializes
487a new @code{struct MHD_WebSocketStream}.
488You can specify some options here to alter the behavior of the websocket stream.
489
490@item
491@code{MHD_websocket_stream_free()} frees a previously allocated
492@code{struct MHD_WebSocketStream}.
493
494@end itemize
495
496With these encode/decode functions we can improve our @code{upgrade_handler}
497callback function from an earlier example to a working websocket:
498
499
500@verbatim
501static void
502upgrade_handler (void *cls,
503 struct MHD_Connection *connection,
504 void *con_cls,
505 const char *extra_in,
506 size_t extra_in_size,
507 MHD_socket fd,
508 struct MHD_UpgradeResponseHandle *urh)
509{
510 /* make the socket blocking (operating-system-dependent code) */
511 make_blocking (fd);
512
513 /* create a websocket stream for this connection */
514 struct MHD_WebSocketStream* ws;
515 int result = MHD_websocket_stream_init (&ws,
516 0,
517 0);
518 if (0 != result)
519 {
520 /* Couldn't create the websocket stream.
521 * So we close the socket and leave
522 */
523 MHD_upgrade_action (urh,
524 MHD_UPGRADE_ACTION_CLOSE);
525 return;
526 }
527
528 /* Let's wait for incoming data */
529 const size_t buf_len = 256;
530 char buf[buf_len];
531 ssize_t got;
532 while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
533 {
534 got = recv (fd,
535 buf,
536 buf_len,
537 0);
538 if (0 >= got)
539 {
540 /* the TCP/IP socket has been closed */
541 break;
542 }
543
544 /* parse the entire received data */
545 size_t buf_offset = 0;
546 while (buf_offset < (size_t) got)
547 {
548 size_t new_offset = 0;
549 char *frame_data = NULL;
550 size_t frame_len = 0;
551 int status = MHD_websocket_decode (ws,
552 buf + buf_offset,
553 ((size_t) got) - buf_offset,
554 &new_offset,
555 &frame_data,
556 &frame_len);
557 if (0 > status)
558 {
559 /* an error occurred and the connection must be closed */
560 if (NULL != frame_data)
561 {
562 MHD_websocket_free (ws, frame_data);
563 }
564 break;
565 }
566 else
567 {
568 buf_offset += new_offset;
569 if (0 < status)
570 {
571 /* the frame is complete */
572 switch (status)
573 {
574 case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
575 /* The client has sent some text.
576 * We will display it and answer with a text frame.
577 */
578 if (NULL != frame_data)
579 {
580 printf ("Received message: %s\n", frame_data);
581 MHD_websocket_free (ws, frame_data);
582 frame_data = NULL;
583 }
584 result = MHD_websocket_encode_text (ws,
585 "Hello",
586 5, /* length of "Hello" */
587 0,
588 &frame_data,
589 &frame_len,
590 NULL);
591 if (0 == result)
592 {
593 send_all (fd,
594 frame_data,
595 frame_len);
596 }
597 break;
598
599 case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
600 /* if we receive a close frame, we will respond with one */
601 MHD_websocket_free (ws,
602 frame_data);
603 frame_data = NULL;
604
605 result = MHD_websocket_encode_close (ws,
606 0,
607 NULL,
608 0,
609 &frame_data,
610 &frame_len);
611 if (0 == result)
612 {
613 send_all (fd,
614 frame_data,
615 frame_len);
616 }
617 break;
618
619 case MHD_WEBSOCKET_STATUS_PING_FRAME:
620 /* if we receive a ping frame, we will respond */
621 /* with the corresponding pong frame */
622 {
623 char *pong = NULL;
624 size_t pong_len = 0;
625 result = MHD_websocket_encode_pong (ws,
626 frame_data,
627 frame_len,
628 &pong,
629 &pong_len);
630 if (0 == result)
631 {
632 send_all (fd,
633 pong,
634 pong_len);
635 }
636 MHD_websocket_free (ws,
637 pong);
638 }
639 break;
640
641 default:
642 /* Other frame types are ignored
643 * in this minimal example.
644 * This is valid, because they become
645 * automatically skipped if we receive them unexpectedly
646 */
647 break;
648 }
649 }
650 if (NULL != frame_data)
651 {
652 MHD_websocket_free (ws, frame_data);
653 }
654 }
655 }
656 }
657
658 /* free the websocket stream */
659 MHD_websocket_stream_free (ws);
660
661 /* close the socket when it is not needed anymore */
662 MHD_upgrade_action (urh,
663 MHD_UPGRADE_ACTION_CLOSE);
664}
665
666/* This helper function is used for the case that
667 * we need to resend some data
668 */
669static void
670send_all (MHD_socket fd,
671 const char *buf,
672 size_t len)
673{
674 ssize_t ret;
675 size_t off;
676
677 for (off = 0; off < len; off += ret)
678 {
679 ret = send (fd,
680 &buf[off],
681 (int) (len - off),
682 0);
683 if (0 > ret)
684 {
685 if (EAGAIN == errno)
686 {
687 ret = 0;
688 continue;
689 }
690 break;
691 }
692 if (0 == ret)
693 break;
694 }
695}
696
697/* This helper function contains operating-system-dependent code and
698 * is used to make a socket blocking.
699 */
700static void
701make_blocking (MHD_socket fd)
702{
703#if defined(MHD_POSIX_SOCKETS)
704 int flags;
705
706 flags = fcntl (fd, F_GETFL);
707 if (-1 == flags)
708 return;
709 if ((flags & ~O_NONBLOCK) != flags)
710 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
711 abort ();
712#elif defined(MHD_WINSOCK_SOCKETS)
713 unsigned long flags = 0;
714
715 ioctlsocket (fd, FIONBIO, &flags);
716#endif /* MHD_WINSOCK_SOCKETS */
717}
718
719@end verbatim
720@noindent
721
722
723Please note that the websocket in this example is only half-duplex.
724It waits until the blocking @code{recv()} call returns and
725only does then something.
726In this example all frame types are decoded by @emph{libmicrohttpd_ws},
727but we only do something when a text, ping or close frame is received.
728Binary and pong frames are ignored in our code.
729This is legit, because the server is only required to implement at
730least support for ping frame or close frame (the other frame types
731could be skipped in theory, because they don't require an answer).
732The pong frame doesn't require an answer and whether text frames or
733binary frames get an answer simply belongs to your server application.
734So this is a valid minimal example.
735
736Until this point you've learned everything you need to basically
737use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
738These libraries offer much more functions for some specific cases.
739
740
741The further chapters of this tutorial focus on some specific problems
742and the client site programming.
743
744
745@heading Using full-duplex websockets
746
747To use full-duplex websockets you can simply create two threads
748per websocket connection.
749One of these threads is used for receiving data with
750a blocking @code{recv()} call and the other thread is triggered
751by the application internal codes and sends the data.
752
753A full-duplex websocket example is implemented in the example file
754@code{websocket_chatserver_example.c}.
755
756@heading Error handling
757
758The most functions of @emph{libmicrohttpd_ws} return a value
759of @code{enum MHD_WEBSOCKET_STATUS}.
760The values of this enumeration can be converted into an integer
761and have an easy interpretation:
762
763@itemize @bullet
764@item
765If the value is less than zero an error occurred and the call has failed.
766Check the enumeration values for more specific information.
767
768@item
769If the value is equal to zero, the call succeeded.
770
771@item
772If the value is greater than zero, the call succeeded and the value
773specifies the decoded frame type.
774Currently positive values are only returned by @code{MHD_websocket_decode()}
775(of the functions with this return enumeration type).
776
777@end itemize
778
779A websocket stream can also get broken when invalid frame data is received.
780Also the other site could send a close frame which puts the stream into
781a state where it may not be used for regular communication.
782Whether a stream has become broken, can be checked with
783@code{MHD_websocket_stream_is_valid()}.
784
785
786@heading Fragmentation
787
788In addition to the regular TCP/IP fragmentation the websocket protocol also
789supports fragmentation.
790Fragmentation could be used for continuous payload data such as video data
791from a webcam.
792Whether or not you want to receive fragmentation is specified upon
793initialization of the websocket stream.
794If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
795of @code{MHD_websocket_stream_init()} then you can receive fragments.
796If you don't pass this flag (in the most cases you just pass zero as flags)
797then you don't want to handle fragments on your own.
798@emph{libmicrohttpd_ws} removes then the fragmentation for you
799in the background.
800You only get the completely assembled frames.
801
802Upon encoding you specify whether or not you want to create a fragmented frame
803by passing a flag to the corresponding encode function.
804Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
805can be used for fragmentation, because the other frame types may
806not be fragmented.
807Encoding fragmented frames is independent of
808the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
809
810@heading Quick guide to websockets in JavaScript
811
812Websockets are supported in all modern web browsers.
813You initialize a websocket connection by creating an instance of
814the @code{WebSocket} class provided by the web browser.
815
816There are some simple rules for using websockets in the browser:
817
818@itemize @bullet
819@item
820When you initialize the instance of the websocket class you must pass an URL.
821The URL must either start with @code{ws://}
822(for not encrypted websocket protocol) or @code{wss://}
823(for TLS-encrypted websocket protocol).
824
825@strong{IMPORTANT:} If your website is accessed via @code{https://}
826then you are in a security context, which means that you are only allowed to
827access other secure protocols.
828So you can only use @code{wss://} for websocket connections then.
829If you try to @code{ws://} instead then your websocket connection will
830automatically fail.
831
832@item
833The WebSocket class uses events to handle the receiving of data.
834JavaScript is per definition a single-threaded language so
835the receiving events will never overlap.
836Sending is done directly by calling a method of the instance of
837the WebSocket class.
838
839@end itemize
840
841
842Here is a short example for receiving/sending data to the same host
843as the website is running on:
844
845@verbatim
846<!DOCTYPE html>
847<html>
848<head>
849<meta charset="UTF-8">
850<title>Websocket Demo</title>
851<script>
852
853let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
854 window.location.host + '/chat';
855let socket = null;
856
857window.onload = function(event) {
858 socket = new WebSocket(url);
859 socket.onopen = function(event) {
860 document.write('The websocket connection has been established.<br>');
861
862 // Send some text
863 socket.send('Hello from JavaScript!');
864 }
865
866 socket.onclose = function(event) {
867 document.write('The websocket connection has been closed.<br>');
868 }
869
870 socket.onerror = function(event) {
871 document.write('An error occurred during the websocket communication.<br>');
872 }
873
874 socket.onmessage = function(event) {
875 document.write('Websocket message received: ' + event.data + '<br>');
876 }
877}
878
879</script>
880</head>
881<body>
882</body>
883</html>
884
885@end verbatim
886@noindent
diff --git a/doc/examples/websocket.c b/doc/examples/websocket.c
new file mode 100644
index 00000000..39995479
--- /dev/null
+++ b/doc/examples/websocket.c
@@ -0,0 +1,446 @@
1/* Feel free to use this example code in any way
2 you see fit (Public Domain) */
3
4#include <sys/types.h>
5#ifndef _WIN32
6#include <sys/select.h>
7#include <sys/socket.h>
8#include <fcntl.h>
9#else
10#include <winsock2.h>
11#endif
12#include <microhttpd.h>
13#include <microhttpd_ws.h>
14#include <time.h>
15#include <string.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <errno.h>
19
20#define PORT 80
21
22#define PAGE \
23 "<!DOCTYPE html>\n" \
24 "<html>\n" \
25 "<head>\n" \
26 "<meta charset=\"UTF-8\">\n" \
27 "<title>Websocket Demo</title>\n" \
28 "<script>\n" \
29 "\n" \
30 "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
31 " + '://' +\n" \
32 " window.location.host + '/chat';\n" \
33 "let socket = null;\n" \
34 "\n" \
35 "window.onload = function(event) {\n" \
36 " socket = new WebSocket(url);\n" \
37 " socket.onopen = function(event) {\n" \
38 " document.write('The websocket connection has been " \
39 "established.<br>');\n" \
40 "\n" \
41 " // Send some text\n" \
42 " socket.send('Hello from JavaScript!');\n" \
43 " }\n" \
44 "\n" \
45 " socket.onclose = function(event) {\n" \
46 " document.write('The websocket connection has been closed.<br>');\n" \
47 " }\n" \
48 "\n" \
49 " socket.onerror = function(event) {\n" \
50 " document.write('An error occurred during the websocket " \
51 "communication.<br>');\n" \
52 " }\n" \
53 "\n" \
54 " socket.onmessage = function(event) {\n" \
55 " document.write('Websocket message received: ' + " \
56 "event.data + '<br>');\n" \
57 " }\n" \
58 "}\n" \
59 "\n" \
60 "</script>\n" \
61 "</head>\n" \
62 "<body>\n" \
63 "</body>\n" \
64 "</html>"
65
66#define PAGE_NOT_FOUND \
67 "404 Not Found"
68
69#define PAGE_INVALID_WEBSOCKET_REQUEST \
70 "Invalid WebSocket request!"
71
72static void
73send_all (MHD_socket fd,
74 const char *buf,
75 size_t len);
76static void
77make_blocking (MHD_socket fd);
78
79static void
80upgrade_handler (void *cls,
81 struct MHD_Connection *connection,
82 void *con_cls,
83 const char *extra_in,
84 size_t extra_in_size,
85 MHD_socket fd,
86 struct MHD_UpgradeResponseHandle *urh)
87{
88 /* make the socket blocking (operating-system-dependent code) */
89 make_blocking (fd);
90
91 /* create a websocket stream for this connection */
92 struct MHD_WebSocketStream* ws;
93 int result = MHD_websocket_stream_init (&ws,
94 0,
95 0);
96 if (0 != result)
97 {
98 /* Couldn't create the websocket stream.
99 * So we close the socket and leave
100 */
101 MHD_upgrade_action (urh,
102 MHD_UPGRADE_ACTION_CLOSE);
103 return;
104 }
105
106 /* Let's wait for incoming data */
107 const size_t buf_len = 256;
108 char buf[buf_len];
109 ssize_t got;
110 while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
111 {
112 got = recv (fd,
113 buf,
114 buf_len,
115 0);
116 if (0 >= got)
117 {
118 /* the TCP/IP socket has been closed */
119 break;
120 }
121
122 /* parse the entire received data */
123 size_t buf_offset = 0;
124 while (buf_offset < (size_t) got)
125 {
126 size_t new_offset = 0;
127 char *frame_data = NULL;
128 size_t frame_len = 0;
129 int status = MHD_websocket_decode (ws,
130 buf + buf_offset,
131 ((size_t) got) - buf_offset,
132 &new_offset,
133 &frame_data,
134 &frame_len);
135 if (0 > status)
136 {
137 /* an error occurred and the connection must be closed */
138 if (NULL != frame_data)
139 {
140 MHD_websocket_free (ws, frame_data);
141 }
142 break;
143 }
144 else
145 {
146 buf_offset += new_offset;
147 if (0 < status)
148 {
149 /* the frame is complete */
150 switch (status)
151 {
152 case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
153 /* The client has sent some text.
154 * We will display it and answer with a text frame.
155 */
156 if (NULL != frame_data)
157 {
158 printf ("Received message: %s\n", frame_data);
159 MHD_websocket_free (ws, frame_data);
160 frame_data = NULL;
161 }
162 result = MHD_websocket_encode_text (ws,
163 "Hello",
164 5, /* length of "Hello" */
165 0,
166 &frame_data,
167 &frame_len,
168 NULL);
169 if (0 == result)
170 {
171 send_all (fd,
172 frame_data,
173 frame_len);
174 }
175 break;
176
177 case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
178 /* if we receive a close frame, we will respond with one */
179 MHD_websocket_free (ws,
180 frame_data);
181 frame_data = NULL;
182
183 result = MHD_websocket_encode_close (ws,
184 0,
185 NULL,
186 0,
187 &frame_data,
188 &frame_len);
189 if (0 == result)
190 {
191 send_all (fd,
192 frame_data,
193 frame_len);
194 }
195 break;
196
197 case MHD_WEBSOCKET_STATUS_PING_FRAME:
198 /* if we receive a ping frame, we will respond */
199 /* with the corresponding pong frame */
200 {
201 char *pong = NULL;
202 size_t pong_len = 0;
203 result = MHD_websocket_encode_pong (ws,
204 frame_data,
205 frame_len,
206 &pong,
207 &pong_len);
208 if (0 == result)
209 {
210 send_all (fd,
211 pong,
212 pong_len);
213 }
214 MHD_websocket_free (ws,
215 pong);
216 }
217 break;
218
219 default:
220 /* Other frame types are ignored
221 * in this minimal example.
222 * This is valid, because they become
223 * automatically skipped if we receive them unexpectedly
224 */
225 break;
226 }
227 }
228 if (NULL != frame_data)
229 {
230 MHD_websocket_free (ws, frame_data);
231 }
232 }
233 }
234 }
235
236 /* free the websocket stream */
237 MHD_websocket_stream_free (ws);
238
239 /* close the socket when it is not needed anymore */
240 MHD_upgrade_action (urh,
241 MHD_UPGRADE_ACTION_CLOSE);
242}
243
244/* This helper function is used for the case that
245 * we need to resend some data
246 */
247static void
248send_all (MHD_socket fd,
249 const char *buf,
250 size_t len)
251{
252 ssize_t ret;
253 size_t off;
254
255 for (off = 0; off < len; off += ret)
256 {
257 ret = send (fd,
258 &buf[off],
259 (int) (len - off),
260 0);
261 if (0 > ret)
262 {
263 if (EAGAIN == errno)
264 {
265 ret = 0;
266 continue;
267 }
268 break;
269 }
270 if (0 == ret)
271 break;
272 }
273}
274
275/* This helper function contains operating-system-dependent code and
276 * is used to make a socket blocking.
277 */
278static void
279make_blocking (MHD_socket fd)
280{
281#ifndef _WIN32
282 int flags;
283
284 flags = fcntl (fd, F_GETFL);
285 if (-1 == flags)
286 return;
287 if ((flags & ~O_NONBLOCK) != flags)
288 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
289 abort ();
290#else
291 unsigned long flags = 0;
292
293 ioctlsocket (fd, FIONBIO, &flags);
294#endif
295}
296
297static enum MHD_Result
298access_handler (void *cls,
299 struct MHD_Connection *connection,
300 const char *url,
301 const char *method,
302 const char *version,
303 const char *upload_data,
304 size_t *upload_data_size,
305 void **ptr)
306{
307 static int aptr;
308 struct MHD_Response *response;
309 int ret;
310
311 (void) cls; /* Unused. Silent compiler warning. */
312 (void) upload_data; /* Unused. Silent compiler warning. */
313 (void) upload_data_size; /* Unused. Silent compiler warning. */
314
315 if (0 != strcmp (method, "GET"))
316 return MHD_NO; /* unexpected method */
317 if (&aptr != *ptr)
318 {
319 /* do never respond on first call */
320 *ptr = &aptr;
321 return MHD_YES;
322 }
323 *ptr = NULL; /* reset when done */
324
325 if (0 == strcmp (url, "/"))
326 {
327 /* Default page for visiting the server */
328 struct MHD_Response *response = MHD_create_response_from_buffer (
329 strlen (PAGE),
330 PAGE,
331 MHD_RESPMEM_PERSISTENT);
332 ret = MHD_queue_response (connection,
333 MHD_HTTP_OK,
334 response);
335 MHD_destroy_response (response);
336 }
337 else if (0 == strcmp (url, "/chat"))
338 {
339 char is_valid = 1;
340 const char* value = NULL;
341 char sec_websocket_accept[29];
342
343 if (0 != MHD_websocket_check_http_version (version))
344 {
345 is_valid = 0;
346 }
347 value = MHD_lookup_connection_value (connection,
348 MHD_HEADER_KIND,
349 MHD_HTTP_HEADER_CONNECTION);
350 if (0 != MHD_websocket_check_connection_header (value))
351 {
352 is_valid = 0;
353 }
354 value = MHD_lookup_connection_value (connection,
355 MHD_HEADER_KIND,
356 MHD_HTTP_HEADER_UPGRADE);
357 if (0 != MHD_websocket_check_upgrade_header (value))
358 {
359 is_valid = 0;
360 }
361 value = MHD_lookup_connection_value (connection,
362 MHD_HEADER_KIND,
363 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
364 if (0 != MHD_websocket_check_version_header (value))
365 {
366 is_valid = 0;
367 }
368 value = MHD_lookup_connection_value (connection,
369 MHD_HEADER_KIND,
370 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
371 if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
372 {
373 is_valid = 0;
374 }
375
376 if (1 == is_valid)
377 {
378 /* upgrade the connection */
379 response = MHD_create_response_for_upgrade (&upgrade_handler,
380 NULL);
381 MHD_add_response_header (response,
382 MHD_HTTP_HEADER_CONNECTION,
383 "Upgrade");
384 MHD_add_response_header (response,
385 MHD_HTTP_HEADER_UPGRADE,
386 "websocket");
387 MHD_add_response_header (response,
388 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
389 sec_websocket_accept);
390 ret = MHD_queue_response (connection,
391 MHD_HTTP_SWITCHING_PROTOCOLS,
392 response);
393 MHD_destroy_response (response);
394 }
395 else
396 {
397 /* return error page */
398 struct MHD_Response*response = MHD_create_response_from_buffer (
399 strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
400 PAGE_INVALID_WEBSOCKET_REQUEST,
401 MHD_RESPMEM_PERSISTENT);
402 ret = MHD_queue_response (connection,
403 MHD_HTTP_BAD_REQUEST,
404 response);
405 MHD_destroy_response (response);
406 }
407 }
408 else
409 {
410 struct MHD_Response*response = MHD_create_response_from_buffer (
411 strlen (PAGE_NOT_FOUND),
412 PAGE_NOT_FOUND,
413 MHD_RESPMEM_PERSISTENT);
414 ret = MHD_queue_response (connection,
415 MHD_HTTP_NOT_FOUND,
416 response);
417 MHD_destroy_response (response);
418 }
419
420 return ret;
421}
422
423int
424main (int argc,
425 char *const *argv)
426{
427 (void) argc; /* Unused. Silent compiler warning. */
428 (void) argv; /* Unused. Silent compiler warning. */
429 struct MHD_Daemon *daemon;
430
431 daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
432 MHD_USE_THREAD_PER_CONNECTION |
433 MHD_ALLOW_UPGRADE |
434 MHD_USE_ERROR_LOG,
435 PORT, NULL, NULL,
436 &access_handler, NULL,
437 MHD_OPTION_END);
438
439 if (NULL == daemon)
440 return 1;
441 (void) getc (stdin);
442
443 MHD_stop_daemon (daemon);
444
445 return 0;
446}
diff --git a/doc/libmicrohttpd-tutorial.texi b/doc/libmicrohttpd-tutorial.texi
index de040fe8..7d3cd23a 100644
--- a/doc/libmicrohttpd-tutorial.texi
+++ b/doc/libmicrohttpd-tutorial.texi
@@ -68,6 +68,7 @@ Free Documentation License".
68* Improved processing of POST data:: 68* Improved processing of POST data::
69* Session management:: 69* Session management::
70* Adding a layer of security:: 70* Adding a layer of security::
71* Websockets::
71* Bibliography:: 72* Bibliography::
72* License text:: 73* License text::
73* Example programs:: 74* Example programs::
@@ -109,6 +110,10 @@ Free Documentation License".
109@chapter Adding a layer of security 110@chapter Adding a layer of security
110@include chapters/tlsauthentication.inc 111@include chapters/tlsauthentication.inc
111 112
113@node Websockets
114@chapter Websockets
115@include chapters/websocket.inc
116
112@node Bibliography 117@node Bibliography
113@appendix Bibliography 118@appendix Bibliography
114@include chapters/bibliography.inc 119@include chapters/bibliography.inc
@@ -128,6 +133,7 @@ Free Documentation License".
128* largepost.c:: 133* largepost.c::
129* sessions.c:: 134* sessions.c::
130* tlsauthentication.c:: 135* tlsauthentication.c::
136* websocket.c::
131@end menu 137@end menu
132 138
133@node hellobrowser.c 139@node hellobrowser.c
@@ -178,4 +184,10 @@ Free Documentation License".
178@verbatiminclude examples/tlsauthentication.c 184@verbatiminclude examples/tlsauthentication.c
179@end smalldisplay 185@end smalldisplay
180 186
187@node websocket.c
188@section websocket.c
189@smalldisplay
190@verbatiminclude examples/websocket.c
191@end smalldisplay
192
181@bye 193@bye
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 8e275a3b..a6bd12eb 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -67,6 +67,7 @@ Free Documentation License".
67* microhttpd-post:: Adding a @code{POST} processor. 67* microhttpd-post:: Adding a @code{POST} processor.
68* microhttpd-info:: Obtaining and modifying status information. 68* microhttpd-info:: Obtaining and modifying status information.
69* microhttpd-util:: Utilities. 69* microhttpd-util:: Utilities.
70* microhttpd-websocket:: Websockets.
70 71
71Appendices 72Appendices
72 73
@@ -1246,6 +1247,493 @@ list.
1246@end deftp 1247@end deftp
1247 1248
1248 1249
1250@deftp {Enumeration} MHD_WEBSOCKET_FLAG
1251@cindex websocket
1252Options for the MHD websocket stream.
1253
1254This is used for initialization of a websocket stream when calling
1255@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} and
1256alters the behavior of the websocket stream.
1257
1258Note that websocket streams are only available if you include the header file
1259@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1260
1261@table @code
1262@item MHD_WEBSOCKET_FLAG_SERVER
1263The websocket stream is initialized in server mode (default).
1264Thus all outgoing payload will not be masked.
1265All incoming payload must be masked.
1266
1267This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_CLIENT}.
1268
1269@item MHD_WEBSOCKET_FLAG_CLIENT
1270The websocket stream is initialized in client mode.
1271You will usually never use that mode in combination with @emph{libmicrohttpd},
1272because @emph{libmicrohttpd} provides a server and not a client.
1273In client mode all outgoing payload will be masked
1274(XOR-ed with random values).
1275All incoming payload must be unmasked.
1276If you use this mode, you must always call @code{MHD_websocket_stream_init2}
1277instead of @code{MHD_websocket_stream_init}, because you need
1278to pass a random number generator callback function for masking.
1279
1280This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_SERVER}.
1281
1282@item MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
1283You don't want to get fragmented data while decoding (default).
1284Fragmented frames will be internally put together until
1285they are complete.
1286Whether or not data is fragmented is decided
1287by the sender of the data during encoding.
1288
1289This cannot be used together with @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}.
1290
1291@item MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
1292You want fragmented data, if it appears while decoding.
1293You will receive the content of the fragmented frame,
1294but if you are decoding text, you will never get an unfinished
1295UTF-8 sequence (if the sequence appears between two fragments).
1296Instead the text will end before the unfinished UTF-8 sequence.
1297With the next fragment, which finishes the UTF-8 sequence,
1298you will get the complete UTF-8 sequence.
1299
1300This cannot be used together with @code{MHD_WEBSOCKET_FLAG_NO_FRAGMENTS}.
1301
1302@item MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
1303If the websocket stream becomes invalid during decoding due to
1304protocol errors, a matching close frame will automatically
1305be generated.
1306The close frame will be returned via the parameters
1307@code{payload} and @code{payload_len} of @code{MHD_websocket_decode} and
1308the return value is negative (a value of @code{enum MHD_WEBSOCKET_STATUS}).
1309
1310The generated close frame must be freed by the caller
1311with @code{MHD_websocket_free}.
1312
1313@end table
1314@end deftp
1315
1316
1317@deftp {Enumeration} MHD_WEBSOCKET_FRAGMENTATION
1318@cindex websocket
1319This enumeration is used to specify the fragmentation behavior
1320when encoding of data (text/binary) for a websocket stream.
1321This is used with @code{MHD_websocket_encode_text} or
1322@code{MHD_websocket_encode_binary}.
1323
1324Note that websocket streams are only available if you include the header file
1325@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1326
1327@table @code
1328@item MHD_WEBSOCKET_FRAGMENTATION_NONE
1329You don't want to use fragmentation.
1330The encoded frame consists of only one frame.
1331
1332@item MHD_WEBSOCKET_FRAGMENTATION_FIRST
1333You want to use fragmentation.
1334The encoded frame is the first frame of
1335a series of data frames of the same type
1336(text or binary).
1337You may send control frames (ping, pong or close)
1338between these data frames.
1339
1340@item MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING
1341You want to use fragmentation.
1342The encoded frame is not the first frame of
1343the series of data frames, but also not the last one.
1344You may send control frames (ping, pong or close)
1345between these data frames.
1346
1347@item MHD_WEBSOCKET_FRAGMENTATION_LAST
1348You want to use fragmentation.
1349The encoded frame is the last frame of
1350the series of data frames, but also not the first one.
1351After this frame, you may send all types of frames again.
1352
1353@end table
1354@end deftp
1355
1356
1357@deftp {Enumeration} MHD_WEBSOCKET_STATUS
1358@cindex websocket
1359This enumeration is used for the return value of almost
1360every websocket stream function.
1361Errors are negative and values equal to or above zero mean a success.
1362Positive values are only used by @code{MHD_websocket_decode}.
1363
1364Note that websocket streams are only available if you include the header file
1365@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1366
1367@table @code
1368@item MHD_WEBSOCKET_STATUS_OK
1369The call succeeded.
1370Especially for @code{MHD_websocket_decode} this means that no error occurred,
1371but also no frame has been completed yet.
1372For other functions this means simply a success.
1373
1374@item MHD_WEBSOCKET_STATUS_TEXT_FRAME
1375@code{MHD_websocket_decode} has decoded a text frame.
1376The parameters @code{payload} and @code{payload_len} are filled with
1377the decoded text (if any).
1378You must free the returned @code{payload} after use with
1379@code{MHD_websocket_free}.
1380
1381@item MHD_WEBSOCKET_STATUS_BINARY_FRAME
1382@code{MHD_websocket_decode} has decoded a binary frame.
1383The parameters @code{payload} and @code{payload_len} are filled with
1384the decoded binary data (if any).
1385You must free the returned @code{payload} after use with
1386@code{MHD_websocket_free}.
1387
1388@item MHD_WEBSOCKET_STATUS_CLOSE_FRAME
1389@code{MHD_websocket_decode} has decoded a close frame.
1390This means you must close the socket using @code{MHD_upgrade_action}
1391with @code{MHD_UPGRADE_ACTION_CLOSE}.
1392You may respond with a close frame before closing.
1393The parameters @code{payload} and @code{payload_len} are filled with
1394the close reason (if any).
1395The close reason starts with a two byte sequence of close code
1396in network byte order (see @code{enum MHD_WEBSOCKET_CLOSEREASON}).
1397After these two bytes a UTF-8 encoded close reason may follow.
1398You can call @code{MHD_websocket_split_close_reason} to split that
1399close reason.
1400You must free the returned @code{payload} after use with
1401@code{MHD_websocket_free}.
1402
1403@item MHD_WEBSOCKET_STATUS_PING_FRAME
1404@code{MHD_websocket_decode} has decoded a ping frame.
1405You should respond to this with a pong frame.
1406The pong frame must contain the same binary data as
1407the corresponding ping frame (if it had any).
1408The parameters @code{payload} and @code{payload_len} are filled with
1409the binary ping data (if any).
1410You must free the returned @code{payload} after use with
1411@code{MHD_websocket_free}.
1412
1413@item MHD_WEBSOCKET_STATUS_PONG_FRAME
1414@code{MHD_websocket_decode} has decoded a pong frame.
1415You should usually only receive pong frames if you sent
1416a ping frame before.
1417The binary data should be equal to your ping frame and can be
1418used to distinguish the response if you sent multiple ping frames.
1419The parameters @code{payload} and @code{payload_len} are filled with
1420the binary pong data (if any).
1421You must free the returned @code{payload} after use with
1422@code{MHD_websocket_free}.
1423
1424@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
1425@code{MHD_websocket_decode} has decoded a text frame fragment.
1426The parameters @code{payload} and @code{payload_len} are filled with
1427the decoded text (if any).
1428This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FRAME}, but it can only
1429appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
1430the call of @code{MHD_websocket_stream_init} or
1431@code{MHD_websocket_stream_init2}.
1432You must free the returned @code{payload} after use with
1433@code{MHD_websocket_free}.
1434
1435@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
1436@code{MHD_websocket_decode} has decoded a binary frame fragment.
1437The parameters @code{payload} and @code{payload_len} are filled with
1438the decoded binary data (if any).
1439This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FRAME}, but it can only
1440appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
1441the call of @code{MHD_websocket_stream_init} or
1442@code{MHD_websocket_stream_init2}.
1443You must free the returned @code{payload} after use with
1444@code{MHD_websocket_free}.
1445
1446@item MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT
1447@code{MHD_websocket_decode} has decoded the next text frame fragment.
1448The parameters @code{payload} and @code{payload_len} are filled with
1449the decoded text (if any).
1450This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
1451only after the first and before the last fragment of a series of fragments.
1452It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
1453during the call of @code{MHD_websocket_stream_init} or
1454@code{MHD_websocket_stream_init2}.
1455You must free the returned @code{payload} after use with
1456@code{MHD_websocket_free}.
1457
1458@item MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT
1459@code{MHD_websocket_decode} has decoded the next binary frame fragment.
1460The parameters @code{payload} and @code{payload_len} are filled with
1461the decoded binary data (if any).
1462This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
1463only after the first and before the last fragment of a series of fragments.
1464It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
1465during the call of @code{MHD_websocket_stream_init} or
1466@code{MHD_websocket_stream_init2}.
1467You must free the returned @code{payload} after use with
1468@code{MHD_websocket_free}.
1469
1470@item MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT
1471@code{MHD_websocket_decode} has decoded the last text frame fragment.
1472The parameters @code{payload} and @code{payload_len} are filled with
1473the decoded text (if any).
1474This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
1475only for the last fragment of a series of fragments.
1476It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
1477during the call of @code{MHD_websocket_stream_init} or
1478@code{MHD_websocket_stream_init2}.
1479You must free the returned @code{payload} after use with
1480@code{MHD_websocket_free}.
1481
1482@item MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT
1483@code{MHD_websocket_decode} has decoded the last binary frame fragment.
1484The parameters @code{payload} and @code{payload_len} are filled with
1485the decoded binary data (if any).
1486This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
1487only for the last fragment of a series of fragments.
1488It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
1489during the call of @code{MHD_websocket_stream_init} or
1490@code{MHD_websocket_stream_init2}.
1491You must free the returned @code{payload} after use with
1492@code{MHD_websocket_free}.
1493
1494@item MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR
1495The call failed and the stream is invalid now for decoding.
1496You must close the websocket now using @code{MHD_upgrade_action}
1497with @code{MHD_UPGRADE_ACTION_CLOSE}.
1498You may send a close frame before closing.
1499This is only used by @code{MHD_websocket_decode} and happens
1500if the stream contains errors (i. e. invalid byte data).
1501
1502@item MHD_WEBSOCKET_STATUS_STREAM_BROKEN
1503You tried to decode something, but the stream has already
1504been marked invalid.
1505You must close the websocket now using @code{MHD_upgrade_action}
1506with @code{MHD_UPGRADE_ACTION_CLOSE}.
1507You may send a close frame before closing.
1508This is only used by @code{MHD_websocket_decode} and happens
1509if you call @code{MDM_websocket_decode} again after
1510has been invalidated.
1511You can call @code{MHD_websocket_stream_is_valid} at any time
1512to check whether a stream is invalid or not.
1513
1514@item MHD_WEBSOCKET_STATUS_MEMORY_ERROR
1515A memory allocation failed. The stream remains valid.
1516If this occurred while decoding, the decoding could be
1517possible later if enough memory is available.
1518This could happen while decoding if you received a too big data frame.
1519You could try to specify max_payload_size during the call of
1520@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} to
1521avoid this and close the websocket instead.
1522
1523@item MHD_WEBSOCKET_STATUS_PARAMETER_ERROR
1524You passed invalid parameters during the function call
1525(i. e. a NULL pointer for a required parameter).
1526The stream remains valid.
1527
1528@item MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED
1529The maximum payload size has been exceeded.
1530If you got this return code from @code{MHD_websocket_decode} then
1531the stream becomes invalid and the websocket must be closed
1532using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
1533You may send a close frame before closing.
1534The maximum payload size is specified during the call of
1535@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
1536This can also appear if you specified 0 as maximum payload size
1537when the message is greater than the maximum allocatable memory size
1538(i. e. more than 4 GiB on 32 bit systems).
1539If you got this return code from @code{MHD_websocket_encode_close},
1540@code{MHD_websocket_encode_ping} or @code{MHD_websocket_encode_pong} then
1541you passed to much payload data. The stream remains valid then.
1542
1543@item MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR
1544An UTF-8 sequence is invalid.
1545If you got this return code from @code{MHD_websocket_decode} then
1546the stream becomes invalid and you must close the websocket
1547using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
1548You may send a close frame before closing.
1549If you got this from @code{MHD_websocket_encode_text} or
1550@code{MHD_websocket_encode_close} then you passed invalid UTF-8 text.
1551The stream remains valid then.
1552
1553@item MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER
1554A check routine for the HTTP headers came to the conclusion that
1555the header value isn't valid for a websocket handshake request.
1556This value can only be returned from the following functions:
1557@code{MHD_websocket_check_http_version},
1558@code{MHD_websocket_check_connection_header},
1559@code{MHD_websocket_check_upgrade_header},
1560@code{MHD_websocket_check_version_header},
1561@code{MHD_websocket_create_accept_header}
1562
1563@end table
1564@end deftp
1565
1566
1567@deftp {Enumeration} MHD_WEBSOCKET_CLOSEREASON
1568@cindex websocket
1569Enumeration of possible close reasons for websocket close frames.
1570
1571The possible values are specified in RFC 6455 7.4.1
1572These close reasons here are the default set specified by RFC 6455,
1573but also other close reasons could be used.
1574
1575The definition is for short:
1576@itemize @bullet
1577@item 0-999 are never used (if you pass 0 in
1578@code{MHD_websocket_encode_close} then no close reason is used).
1579@item 1000-2999 are specified by RFC 6455.
1580@item 3000-3999 are specified by libraries, etc. but must be registered by IANA.
1581@item 4000-4999 are reserved for private use.
1582@end itemize
1583
1584Note that websocket streams are only available if you include the header file
1585@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1586
1587@table @code
1588@item MHD_WEBSOCKET_CLOSEREASON_NO_REASON
1589This value is used as placeholder for @code{MHD_websocket_encode_close}
1590to tell that you don't want to specify any reason.
1591If you use this value then no reason text may be used.
1592This value cannot be a result of decoding, because this value
1593is not a valid close reason for the websocket protocol.
1594
1595@item MHD_WEBSOCKET_CLOSEREASON_REGULAR
1596You close the websocket because it fulfilled its purpose and shall
1597now be closed in a normal, planned way.
1598
1599@item MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY
1600You close the websocket because you are shutting down the server or
1601something similar.
1602
1603@item MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR
1604You close the websocket because a protocol error occurred
1605during decoding (i. e. invalid byte data).
1606
1607@item MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE
1608You close the websocket because you received data which you don't accept.
1609For example if you received a binary frame,
1610but your application only expects text frames.
1611
1612@item MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8
1613You close the websocket because it contains malformed UTF-8.
1614The UTF-8 validity is automatically checked by @code{MHD_websocket_decode},
1615so you don't need to check it on your own.
1616UTF-8 is specified in RFC 3629.
1617
1618@item MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED
1619You close the websocket because you received a frame which is too big
1620to process.
1621You can specify the maximum allowed payload size during the call of
1622@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
1623
1624@item MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION
1625This status code can be sent by the client if it
1626expected a specific extension, but this extension hasn't been negotiated.
1627
1628@item MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION
1629The server closes the websocket because it encountered
1630an unexpected condition that prevented it from fulfilling the request.
1631
1632@end table
1633@end deftp
1634
1635
1636@deftp {Enumeration} MHD_WEBSOCKET_UTF8STEP
1637@cindex websocket
1638Enumeration of possible UTF-8 check steps for websocket functions
1639
1640These values are used during the encoding of fragmented text frames
1641or for error analysis while encoding text frames.
1642Its values specify the next step of the UTF-8 check.
1643UTF-8 sequences consist of one to four bytes.
1644This enumeration just says how long the current UTF-8 sequence is
1645and what is the next expected byte.
1646
1647Note that websocket streams are only available if you include the header file
1648@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1649
1650@table @code
1651@item MHD_WEBSOCKET_UTF8STEP_NORMAL
1652There is no open UTF-8 sequence.
1653The next byte must be 0x00-0x7F or 0xC2-0xF4.
1654
1655@item MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1
1656The second byte of a two byte UTF-8 sequence.
1657The first byte was 0xC2-0xDF.
1658The next byte must be 0x80-0xBF.
1659
1660@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2
1661The second byte of a three byte UTF-8 sequence.
1662The first byte was 0xE0.
1663The next byte must be 0xA0-0xBF.
1664
1665@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2
1666The second byte of a three byte UTF-8 sequence.
1667The first byte was 0xED.
1668The next byte must by 0x80-0x9F.
1669
1670@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2
1671The second byte of a three byte UTF-8 sequence.
1672The first byte was 0xE1-0xEC or 0xEE-0xEF.
1673The next byte must be 0x80-0xBF.
1674
1675@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2
1676The third byte of a three byte UTF-8 sequence.
1677The next byte must be 0x80-0xBF.
1678
1679@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3
1680The second byte of a four byte UTF-8 sequence.
1681The first byte was 0xF0.
1682The next byte must be 0x90-0xBF.
1683
1684@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3
1685The second byte of a four byte UTF-8 sequence.
1686The first byte was 0xF4.
1687The next byte must be 0x80-0x8F.
1688
1689@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3
1690The second byte of a four byte UTF-8 sequence.
1691The first byte was 0xF1-0xF3.
1692The next byte must be 0x80-0xBF.
1693
1694@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3
1695The third byte of a four byte UTF-8 sequence.
1696The next byte must be 0x80-0xBF.
1697
1698@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3
1699The fourth byte of a four byte UTF-8 sequence.
1700The next byte must be 0x80-0xBF.
1701
1702@end table
1703@end deftp
1704
1705
1706@deftp {Enumeration} MHD_WEBSOCKET_VALIDITY
1707@cindex websocket
1708Enumeration of validity values of a websocket stream
1709
1710These values are used for @code{MHD_websocket_stream_is_valid}
1711and specify the validity status.
1712
1713Note that websocket streams are only available if you include the header file
1714@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
1715
1716@table @code
1717@item MHD_WEBSOCKET_VALIDITY_INVALID
1718The stream is invalid.
1719It cannot be used for decoding anymore.
1720
1721@item MHD_WEBSOCKET_VALIDITY_VALID
1722The stream is valid.
1723Decoding works as expected.
1724
1725@item MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES
1726The stream has received a close frame and
1727is partly invalid.
1728You can still use the stream for decoding,
1729but if a data frame is received an error will be reported.
1730After a close frame has been sent, no data frames
1731may follow from the sender of the close frame.
1732
1733@end table
1734@end deftp
1735
1736
1249@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1737@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1250 1738
1251@c ------------------------------------------------------------ 1739@c ------------------------------------------------------------
@@ -1291,6 +1779,12 @@ Information about an MHD daemon.
1291@end deftp 1779@end deftp
1292 1780
1293 1781
1782@deftp {C Struct} MHD_WebSocketStream
1783@cindex websocket
1784Information about a MHD websocket stream.
1785@end deftp
1786
1787
1294@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1788@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1295 1789
1296@c ------------------------------------------------------------ 1790@c ------------------------------------------------------------
@@ -1549,6 +2043,95 @@ iteration.
1549@end deftypefn 2043@end deftypefn
1550 2044
1551 2045
2046@deftypefn {Function Pointer} void* {*MHD_WebSocketMallocCallback} (size_t buf_len)
2047@cindex websocket
2048This callback function is used internally by many websocket functions
2049for allocating data.
2050By default @code{malloc} is used.
2051You can use your own allocation function with @code{MHD_websocket_stream_init2}
2052if you wish to.
2053This can be useful for operating systems like Windows
2054where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
2055You can call the associated @code{malloc} callback of
2056a websocket stream with @code{MHD_websocket_malloc}.
2057
2058@table @var
2059@item buf_len
2060size of the buffer to allocate in bytes.
2061@end table
2062
2063Return the pointer of the allocated buffer or @code{NULL} on failure.
2064@end deftypefn
2065
2066
2067@deftypefn {Function Pointer} void* {*MHD_WebSocketReallocCallback} (void *buf, size_t new_buf_len)
2068@cindex websocket
2069This callback function is used internally by many websocket
2070functions for reallocating data.
2071By default @code{realloc} is used.
2072You can use your own reallocation function with
2073@code{MHD_websocket_stream_init2} if you wish to.
2074This can be useful for operating systems like Windows
2075where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
2076You can call the associated @code{realloc} callback of
2077a websocket stream with @code{MHD_websocket_realloc}.
2078
2079@table @var
2080@item buf
2081current buffer, may be @code{NULL};
2082
2083@item new_buf_len
2084new size of the buffer in bytes.
2085@end table
2086
2087Return the pointer of the reallocated buffer or @code{NULL} on failure.
2088On failure the old pointer must remain valid.
2089@end deftypefn
2090
2091
2092@deftypefn {Function Pointer} void {*MHD_WebSocketFreeCallback} (void *buf)
2093@cindex websocket
2094This callback function is used internally by many websocket
2095functions for freeing data.
2096By default @code{free} is used.
2097You can use your own free function with
2098@code{MHD_websocket_stream_init2} if you wish to.
2099This can be useful for operating systems like Windows
2100where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
2101You can call the associated @code{free} callback of
2102a websocket stream with @code{MHD_websocket_free}.
2103
2104@table @var
2105@item cls
2106current buffer to free, this may be @code{NULL} then nothing happens.
2107@end table
2108@end deftypefn
2109
2110
2111@deftypefn {Function Pointer} size_t {*MHD_WebSocketRandomNumberGenerator} (void *cls, void* buf, size_t buf_len)
2112@cindex websocket
2113This callback function is used for generating random numbers
2114for masking payload data in client mode.
2115If you use websockets in server mode with @emph{libmicrohttpd} then
2116you don't need a random number generator, because
2117the server doesn't mask its outgoing messages.
2118However if you wish to use a websocket stream in client mode,
2119you must pass this callback function to @code{MHD_websocket_stream_init2}.
2120
2121@table @var
2122@item cls
2123closure specified in @code{MHD_websocket_stream_init2};
2124@item buf
2125buffer to fill with random values;
2126@item buf_len
2127size of buffer in bytes.
2128@end table
2129
2130Return the number of generated random bytes.
2131The return value should usually equal to buf_len.
2132@end deftypefn
2133
2134
1552@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2135@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1553 2136
1554@c ------------------------------------------------------------ 2137@c ------------------------------------------------------------
@@ -3346,6 +3929,704 @@ shorter afterwards due to elimination of escape sequences).
3346 3929
3347 3930
3348 3931
3932
3933@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3934
3935@c ------------------------------------------------------------
3936@node microhttpd-websocket
3937@chapter Websocket functions.
3938
3939@noindent
3940Websocket functions provide what you need to use an upgraded connection
3941as a websocket.
3942These functions are only available if you include the header file
3943@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
3944
3945@menu
3946* microhttpd-websocket handshake:: Websocket handshake functions
3947* microhttpd-websocket stream:: Websocket stream functions
3948* microhttpd-websocket decode:: Websocket decode functions
3949* microhttpd-websocket encode:: Websocket encode functions
3950* microhttpd-websocket memory:: Websocket memory functions
3951@end menu
3952
3953@c ------------------------------------------------------------
3954@node microhttpd-websocket handshake
3955@section Websocket handshake functions
3956
3957
3958@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_http_version (const char* http_version)
3959@cindex websocket
3960Checks the HTTP version of the incoming request.
3961Websocket requests are only allowed for HTTP/1.1 or above.
3962
3963@table @var
3964@item http_version
3965The value of the @code{version} parameter of your
3966@code{access_handler} callback.
3967If you pass @code{NULL} then this is handled like a not
3968matching HTTP version.
3969@end table
3970
3971Returns 0 when the HTTP version is
3972valid for a websocket request and
3973a value less than zero when the HTTP version isn't
3974valid for a websocket request.
3975Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
3976@end deftypefun
3977
3978
3979@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_connection_header (const char* connection_header)
3980@cindex websocket
3981Checks the value of the @code{Connection} HTTP request header.
3982Websocket requests require the token @code{Upgrade} in
3983the @code{Connection} HTTP request header.
3984
3985@table @var
3986@item connection_header
3987Value of the @code{Connection} request header.
3988You can get this request header value by passing
3989@code{MHD_HTTP_HEADER_CONNECTION} to
3990@code{MHD_lookup_connection_value()}.
3991If you pass @code{NULL} then this is handled like a not
3992matching @code{Connection} header value.
3993@end table
3994
3995Returns 0 when the @code{Connection} header is
3996valid for a websocket request and
3997a value less than zero when the @code{Connection} header isn't
3998valid for a websocket request.
3999Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4000@end deftypefun
4001
4002
4003@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_upgrade_header (const char* upgrade_header)
4004@cindex websocket
4005Checks the value of the @code{Upgrade} HTTP request header.
4006Websocket requests require the value @code{websocket} in
4007the @code{Upgrade} HTTP request header.
4008
4009@table @var
4010@item upgrade_header
4011Value of the @code{Upgrade} request header.
4012You can get this request header value by passing
4013@code{MHD_HTTP_HEADER_UPGRADE} to
4014@code{MHD_lookup_connection_value()}.
4015If you pass @code{NULL} then this is handled like a not
4016matching @code{Upgrade} header value.
4017@end table
4018
4019Returns 0 when the @code{Upgrade} header is
4020valid for a websocket request and
4021a value less than zero when the @code{Upgrade} header isn't
4022valid for a websocket request.
4023Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4024@end deftypefun
4025
4026
4027@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_version_header (const char* version_header)
4028@cindex websocket
4029Checks the value of the @code{Sec-WebSocket-Version} HTTP request header.
4030Websocket requests require the value @code{13} in
4031the @code{Sec-WebSocket-Version} HTTP request header.
4032
4033@table @var
4034@item version_header
4035Value of the @code{Sec-WebSocket-Version} request header.
4036You can get this request header value by passing
4037@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION} to
4038@code{MHD_lookup_connection_value()}.
4039If you pass @code{NULL} then this is handled like a not
4040matching @code{Sec-WebSocket-Version} header value.
4041@end table
4042
4043Returns 0 when the @code{Sec-WebSocket-Version} header is
4044valid for a websocket request and
4045a value less than zero when the @code{Sec-WebSocket-Version} header isn't
4046valid for a websocket request.
4047Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4048@end deftypefun
4049
4050
4051@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_create_accept_header (const char* sec_websocket_key, char* sec_websocket_accept)
4052@cindex websocket
4053Checks the value of the @code{Sec-WebSocket-Key}
4054HTTP request header and generates the value for
4055the @code{Sec-WebSocket-Accept} HTTP response header.
4056The generated value must be sent to the client.
4057
4058@table @var
4059@item sec_websocket_key
4060Value of the @code{Sec-WebSocket-Key} request header.
4061You can get this request header value by passing
4062@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY} to
4063@code{MHD_lookup_connection_value()}.
4064If you pass @code{NULL} then this is handled like a not
4065matching @code{Sec-WebSocket-Key} header value.
4066
4067@item sec_websocket_accept
4068Response buffer, which will receive
4069the generated value for the @code{Sec-WebSocket-Accept}
4070HTTP response header.
4071This buffer must be at least 29 bytes long and
4072will contain the response value plus a terminating @code{NUL}
4073character on success.
4074Must not be @code{NULL}.
4075You can add this HTTP header to your response by passing
4076@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT} to
4077@code{MHD_add_response_header()}.
4078@end table
4079
4080Returns 0 when the @code{Sec-WebSocket-Key} header was
4081not empty and a result value for the @code{Sec-WebSocket-Accept}
4082was calculated.
4083A value less than zero is returned when the @code{Sec-WebSocket-Key}
4084header isn't valid for a websocket request or when any
4085error occurred.
4086Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4087@end deftypefun
4088
4089
4090@c ------------------------------------------------------------
4091@node microhttpd-websocket stream
4092@section Websocket stream functions
4093
4094@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init (struct MHD_WebSocketStream **ws, int flags, size_t max_payload_size)
4095@cindex websocket
4096Creates a new websocket stream, used for decoding/encoding.
4097
4098@table @var
4099@item ws
4100pointer a variable to fill with the newly created
4101@code{struct MHD_WebSocketStream},
4102receives @code{NULL} on error. May not be @code{NULL}.
4103
4104If not required anymore, free the created websocket stream with
4105@code{MHD_websocket_stream_free()}.
4106
4107@item flags
4108combination of @code{enum MHD_WEBSOCKET_FLAG} values to
4109modify the behavior of the websocket stream.
4110
4111@item max_payload_size
4112maximum size for incoming payload data in bytes. Use 0 to allow each size.
4113@end table
4114
4115Returns 0 on success, negative values on error.
4116Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4117@end deftypefun
4118
4119
4120@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init2 (struct MHD_WebSocketStream **ws, int flags, size_t max_payload_size, MHD_WebSocketMallocCallback callback_malloc, MHD_WebSocketReallocCallback callback_realloc, MHD_WebSocketFreeCallback callback_free, void* cls_rng, MHD_WebSocketRandomNumberGenerator callback_rng)
4121@cindex websocket
4122Creates a new websocket stream, used for decoding/encoding,
4123but with custom memory functions for malloc, realloc and free.
4124Also a random number generator can be specified for client mode.
4125
4126@table @var
4127@item ws
4128pointer a variable to fill with the newly created
4129@code{struct MHD_WebSocketStream},
4130receives @code{NULL} on error. Must not be @code{NULL}.
4131
4132If not required anymore, free the created websocket stream with
4133@code{MHD_websocket_stream_free}.
4134
4135@item flags
4136combination of @code{enum MHD_WEBSOCKET_FLAG} values to
4137modify the behavior of the websocket stream.
4138
4139@item max_payload_size
4140maximum size for incoming payload data in bytes. Use 0 to allow each size.
4141
4142@item callback_malloc
4143callback function for allocating memory. Must not be @code{NULL}.
4144The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{malloc} here.
4145
4146@item callback_realloc
4147callback function for reallocating memory. Must not be @code{NULL}.
4148The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{realloc} here.
4149
4150@item callback_free
4151callback function for freeing memory. Must not be @code{NULL}.
4152The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{free} here.
4153
4154@item cls_rng
4155closure for the random number generator.
4156This is only required when
4157@code{MHD_WEBSOCKET_FLAG_CLIENT} is passed in @code{flags}.
4158The given value is passed to the random number generator callback.
4159May be @code{NULL} if not needed.
4160Should be @code{NULL} when you are not using @code{MHD_WEBSOCKET_FLAG_CLIENT}.
4161The shorter @code{MHD_websocket_stream_init} passes @code{NULL} here.
4162
4163@item callback_rng
4164callback function for a secure random number generator.
4165This is only required when @code{MHD_WEBSOCKET_FLAG_CLIENT} is
4166passed in @code{flags} and must not be @code{NULL} then.
4167Should be @code{NULL} otherwise.
4168The shorter @code{MHD_websocket_stream_init()} passes @code{NULL} here.
4169@end table
4170
4171Returns 0 on success, negative values on error.
4172Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4173@end deftypefun
4174
4175
4176@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_free (struct MHD_WebSocketStream *ws)
4177@cindex websocket
4178Frees a previously allocated websocket stream
4179
4180@table @var
4181@item ws
4182websocket stream to free, this value may be @code{NULL}.
4183@end table
4184
4185Returns 0 on success, negative values on error.
4186Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4187@end deftypefun
4188
4189
4190@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_invalidate (struct MHD_WebSocketStream *ws)
4191@cindex websocket
4192Invalidates a websocket stream.
4193After invalidation a websocket stream cannot be used for decoding anymore.
4194Encoding is still possible.
4195
4196@table @var
4197@item ws
4198websocket stream to invalidate.
4199@end table
4200
4201Returns 0 on success, negative values on error.
4202Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4203@end deftypefun
4204
4205
4206@deftypefun {enum MHD_WEBSOCKET_VALIDITY} MHD_websocket_stream_is_valid (struct MHD_WebSocketStream *ws)
4207@cindex websocket
4208Queries whether a websocket stream is valid.
4209Invalidated websocket streams cannot be used for decoding anymore.
4210Encoding is still possible.
4211
4212@table @var
4213@item ws
4214websocket stream to invalidate.
4215@end table
4216
4217Returns 0 if invalid, 1 if valid for all types or
42182 if valid only for control frames.
4219Can be compared with @code{enum MHD_WEBSOCKET_VALIDITY}.
4220@end deftypefun
4221
4222
4223@c ------------------------------------------------------------
4224@node microhttpd-websocket decode
4225@section Websocket decode functions
4226
4227
4228@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_decode (struct MHD_WebSocketStream* ws, const char* streambuf, size_t streambuf_len, size_t* streambuf_read_len, char** payload, size_t* payload_len)
4229@cindex websocket
4230Decodes a byte sequence for a websocket stream.
4231Decoding is done until either a frame is complete or
4232the end of the byte sequence is reached.
4233
4234@table @var
4235@item ws
4236websocket stream for decoding.
4237
4238@item streambuf
4239byte sequence for decoding.
4240This is what you typically received via @code{recv()}.
4241
4242@item streambuf_len
4243length of the byte sequence in parameter @code{streambuf}.
4244
4245@item streambuf_read_len
4246pointer to a variable, which receives the number of bytes,
4247that has been processed by this call.
4248This value may be less than the value of @code{streambuf_len} when
4249a frame is decoded before the end of the buffer is reached.
4250The remaining bytes of @code{buf} must be passed to
4251the next call of this function.
4252
4253@item payload
4254pointer to a variable, which receives the allocated buffer with the payload
4255data of the decoded frame. Must not be @code{NULL}.
4256If no decoded data is available or an error occurred @code{NULL} is returned.
4257When the returned value is not @code{NULL} then the buffer contains always
4258@code{payload_len} bytes plus one terminating @code{NUL} character
4259(regardless of the frame type).
4260
4261The caller must free this buffer using @code{MHD_websocket_free()}.
4262
4263If you passed the flag @code{MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR}
4264upon creation of the websocket stream and a decoding error occurred
4265(function return value less than 0), then this buffer contains
4266a generated close frame, which must be sent via the socket to the recipient.
4267
4268If you passed the flag @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
4269upon creation of the websocket stream then
4270this payload may only be a part of the complete message.
4271Only complete UTF-8 sequences are returned for fragmented text frames.
4272If necessary the UTF-8 sequence will be completed with the next text fragment.
4273
4274@item payload_len
4275pointer to a variable, which receives length of the result
4276@code{payload} buffer in bytes.
4277Must not be @code{NULL}.
4278This receives 0 when no data is available, when the decoded payload
4279has a length of zero or when an error occurred.
4280@end table
4281
4282Returns a value greater than zero when a frame is complete.
4283Compare with @code{enum MHD_WEBSOCKET_STATUS} to distinguish the frame type.
4284Returns 0 when the call succeeded, but no frame is available.
4285Returns a value less than zero on errors.
4286@end deftypefun
4287
4288
4289@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_split_close_reason (const char* payload, size_t payload_len, unsigned short* reason_code, const char** reason_utf8, size_t* reason_utf8_len)
4290@cindex websocket
4291Splits the payload of a decoded close frame.
4292
4293@table @var
4294@item payload
4295payload of the close frame.
4296This parameter may only be @code{NULL} if @code{payload_len} is 0.
4297
4298@item payload_len
4299length of @code{payload}.
4300
4301@item reason_code
4302pointer to a variable, which receives the numeric close reason.
4303If there was no close reason, this is 0.
4304This value can be compared with @code{enum MHD_WEBSOCKET_CLOSEREASON}.
4305May be @code{NULL}.
4306
4307@item reason_utf8
4308pointer to a variable, which receives the literal close reason.
4309If there was no literal close reason, this will be @code{NULL}.
4310May be @code{NULL}.
4311
4312Please note that no memory is allocated in this function.
4313If not @code{NULL} the returned value of this parameter
4314points to a position in the specified @code{payload}.
4315
4316@item reason_utf8_len
4317pointer to a variable, which receives the length of the literal close reason.
4318If there was no literal close reason, this is 0.
4319May be @code{NULL}.
4320@end table
4321
4322Returns 0 on success or a value less than zero on errors.
4323Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4324@end deftypefun
4325
4326
4327@c ------------------------------------------------------------
4328@node microhttpd-websocket encode
4329@section Websocket encode functions
4330
4331
4332@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_text (struct MHD_WebSocketStream* ws, const char* payload_utf8, size_t payload_utf8_len, int fragmentation, char** frame, size_t* frame_len, int* utf8_step)
4333@cindex websocket
4334Encodes an UTF-8 encoded text into websocket text frame
4335
4336@table @var
4337@item ws
4338websocket stream;
4339
4340@item payload_utf8
4341text to send. This must be UTF-8 encoded.
4342If you don't want UTF-8 then send a binary frame
4343with @code{MHD_websocket_encode_binary()} instead.
4344May be be @code{NULL} if @code{payload_utf8_len} is 0,
4345must not be @code{NULL} otherwise.
4346
4347@item payload_utf8_len
4348length of @code{payload_utf8} in bytes.
4349
4350@item fragmentation
4351A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
4352to specify the fragmentation behavior.
4353Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
4354if you don't want to use fragmentation (default).
4355
4356@item frame
4357pointer to a variable, which receives a buffer with the encoded text frame.
4358Must not be @code{NULL}.
4359The buffer contains what you typically send via @code{send()} to the recipient.
4360If no encoded data is available the variable receives @code{NULL}.
4361
4362If the variable is not @code{NULL} then the buffer contains always
4363@code{frame_len} bytes plus one terminating @code{NUL} character.
4364The caller must free this buffer using @code{MHD_websocket_free()}.
4365
4366@item frame_len
4367pointer to a variable, which receives the length of the encoded frame in bytes.
4368Must not be @code{NULL}.
4369
4370@item utf8_step
4371If fragmentation is used (the parameter @code{fragmentation} is not 0)
4372then is parameter is required and must not be @code{NULL}.
4373If no fragmentation is used, this parameter is optional and
4374should be @code{NULL}.
4375
4376This parameter is a pointer to a variable which contains the last check status
4377of the UTF-8 sequence. It is required to continue a previous
4378UTF-8 sequence check when fragmentation is used, because a UTF-8 sequence
4379could be splitted upon fragments.
4380
4381@code{enum MHD_WEBSOCKET_UTF8STEP} is used for this value.
4382If you start a new fragment using
4383@code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or
4384@code{MHD_WEBSOCKET_FRAGMENTATION_FIRST} the old value of this variable
4385will be discarded and the value of this variable will be initialized
4386to @code{MHD_WEBSOCKET_UTF8STEP_NORMAL}.
4387On all other fragmentation modes the previous value of the pointed variable
4388will be used to continue the UTF-8 sequence check.
4389@end table
4390
4391Returns 0 on success or a value less than zero on errors.
4392Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4393@end deftypefun
4394
4395
4396@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_binary (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, int fragmentation, char** frame, size_t* frame_len)
4397@cindex websocket
4398Encodes binary data into websocket binary frame
4399
4400@table @var
4401@item ws
4402websocket stream;
4403
4404@item payload
4405binary data to send.
4406May be be @code{NULL} if @code{payload_len} is 0,
4407must not be @code{NULL} otherwise.
4408
4409@item payload_len
4410length of @code{payload} in bytes.
4411
4412@item fragmentation
4413A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
4414to specify the fragmentation behavior.
4415Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
4416if you don't want to use fragmentation (default).
4417
4418@item frame
4419pointer to a variable, which receives a buffer with the encoded binary frame.
4420Must not be @code{NULL}.
4421The buffer contains what you typically send via @code{send()} to the recipient.
4422If no encoded data is available the variable receives @code{NULL}.
4423
4424If the variable is not @code{NULL} then the buffer contains always
4425@code{frame_len} bytes plus one terminating @code{NUL} character.
4426The caller must free this buffer using @code{MHD_websocket_free()}.
4427
4428@item frame_len
4429pointer to a variable, which receives the length of the encoded frame in bytes.
4430Must not be @code{NULL}.
4431@end table
4432
4433Returns 0 on success or a value less than zero on errors.
4434Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4435@end deftypefun
4436
4437
4438@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_ping (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
4439@cindex websocket
4440Encodes a websocket ping frame.
4441Ping frames are used to check whether a recipient is still available
4442and what latency the websocket connection has.
4443
4444@table @var
4445@item ws
4446websocket stream;
4447
4448@item payload
4449binary ping data to send.
4450May be @code{NULL} if @code{payload_len} is 0.
4451
4452@item payload_len
4453length of @code{payload} in bytes.
4454This may not exceed 125 bytes.
4455
4456@item frame
4457pointer to a variable, which receives a buffer with the encoded ping frame.
4458Must not be @code{NULL}.
4459The buffer contains what you typically send via @code{send()} to the recipient.
4460If no encoded data is available the variable receives @code{NULL}.
4461
4462If the variable is not @code{NULL} then the buffer contains always
4463@code{frame_len} bytes plus one terminating @code{NUL} character.
4464The caller must free this buffer using @code{MHD_websocket_free()}.
4465
4466@item frame_len
4467pointer to a variable, which receives the length of the encoded frame in bytes.
4468Must not be @code{NULL}.
4469@end table
4470
4471Returns 0 on success or a value less than zero on errors.
4472Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4473@end deftypefun
4474
4475
4476@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_pong (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
4477@cindex websocket
4478Encodes a websocket pong frame.
4479Pong frames are used to answer a previously received websocket ping frame.
4480
4481@table @var
4482@item ws
4483websocket stream;
4484
4485@item payload
4486binary pong data to send, which should be
4487the decoded payload from the received ping frame.
4488May be @code{NULL} if @code{payload_len} is 0.
4489
4490@item payload_len
4491length of @code{payload} in bytes.
4492This may not exceed 125 bytes.
4493
4494@item frame
4495pointer to a variable, which receives a buffer with the encoded pong frame.
4496Must not be @code{NULL}.
4497The buffer contains what you typically send via @code{send()} to the recipient.
4498If no encoded data is available the variable receives @code{NULL}.
4499
4500If the variable is not @code{NULL} then the buffer contains always
4501@code{frame_len} bytes plus one terminating @code{NUL} character.
4502The caller must free this buffer using @code{MHD_websocket_free()}.
4503
4504@item frame_len
4505pointer to a variable, which receives the length of the encoded frame in bytes.
4506Must not be @code{NULL}.
4507@end table
4508
4509Returns 0 on success or a value less than zero on errors.
4510Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4511@end deftypefun
4512
4513
4514@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_close (struct MHD_WebSocketStream* ws, unsigned short reason_code, const char* reason_utf8, size_t reason_utf8_len, char** frame, size_t* frame_len)
4515@cindex websocket
4516Encodes a websocket close frame.
4517Close frames are used to close a websocket connection in a formal way.
4518
4519@table @var
4520@item ws
4521websocket stream;
4522
4523@item reason_code
4524reason for close.
4525You can use @code{enum MHD_WEBSOCKET_CLOSEREASON} for typical reasons,
4526but you are not limited to these values.
4527The allowed values are specified in RFC 6455 7.4.
4528If you don't want to enter a reason, you can specify
4529@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON} (or just 0) then
4530no reason is encoded.
4531
4532@item reason_utf8
4533An UTF-8 encoded text reason why the connection is closed.
4534This may be @code{NULL} if @code{reason_utf8_len} is 0.
4535This must be @code{NULL} if @code{reason_code} equals to zero
4536(@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON}).
4537
4538@item reason_utf8_len
4539length of the UTF-8 encoded text reason in bytes.
4540This may not exceed 123 bytes.
4541
4542@item frame
4543pointer to a variable, which receives a buffer with the encoded close frame.
4544Must not be @code{NULL}.
4545The buffer contains what you typically send via @code{send()} to the recipient.
4546If no encoded data is available the variable receives @code{NULL}.
4547
4548If the variable is not @code{NULL} then the buffer contains always
4549@code{frame_len} bytes plus one terminating @code{NUL} character.
4550The caller must free this buffer using @code{MHD_websocket_free()}.
4551
4552@item frame_len
4553pointer to a variable, which receives the length of the encoded frame in bytes.
4554Must not be @code{NULL}.
4555@end table
4556
4557Returns 0 on success or a value less than zero on errors.
4558Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
4559@end deftypefun
4560
4561
4562@c ------------------------------------------------------------
4563@node microhttpd-websocket memory
4564@section Websocket memory functions
4565
4566
4567@deftypefun {void*} MHD_websocket_malloc (struct MHD_WebSocketStream* ws, size_t buf_len)
4568@cindex websocket
4569Allocates memory with the associated @code{malloc()} function
4570of the websocket stream.
4571The memory allocation function could be different for a websocket stream if
4572@code{MHD_websocket_stream_init2()} has been used for initialization.
4573
4574@table @var
4575@item ws
4576websocket stream;
4577
4578@item buf_len
4579size of the buffer to allocate in bytes.
4580@end table
4581
4582Returns the pointer of the allocated buffer or @code{NULL} on failure.
4583@end deftypefun
4584
4585
4586@deftypefun {void*} MHD_websocket_realloc (struct MHD_WebSocketStream* ws, void* buf, size_t new_buf_len)
4587@cindex websocket
4588Reallocates memory with the associated @code{realloc()} function
4589of the websocket stream.
4590The memory reallocation function could be different for a websocket stream if
4591@code{MHD_websocket_stream_init2()} has been used for initialization.
4592
4593@table @var
4594@item ws
4595websocket stream;
4596
4597@item buf
4598current buffer, may be @code{NULL};
4599
4600@item new_buf_len
4601new size of the buffer in bytes.
4602@end table
4603
4604Return the pointer of the reallocated buffer or @code{NULL} on failure.
4605On failure the old pointer remains valid.
4606@end deftypefun
4607
4608
4609@deftypefun {void} MHD_websocket_free (struct MHD_WebSocketStream* ws, void* buf)
4610@cindex websocket
4611Frees memory with the associated @code{free()} function
4612of the websocket stream.
4613The memory free function could be different for a websocket stream if
4614@code{MHD_websocket_stream_init2()} has been used for initialization.
4615
4616@table @var
4617@item ws
4618websocket stream;
4619
4620@item buf
4621buffer to free, this may be @code{NULL} then nothing happens.
4622@end table
4623
4624@end deftypefun
4625
4626
4627
4628
4629
3349@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4630@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3350 4631
3351 4632
diff --git a/src/examples/websocket_chatserver_example.c b/src/examples/websocket_chatserver_example.c
index 701684cc..0893279b 100644
--- a/src/examples/websocket_chatserver_example.c
+++ b/src/examples/websocket_chatserver_example.c
@@ -43,6 +43,12 @@
43 See: https://github.com/coapp-packages/pthreads/issues/2 43 See: https://github.com/coapp-packages/pthreads/issues/2
44*/ 44*/
45#include "pthread_windows.h" 45#include "pthread_windows.h"
46
47/*
48 On Windows we will use stricmp instead of strcasecmp (strcasecmp is undefined there).
49*/
50#define strcasecmp stricmp
51
46#else 52#else
47/* 53/*
48 On Unix systems we can use pthread. 54 On Unix systems we can use pthread.
@@ -180,429 +186,429 @@
180 " function window_onload(event)\n" \ 186 " function window_onload(event)\n" \
181 " {\n" \ 187 " {\n" \
182 " // Determine the base url (for http:// this is ws:// for https:// this must be wss://)\n" \ 188 " // Determine the base url (for http:// this is ws:// for https:// this must be wss://)\n" \
183 // " baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/ChatServerWebSocket';\n" \ 189 " baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/ChatServerWebSocket';\n" \
184 // " chat_generate();\n" \ 190 " chat_generate();\n" \
185 // " chat_connect();\n" \ 191 " chat_connect();\n" \
186 // " }\n" \ 192 " }\n" \
187 // "\n" \ 193 "\n" \
188 // " /**\n" \ 194 " /**\n" \
189 // " This function generates the chat using DOM\n" \ 195 " This function generates the chat using DOM\n" \
190 // " */\n" \ 196 " */\n" \
191 // " function chat_generate()\n" \ 197 " function chat_generate()\n" \
192 // " {\n" \ 198 " {\n" \
193 // " document.body.innerHTML = '';\n" \ 199 " document.body.innerHTML = '';\n" \
194 // " let chat = document.createElement('div');\n" \ 200 " let chat = document.createElement('div');\n" \
195 // " document.body.appendChild(chat);\n" \ 201 " document.body.appendChild(chat);\n" \
196 // " chat.id = 'Chat';\n" \ 202 " chat.id = 'Chat';\n" \
197 // " let messagesAndInput = document.createElement('div');\n" \ 203 " let messagesAndInput = document.createElement('div');\n" \
198 // " chat.appendChild(messagesAndInput);\n" \ 204 " chat.appendChild(messagesAndInput);\n" \
199 // " messagesAndInput.classList.add('MessagesAndInput');\n" \ 205 " messagesAndInput.classList.add('MessagesAndInput');\n" \
200 // " let messages = document.createElement('div');\n" \ 206 " let messages = document.createElement('div');\n" \
201 // " messagesAndInput.appendChild(messages);\n" \ 207 " messagesAndInput.appendChild(messages);\n" \
202 // " messages.id = 'Messages';\n" \ 208 " messages.id = 'Messages';\n" \
203 // " let input = document.createElement('div');\n" \ 209 " let input = document.createElement('div');\n" \
204 // " messagesAndInput.appendChild(input);\n" \ 210 " messagesAndInput.appendChild(input);\n" \
205 // " input.classList.add('Input');\n" \ 211 " input.classList.add('Input');\n" \
206 // " let inputMessage = document.createElement('input');\n" \ 212 " let inputMessage = document.createElement('input');\n" \
207 // " input.appendChild(inputMessage);\n" \ 213 " input.appendChild(inputMessage);\n" \
208 // " inputMessage.type = 'text';\n" \ 214 " inputMessage.type = 'text';\n" \
209 // " inputMessage.id = 'InputMessage';\n" \ 215 " inputMessage.id = 'InputMessage';\n" \
210 // " inputMessage.disabled = true;\n" \ 216 " inputMessage.disabled = true;\n" \
211 // " inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \ 217 " inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
212 // " let inputMessageSend = document.createElement('button');\n" \ 218 " let inputMessageSend = document.createElement('button');\n" \
213 // " input.appendChild(inputMessageSend);\n" \ 219 " input.appendChild(inputMessageSend);\n" \
214 // " inputMessageSend.id = 'InputMessageButton';\n" \ 220 " inputMessageSend.id = 'InputMessageButton';\n" \
215 // " inputMessageSend.disabled = true;\n" \ 221 " inputMessageSend.disabled = true;\n" \
216 // " inputMessageSend.innerText = 'send';\n" \ 222 " inputMessageSend.innerText = 'send';\n" \
217 // " inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \ 223 " inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \
218 // " let inputImage = document.createElement('input');\n" \ 224 " let inputImage = document.createElement('input');\n" \
219 // " input.appendChild(inputImage);\n" \ 225 " input.appendChild(inputImage);\n" \
220 // " inputImage.id = 'InputImage';\n" \ 226 " inputImage.id = 'InputImage';\n" \
221 // " inputImage.type = 'file';\n" \ 227 " inputImage.type = 'file';\n" \
222 // " inputImage.accept = 'image/*';\n" \ 228 " inputImage.accept = 'image/*';\n" \
223 // " inputImage.style.display = 'none';\n" \ 229 " inputImage.style.display = 'none';\n" \
224 // " inputImage.addEventListener('change', chat_onImageSelected);\n" \ 230 " inputImage.addEventListener('change', chat_onImageSelected);\n" \
225 // " let inputImageButton = document.createElement('button');\n" \ 231 " let inputImageButton = document.createElement('button');\n" \
226 // " input.appendChild(inputImageButton);\n" \ 232 " input.appendChild(inputImageButton);\n" \
227 // " inputImageButton.id = 'InputImageButton';\n" \ 233 " inputImageButton.id = 'InputImageButton';\n" \
228 // " inputImageButton.disabled = true;\n" \ 234 " inputImageButton.disabled = true;\n" \
229 // " inputImageButton.innerText = 'image';\n" \ 235 " inputImageButton.innerText = 'image';\n" \
230 // " inputImageButton.addEventListener('click', chat_onImageClicked);\n" \ 236 " inputImageButton.addEventListener('click', chat_onImageClicked);\n" \
231 // " let users = document.createElement('div');\n" \ 237 " let users = document.createElement('div');\n" \
232 // " chat.appendChild(users);\n" \ 238 " chat.appendChild(users);\n" \
233 // " users.id = 'Users';\n" \ 239 " users.id = 'Users';\n" \
234 // " users.addEventListener('click', chat_onUserClicked);\n" \ 240 " users.addEventListener('click', chat_onUserClicked);\n" \
235 // " let allUsers = document.createElement('div');\n" \ 241 " let allUsers = document.createElement('div');\n" \
236 // " users.appendChild(allUsers);\n" \ 242 " users.appendChild(allUsers);\n" \
237 // " allUsers.classList.add('selected');\n" \ 243 " allUsers.classList.add('selected');\n" \
238 // " allUsers.innerText = '<everyone>';\n" \ 244 " allUsers.innerText = '<everyone>';\n" \
239 // " allUsers.setAttribute('data-user', '0');\n" \ 245 " allUsers.setAttribute('data-user', '0');\n" \
240 // " }\n" \ 246 " }\n" \
241 // "\n" \ 247 "\n" \
242 // " /**\n" \ 248 " /**\n" \
243 // " This function creates and connects a WebSocket\n" \ 249 " This function creates and connects a WebSocket\n" \
244 // " */\n" \ 250 " */\n" \
245 // " function chat_connect()\n" \ 251 " function chat_connect()\n" \
246 // " {\n" \ 252 " {\n" \
247 // " chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \ 253 " chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \
248 // " socket = new WebSocket(baseUrl);\n" \ 254 " socket = new WebSocket(baseUrl);\n" \
249 // " socket.binaryType = 'arraybuffer';\n" \ 255 " socket.binaryType = 'arraybuffer';\n" \
250 // " socket.onopen = socket_onopen;\n" \ 256 " socket.onopen = socket_onopen;\n" \
251 // " socket.onclose = socket_onclose;\n" \ 257 " socket.onclose = socket_onclose;\n" \
252 // " socket.onerror = socket_onerror;\n" \ 258 " socket.onerror = socket_onerror;\n" \
253 // " socket.onmessage = socket_onmessage;\n" \ 259 " socket.onmessage = socket_onmessage;\n" \
254 // " }\n" \ 260 " }\n" \
255 // "\n" \ 261 "\n" \
256 // " /**\n" \ 262 " /**\n" \
257 // " This function adds new text to the chat list\n" \ 263 " This function adds new text to the chat list\n" \
258 // " */\n" \ 264 " */\n" \
259 // " function chat_addMessage(text, options)\n" \ 265 " function chat_addMessage(text, options)\n" \
260 // " {\n" \ 266 " {\n" \
261 // " let type = options && options.type || 'regular';\n" \ 267 " let type = options && options.type || 'regular';\n" \
262 // " if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \ 268 " if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
263 // " type = 'regular';\n" \ 269 " type = 'regular';\n" \
264 // " let message = document.createElement('div');\n" \ 270 " let message = document.createElement('div');\n" \
265 // " message.classList.add('Message');\n" \ 271 " message.classList.add('Message');\n" \
266 // " message.classList.add(type);\n" \ 272 " message.classList.add(type);\n" \
267 // " if(typeof(text) === 'string')\n" \ 273 " if(typeof(text) === 'string')\n" \
268 // " {\n" \ 274 " {\n" \
269 // " let content = document.createElement('span');\n" \ 275 " let content = document.createElement('span');\n" \
270 // " message.appendChild(content);\n" \ 276 " message.appendChild(content);\n" \
271 // " if(options && options.from)\n" \ 277 " if(options && options.from)\n" \
272 // " content.innerText = `${options.from}: ${text}`;\n" \ 278 " content.innerText = `${options.from}: ${text}`;\n" \
273 // " else\n" \ 279 " else\n" \
274 // " content.innerText = text;\n" \ 280 " content.innerText = text;\n" \
275 // " if(options && options.reconnect)\n" \ 281 " if(options && options.reconnect)\n" \
276 // " {\n" \ 282 " {\n" \
277 // " let span = document.createElement('span');\n" \ 283 " let span = document.createElement('span');\n" \
278 // " span.appendChild(document.createTextNode(' ('));\n" \ 284 " span.appendChild(document.createTextNode(' ('));\n" \
279 // " let reconnect = document.createElement('a');\n" \ 285 " let reconnect = document.createElement('a');\n" \
280 // " reconnect.href = 'javascript:chat_connect()';\n" \ 286 " reconnect.href = 'javascript:chat_connect()';\n" \
281 // " reconnect.innerText = 'reconnect';\n" \ 287 " reconnect.innerText = 'reconnect';\n" \
282 // " span.appendChild(reconnect);\n" \ 288 " span.appendChild(reconnect);\n" \
283 // " span.appendChild(document.createTextNode(')'));\n" \ 289 " span.appendChild(document.createTextNode(')'));\n" \
284 // " message.appendChild(span);\n" \ 290 " message.appendChild(span);\n" \
285 // " }\n" \ 291 " }\n" \
286 // " }\n" \ 292 " }\n" \
287 // " else\n" \ 293 " else\n" \
288 // " {\n" \ 294 " {\n" \
289 // " let content = document.createElement('span');\n" \ 295 " let content = document.createElement('span');\n" \
290 // " message.appendChild(content);\n" \ 296 " message.appendChild(content);\n" \
291 // " if(options && options.from)\n" \ 297 " if(options && options.from)\n" \
292 // " {\n" \ 298 " {\n" \
293 // " content.innerText = `${options.from}:\\n`;\n" \ 299 " content.innerText = `${options.from}:\\n`;\n" \
294 // " }\n" \ 300 " }\n" \
295 // " if(options && options.pictureType && text instanceof Uint8Array)\n" \ 301 " if(options && options.pictureType && text instanceof Uint8Array)\n" \
296 // " {\n" \ 302 " {\n" \
297 // " let img = document.createElement('img');\n" \ 303 " let img = document.createElement('img');\n" \
298 // " content.appendChild(img);\n" \ 304 " content.appendChild(img);\n" \
299 // " img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \ 305 " img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \
300 // " }\n" \ 306 " }\n" \
301 // " }\n" \ 307 " }\n" \
302 // " document.getElementById('Messages').appendChild(message);\n" \ 308 " document.getElementById('Messages').appendChild(message);\n" \
303 // " message.scrollIntoView();\n" \ 309 " message.scrollIntoView();\n" \
304 // " }\n" \ 310 " }\n" \
305 // "\n" \ 311 "\n" \
306 // " /**\n" \ 312 " /**\n" \
307 // " This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \ 313 " This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \
308 // " */\n" \ 314 " */\n" \
309 // " function chat_onKeyDown(event)\n" \ 315 " function chat_onKeyDown(event)\n" \
310 // " {\n" \ 316 " {\n" \
311 // " if(event.key == 'Enter')\n" \ 317 " if(event.key == 'Enter')\n" \
312 // " chat_onSendClicked();\n" \ 318 " chat_onSendClicked();\n" \
313 // " }\n" \ 319 " }\n" \
314 // "\n" \ 320 "\n" \
315 // " /**\n" \ 321 " /**\n" \
316 // " This is the code to send a message or command, when clicking the 'send' button\n" \ 322 " This is the code to send a message or command, when clicking the 'send' button\n" \
317 // " */\n" \ 323 " */\n" \
318 // " function chat_onSendClicked(event)\n" \ 324 " function chat_onSendClicked(event)\n" \
319 // " {\n" \ 325 " {\n" \
320 // " let message = document.getElementById('InputMessage').value;\n" \ 326 " let message = document.getElementById('InputMessage').value;\n" \
321 // " if(message.length == 0)\n" \ 327 " if(message.length == 0)\n" \
322 // " return;\n" \ 328 " return;\n" \
323 // " if(message.substr(0, 1) == '/')\n" \ 329 " if(message.substr(0, 1) == '/')\n" \
324 // " {\n" \ 330 " {\n" \
325 // " // command\n" \ 331 " // command\n" \
326 // " let match;\n" \ 332 " let match;\n" \
327 // " if(/^\\/disconnect\\s*$/.test(message))\n" \ 333 " if(/^\\/disconnect\\s*$/.test(message))\n" \
328 // " {\n" \ 334 " {\n" \
329 // " socket.close(1000);\n" \ 335 " socket.close(1000);\n" \
330 // " }\n" \ 336 " }\n" \
331 // " else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \ 337 " else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
332 // " {\n" \ 338 " {\n" \
333 // " message = message.substr(match[0].length);\n" \ 339 " message = message.substr(match[0].length);\n" \
334 // " let userId = chat_getUserIdByName(match[1]);\n" \ 340 " let userId = chat_getUserIdByName(match[1]);\n" \
335 // " if(userId !== null)\n" \ 341 " if(userId !== null)\n" \
336 // " {\n" \ 342 " {\n" \
337 // " socket.send(`private|${userId}|${message}`);\n" \ 343 " socket.send(`private|${userId}|${message}`);\n" \
338 // " }\n" \ 344 " }\n" \
339 // " else\n" \ 345 " else\n" \
340 // " {\n" \ 346 " {\n" \
341 // " chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \ 347 " chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \
342 // " }\n" \ 348 " }\n" \
343 // " }\n" \ 349 " }\n" \
344 // " else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \ 350 " else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
345 // " {\n" \ 351 " {\n" \
346 // " let userId = chat_getUserIdByName(match[1]);\n" \ 352 " let userId = chat_getUserIdByName(match[1]);\n" \
347 // " if(userId !== null)\n" \ 353 " if(userId !== null)\n" \
348 // " {\n" \ 354 " {\n" \
349 // " socket.send(`ping|${userId}|`);\n" \ 355 " socket.send(`ping|${userId}|`);\n" \
350 // " }\n" \ 356 " }\n" \
351 // " else\n" \ 357 " else\n" \
352 // " {\n" \ 358 " {\n" \
353 // " chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \ 359 " chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \
354 // " }\n" \ 360 " }\n" \
355 // " }\n" \ 361 " }\n" \
356 // " else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \ 362 " else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
357 // " {\n" \ 363 " {\n" \
358 // " socket.send(`name||${match[1]}`);\n" \ 364 " socket.send(`name||${match[1]}`);\n" \
359 // " }\n" \ 365 " }\n" \
360 // " else\n" \ 366 " else\n" \
361 // " {\n" \ 367 " {\n" \
362 // " chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \ 368 " chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \
363 // " }\n" \ 369 " }\n" \
364 // " }\n" \ 370 " }\n" \
365 // " else\n" \ 371 " else\n" \
366 // " {\n" \ 372 " {\n" \
367 // " // regular chat message to the selected user\n" \ 373 " // regular chat message to the selected user\n" \
368 // " let selectedUser = document.querySelector('div#Users > div.selected');\n" \ 374 " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
369 // " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \ 375 " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
370 // " if(selectedUserId == 0)\n" \ 376 " if(selectedUserId == 0)\n" \
371 // " socket.send(`||${message}`);\n" \ 377 " socket.send(`||${message}`);\n" \
372 // " else\n" \ 378 " else\n" \
373 // " socket.send(`private|${selectedUserId}|${message}`);\n" \ 379 " socket.send(`private|${selectedUserId}|${message}`);\n" \
374 // " }\n" \ 380 " }\n" \
375 // " document.getElementById('InputMessage').value = '';\n" \ 381 " document.getElementById('InputMessage').value = '';\n" \
376 // " }\n" \ 382 " }\n" \
377 // "\n" \ 383 "\n" \
378 // " /**\n" \ 384 " /**\n" \
379 // " This is the event when the user hits the 'image' button\n" \ 385 " This is the event when the user hits the 'image' button\n" \
380 // " */\n" \ 386 " */\n" \
381 // " function chat_onImageClicked(event)\n" \ 387 " function chat_onImageClicked(event)\n" \
382 // " {\n" \ 388 " {\n" \
383 // " document.getElementById('InputImage').click();\n" \ 389 " document.getElementById('InputImage').click();\n" \
384 // " }\n" \ 390 " }\n" \
385 // "\n" \ 391 "\n" \
386 // " /**\n" \ 392 " /**\n" \
387 // " This is the event when the user selected an image.\n" \ 393 " This is the event when the user selected an image.\n" \
388 // " The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \ 394 " The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \
389 // " */\n" \ 395 " */\n" \
390 // " function chat_onImageSelected(event)\n" \ 396 " function chat_onImageSelected(event)\n" \
391 // " {\n" \ 397 " {\n" \
392 // " let file = event.target.files[0];\n" \ 398 " let file = event.target.files[0];\n" \
393 // " if(!file || !/^image\\//.test(file.type))\n" \ 399 " if(!file || !/^image\\//.test(file.type))\n" \
394 // " return;\n" \ 400 " return;\n" \
395 // " let selectedUser = document.querySelector('div#Users > div.selected');\n" \ 401 " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
396 // " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \ 402 " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
397 // " let reader = new FileReader();\n" \ 403 " let reader = new FileReader();\n" \
398 // " reader.onload = function(event) {\n" \ 404 " reader.onload = function(event) {\n" \
399 // " chat_onImageRead(event, file.type, selectedUserId);\n" \ 405 " chat_onImageRead(event, file.type, selectedUserId);\n" \
400 // " };\n" \ 406 " };\n" \
401 // " reader.readAsArrayBuffer(file);\n" \ 407 " reader.readAsArrayBuffer(file);\n" \
402 // " }\n" \ 408 " }\n" \
403 // "\n" \ 409 "\n" \
404 // " /**\n" \ 410 " /**\n" \
405 // " This is the event when the user selected image has been read.\n" \ 411 " This is the event when the user selected image has been read.\n" \
406 // " This will add our chat protocol prefix and send it via the websocket.\n" \ 412 " This will add our chat protocol prefix and send it via the websocket.\n" \
407 // " */\n" \ 413 " */\n" \
408 // " function chat_onImageRead(event, fileType, selectedUserId)\n" \ 414 " function chat_onImageRead(event, fileType, selectedUserId)\n" \
409 // " {\n" \ 415 " {\n" \
410 // " let encoder = new TextEncoder();\n" \ 416 " let encoder = new TextEncoder();\n" \
411 // " let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \ 417 " let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \
412 // " prefix = encoder.encode(prefix);\n" \ 418 " prefix = encoder.encode(prefix);\n" \
413 // " let byteData = new Uint8Array(event.target.result);\n" \ 419 " let byteData = new Uint8Array(event.target.result);\n" \
414 // " let totalLength = prefix.length + byteData.length;\n" \ 420 " let totalLength = prefix.length + byteData.length;\n" \
415 // " let resultByteData = new Uint8Array(totalLength);\n" \ 421 " let resultByteData = new Uint8Array(totalLength);\n" \
416 // " resultByteData.set(prefix, 0);\n" \ 422 " resultByteData.set(prefix, 0);\n" \
417 // " resultByteData.set(byteData, prefix.length);\n" \ 423 " resultByteData.set(byteData, prefix.length);\n" \
418 // " socket.send(resultByteData);\n" \ 424 " socket.send(resultByteData);\n" \
419 // " }\n" \ 425 " }\n" \
420 // "\n" \ 426 "\n" \
421 // " /**\n" \ 427 " /**\n" \
422 // " This is the event when the user clicked a name in the user list.\n" \ 428 " This is the event when the user clicked a name in the user list.\n" \
423 // " This is useful to send private messages or images without needing to add the /m prefix.\n" \ 429 " This is useful to send private messages or images without needing to add the /m prefix.\n" \
424 // " */\n" \ 430 " */\n" \
425 // " function chat_onUserClicked(event, selectedUserId)\n" \ 431 " function chat_onUserClicked(event, selectedUserId)\n" \
426 // " {\n" \ 432 " {\n" \
427 // " let newSelected = event.target.closest('div#Users > div');\n" \ 433 " let newSelected = event.target.closest('div#Users > div');\n" \
428 // " if(newSelected === null)\n" \ 434 " if(newSelected === null)\n" \
429 // " return;\n" \ 435 " return;\n" \
430 // " for(let div of this.querySelectorAll(':scope > div.selected'))\n" \ 436 " for(let div of this.querySelectorAll(':scope > div.selected'))\n" \
431 // " div.classList.remove('selected');\n" \ 437 " div.classList.remove('selected');\n" \
432 // " newSelected.classList.add('selected');\n" \ 438 " newSelected.classList.add('selected');\n" \
433 // " }\n" \ 439 " }\n" \
434 // "\n" \ 440 "\n" \
435 // " /**\n" \ 441 " /**\n" \
436 // " This functions returns the current id of a user identified by its name.\n" \ 442 " This functions returns the current id of a user identified by its name.\n" \
437 // " */\n" \ 443 " */\n" \
438 // " function chat_getUserIdByName(name)\n" \ 444 " function chat_getUserIdByName(name)\n" \
439 // " {\n" \ 445 " {\n" \
440 // " let nameUpper = name.toUpperCase();\n" \ 446 " let nameUpper = name.toUpperCase();\n" \
441 // " for(let pair of connectedUsers)\n" \ 447 " for(let pair of connectedUsers)\n" \
442 // " {\n" \ 448 " {\n" \
443 // " if(pair[1].toUpperCase() == nameUpper)\n" \ 449 " if(pair[1].toUpperCase() == nameUpper)\n" \
444 // " return pair[0];\n" \ 450 " return pair[0];\n" \
445 // " }\n" \ 451 " }\n" \
446 // " return null;\n" \ 452 " return null;\n" \
447 // " }\n" \ 453 " }\n" \
448 // "\n" \ 454 "\n" \
449 // " /**\n" \ 455 " /**\n" \
450 // " This functions clears the entire user list (needed for reconnecting).\n" \ 456 " This functions clears the entire user list (needed for reconnecting).\n" \
451 // " */\n" \ 457 " */\n" \
452 // " function chat_clearUserList()\n" \ 458 " function chat_clearUserList()\n" \
453 // " {\n" \ 459 " {\n" \
454 // " let users = document.getElementById('Users');\n" \ 460 " let users = document.getElementById('Users');\n" \
455 // " for(let div of users.querySelectorAll(':scope > div'))\n" \ 461 " for(let div of users.querySelectorAll(':scope > div'))\n" \
456 // " {\n" \ 462 " {\n" \
457 // " if(div.getAttribute('data-user') === '0')\n" \ 463 " if(div.getAttribute('data-user') === '0')\n" \
458 // " {\n" \ 464 " {\n" \
459 // " div.classList.add('selected');\n" \ 465 " div.classList.add('selected');\n" \
460 // " }\n" \ 466 " }\n" \
461 // " else\n" \ 467 " else\n" \
462 // " {\n" \ 468 " {\n" \
463 // " div.parentNode.removeChild(div);\n" \ 469 " div.parentNode.removeChild(div);\n" \
464 // " }\n" \ 470 " }\n" \
465 // " }\n" \ 471 " }\n" \
466 // " return null;\n" \ 472 " return null;\n" \
467 // " }\n" \ 473 " }\n" \
468 // "\n" \ 474 "\n" \
469 // " /**\n" \ 475 " /**\n" \
470 // " This is the event when the socket has established a connection.\n" \ 476 " This is the event when the socket has established a connection.\n" \
471 // " This will initialize an empty chat and enable the controls.\n" \ 477 " This will initialize an empty chat and enable the controls.\n" \
472 // " */\n" \ 478 " */\n" \
473 // " function socket_onopen(event)\n" \ 479 " function socket_onopen(event)\n" \
474 // " {\n" \ 480 " {\n" \
475 // " connectedUsers.clear();\n" \ 481 " connectedUsers.clear();\n" \
476 // " chat_clearUserList();\n" \ 482 " chat_clearUserList();\n" \
477 // " chat_addMessage('Connected!', { type: 'system' });\n" \ 483 " chat_addMessage('Connected!', { type: 'system' });\n" \
478 // " document.getElementById('InputMessage').disabled = false;\n" \ 484 " document.getElementById('InputMessage').disabled = false;\n" \
479 // " document.getElementById('InputMessageButton').disabled = false;\n" \ 485 " document.getElementById('InputMessageButton').disabled = false;\n" \
480 // " document.getElementById('InputImageButton').disabled = false;\n" \ 486 " document.getElementById('InputImageButton').disabled = false;\n" \
481 // " }\n" \ 487 " }\n" \
482 // "\n" \ 488 "\n" \
483 // " /**\n" \ 489 " /**\n" \
484 // " This is the event when the socket has been closed.\n" \ 490 " This is the event when the socket has been closed.\n" \
485 // " This will lock the controls.\n" \ 491 " This will lock the controls.\n" \
486 // " */\n" \ 492 " */\n" \
487 // " function socket_onclose(event)\n" \ 493 " function socket_onclose(event)\n" \
488 // " {\n" \ 494 " {\n" \
489 // " chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \ 495 " chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \
490 // " document.getElementById('InputMessage').disabled = true;\n" \ 496 " document.getElementById('InputMessage').disabled = true;\n" \
491 // " document.getElementById('InputMessageButton').disabled = true;\n" \ 497 " document.getElementById('InputMessageButton').disabled = true;\n" \
492 // " document.getElementById('InputImageButton').disabled = true;\n" \ 498 " document.getElementById('InputImageButton').disabled = true;\n" \
493 // " }\n" \ 499 " }\n" \
494 // "\n" \ 500 "\n" \
495 // " /**\n" \ 501 " /**\n" \
496 // " This is the event when the socket reported an error.\n" \ 502 " This is the event when the socket reported an error.\n" \
497 // " This will just make an output.\n" \ 503 " This will just make an output.\n" \
498 // " In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \ 504 " In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \
499 // " */\n" \ 505 " */\n" \
500 // " function socket_onerror(event)\n" \ 506 " function socket_onerror(event)\n" \
501 // " {\n" \ 507 " {\n" \
502 // " console.error('WebSocket error reported: ', event);\n" \ 508 " console.error('WebSocket error reported: ', event);\n" \
503 // " chat_addMessage('The socket reported an error!', { type: 'error' });\n" \ 509 " chat_addMessage('The socket reported an error!', { type: 'error' });\n" \
504 // " }\n" \ 510 " }\n" \
505 // "\n" \ 511 "\n" \
506 // " /**\n" \ 512 " /**\n" \
507 // " This is the event when the socket has received a message.\n" \ 513 " This is the event when the socket has received a message.\n" \
508 // " This will parse the message and execute the corresponding command (or add the message).\n" \ 514 " This will parse the message and execute the corresponding command (or add the message).\n" \
509 // " */\n" \ 515 " */\n" \
510 // " function socket_onmessage(event)\n" \ 516 " function socket_onmessage(event)\n" \
511 // " {\n" \ 517 " {\n" \
512 // " if(typeof(event.data) === 'string')\n" \ 518 " if(typeof(event.data) === 'string')\n" \
513 // " {\n" \ 519 " {\n" \
514 // " // text message or command\n" \ 520 " // text message or command\n" \
515 // " let message = event.data.split('|', 3);\n" \ 521 " let message = event.data.split('|', 3);\n" \
516 // " switch(message[0])\n" \ 522 " switch(message[0])\n" \
517 // " {\n" \ 523 " {\n" \
518 // " case 'userinit':\n" \ 524 " case 'userinit':\n" \
519 // " connectedUsers.set(message[1], message[2]);\n" \ 525 " connectedUsers.set(message[1], message[2]);\n" \
520 // " {\n" \ 526 " {\n" \
521 // " let users = document.getElementById('Users');\n" \ 527 " let users = document.getElementById('Users');\n" \
522 // " let div = document.createElement('div');\n" \ 528 " let div = document.createElement('div');\n" \
523 // " users.appendChild(div);\n" \ 529 " users.appendChild(div);\n" \
524 // " div.innerText = message[2];\n" \ 530 " div.innerText = message[2];\n" \
525 // " div.setAttribute('data-user', message[1]);\n" \ 531 " div.setAttribute('data-user', message[1]);\n" \
526 // " }\n" \ 532 " }\n" \
527 // " break;\n" \ 533 " break;\n" \
528 // " case 'useradd':\n" \ 534 " case 'useradd':\n" \
529 // " connectedUsers.set(message[1], message[2]);\n" \ 535 " connectedUsers.set(message[1], message[2]);\n" \
530 // " chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \ 536 " chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \
531 // " {\n" \ 537 " {\n" \
532 // " let users = document.getElementById('Users');\n" \ 538 " let users = document.getElementById('Users');\n" \
533 // " let div = document.createElement('div');\n" \ 539 " let div = document.createElement('div');\n" \
534 // " users.appendChild(div);\n" \ 540 " users.appendChild(div);\n" \
535 // " div.innerText = message[2];\n" \ 541 " div.innerText = message[2];\n" \
536 // " div.setAttribute('data-user', message[1]);\n" \ 542 " div.setAttribute('data-user', message[1]);\n" \
537 // " }\n" \ 543 " }\n" \
538 // " break;\n" \ 544 " break;\n" \
539 // " case 'userdel':\n" \ 545 " case 'userdel':\n" \
540 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \ 546 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \
541 // " connectedUsers.delete(message[1]);\n" \ 547 " connectedUsers.delete(message[1]);\n" \
542 // " {\n" \ 548 " {\n" \
543 // " let users = document.getElementById('Users');\n" \ 549 " let users = document.getElementById('Users');\n" \
544 // " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \ 550 " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
545 // " if(div !== null)\n" \ 551 " if(div !== null)\n" \
546 // " {\n" \ 552 " {\n" \
547 // " users.removeChild(div);\n" \ 553 " users.removeChild(div);\n" \
548 // " if(div.classList.contains('selected'))\n" \ 554 " if(div.classList.contains('selected'))\n" \
549 // " users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \ 555 " users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
550 // " }\n" \ 556 " }\n" \
551 // " }\n" \ 557 " }\n" \
552 // " break;\n" \ 558 " break;\n" \
553 // " case 'username':\n" \ 559 " case 'username':\n" \
554 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \ 560 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \
555 // " connectedUsers.set(message[1], message[2]);\n" \ 561 " connectedUsers.set(message[1], message[2]);\n" \
556 // " {\n" \ 562 " {\n" \
557 // " let users = document.getElementById('Users');\n" \ 563 " let users = document.getElementById('Users');\n" \
558 // " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \ 564 " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
559 // " if(div !== null)\n" \ 565 " if(div !== null)\n" \
560 // " {\n" \ 566 " {\n" \
561 // " div.innerText = message[2];\n" \ 567 " div.innerText = message[2];\n" \
562 // " }\n" \ 568 " }\n" \
563 // " }\n" \ 569 " }\n" \
564 // " break;\n" \ 570 " break;\n" \
565 // " case 'ping':\n" \ 571 " case 'ping':\n" \
566 // " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \ 572 " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \
567 // " break;\n" \ 573 " break;\n" \
568 // " default:\n" \ 574 " default:\n" \
569 // " chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \ 575 " chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \
570 // " break;\n" \ 576 " break;\n" \
571 // " }\n" \ 577 " }\n" \
572 // " }\n" \ 578 " }\n" \
573 // " else\n" \ 579 " else\n" \
574 // " {\n" \ 580 " {\n" \
575 // " // We received a binary frame, which means a picture here\n" \ 581 " // We received a binary frame, which means a picture here\n" \
576 // " let byteData = new Uint8Array(event.data);\n" \ 582 " let byteData = new Uint8Array(event.data);\n" \
577 // " let decoder = new TextDecoder();\n" \ 583 " let decoder = new TextDecoder();\n" \
578 // " let message = [ ];\n" \ 584 " let message = [ ];\n" \
579 // " // message type\n" \ 585 " // message type\n" \
580 // " let j = 0;\n" \ 586 " let j = 0;\n" \
581 // " let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \ 587 " let i = byteData.indexOf(0x7C, j); // | = 0x7C;\n" \
582 // " if(i < 0)\n" \ 588 " if(i < 0)\n" \
583 // " return;\n" \ 589 " return;\n" \
584 // " message.push(decoder.decode(byteData.slice(0, i)));\n" \ 590 " message.push(decoder.decode(byteData.slice(0, i)));\n" \
585 // " // picture from\n" \ 591 " // picture from\n" \
586 // " j = i + 1;\n" \ 592 " j = i + 1;\n" \
587 // " i = byteData.indexOf(0x7C, j);\n" \ 593 " i = byteData.indexOf(0x7C, j);\n" \
588 // " if(i < 0)\n" \ 594 " if(i < 0)\n" \
589 // " return;\n" \ 595 " return;\n" \
590 // " message.push(decoder.decode(byteData.slice(j, i)));\n" \ 596 " message.push(decoder.decode(byteData.slice(j, i)));\n" \
591 // " // picture encoding\n" \ 597 " // picture encoding\n" \
592 // " j = i + 1;\n" \ 598 " j = i + 1;\n" \
593 // " i = byteData.indexOf(0x7C, j);\n" \ 599 " i = byteData.indexOf(0x7C, j);\n" \
594 // " if(i < 0)\n" \ 600 " if(i < 0)\n" \
595 // " return;\n" \ 601 " return;\n" \
596 // " message.push(decoder.decode(byteData.slice(j, i)));\n" \ 602 " message.push(decoder.decode(byteData.slice(j, i)));\n" \
597 // " // picture\n" \ 603 " // picture\n" \
598 // " byteData = byteData.slice(i + 1);\n" \ 604 " byteData = byteData.slice(i + 1);\n" \
599 // " chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \ 605 " chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \
600 // " }\n" \ 606 " }\n" \
601 // " }\n" \ 607 " }\n" \
602 // "</script>" \ 608 "</script>" \
603 // "</head>" \ 609 "</head>" \
604 // "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \ 610 "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \
605 // "</html>" 611 "</html>"
606 612
607#define PAGE_NOT_FOUND \ 613#define PAGE_NOT_FOUND \
608 "404 Not Found" 614 "404 Not Found"
@@ -708,7 +714,7 @@ make_blocking (MHD_socket fd)
708#elif defined(MHD_WINSOCK_SOCKETS) 714#elif defined(MHD_WINSOCK_SOCKETS)
709 unsigned long flags = 0; 715 unsigned long flags = 0;
710 716
711 ioctlsocket (fd, (int) FIONBIO, &flags); 717 ioctlsocket (fd, FIONBIO, &flags);
712#endif /* MHD_WINSOCK_SOCKETS */ 718#endif /* MHD_WINSOCK_SOCKETS */
713 719
714} 720}
@@ -739,7 +745,6 @@ send_all (struct ConnectedUser*cu,
739 0); 745 0);
740 if (0 > ret) 746 if (0 > ret)
741 { 747 {
742 int err = errno;
743 if (EAGAIN == errno) 748 if (EAGAIN == errno)
744 { 749 {
745 ret = 0; 750 ret = 0;
@@ -927,7 +932,7 @@ chat_adduser (struct ConnectedUser*cu)
927{ 932{
928 /* initialize the notification message of the new user */ 933 /* initialize the notification message of the new user */
929 char user_index[32]; 934 char user_index[32];
930 itoa ((int) cu->user_id, user_index, 10); 935 snprintf (user_index, 32, "%d", (int) cu->user_id);
931 size_t user_index_len = strlen (user_index); 936 size_t user_index_len = strlen (user_index);
932 size_t data_len = user_index_len + cu->user_name_len + 9; 937 size_t data_len = user_index_len + cu->user_name_len + 9;
933 char*data = (char*) malloc (data_len + 1); 938 char*data = (char*) malloc (data_len + 1);
@@ -998,7 +1003,7 @@ chat_removeuser (struct ConnectedUser*cu)
998 char user_index[32]; 1003 char user_index[32];
999 1004
1000 /* initialize the chat message for the removed user */ 1005 /* initialize the chat message for the removed user */
1001 itoa ((int) cu->user_id, user_index, 10); 1006 snprintf (user_index, 32, "%d", (int) cu->user_id);
1002 size_t user_index_len = strlen (user_index); 1007 size_t user_index_len = strlen (user_index);
1003 size_t data_len = user_index_len + 9; 1008 size_t data_len = user_index_len + 9;
1004 char*data = (char*) malloc (data_len + 1); 1009 char*data = (char*) malloc (data_len + 1);
@@ -1071,9 +1076,8 @@ chat_renameuser (struct ConnectedUser*cu,
1071 { 1076 {
1072 if (cu != users[i]) 1077 if (cu != users[i])
1073 { 1078 {
1074 if ((users[i]->user_name_len == new_name_len) && (0 == stricmp ( 1079 if ((users[i]->user_name_len == new_name_len) &&
1075 users[i]->user_name, 1080 (0 == strcasecmp (users[i]->user_name, new_name)))
1076 new_name)))
1077 { 1081 {
1078 pthread_mutex_unlock (&chat_mutex); 1082 pthread_mutex_unlock (&chat_mutex);
1079 return 2; 1083 return 2;
@@ -1083,7 +1087,7 @@ chat_renameuser (struct ConnectedUser*cu,
1083 1087
1084 /* generate the notification message */ 1088 /* generate the notification message */
1085 char user_index[32]; 1089 char user_index[32];
1086 itoa ((int) cu->user_id, user_index, 10); 1090 snprintf (user_index, 32, "%d", (int) cu->user_id);
1087 size_t user_index_len = strlen (user_index); 1091 size_t user_index_len = strlen (user_index);
1088 size_t data_len = user_index_len + new_name_len + 10; 1092 size_t data_len = user_index_len + new_name_len + 10;
1089 char*data = (char*) malloc (data_len + 1); 1093 char*data = (char*) malloc (data_len + 1);
@@ -1202,17 +1206,17 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1202 /* no command means regular message */ 1206 /* no command means regular message */
1203 command = 0; 1207 command = 0;
1204 } 1208 }
1205 else if (0 == stricmp (frame_data, "private")) 1209 else if (0 == strcasecmp (frame_data, "private"))
1206 { 1210 {
1207 /* private means private message */ 1211 /* private means private message */
1208 command = 1; 1212 command = 1;
1209 } 1213 }
1210 else if (0 == stricmp (frame_data, "name")) 1214 else if (0 == strcasecmp (frame_data, "name"))
1211 { 1215 {
1212 /* name means chat user rename */ 1216 /* name means chat user rename */
1213 command = 2; 1217 command = 2;
1214 } 1218 }
1215 else if (0 == stricmp (frame_data, "ping")) 1219 else if (0 == strcasecmp (frame_data, "ping"))
1216 { 1220 {
1217 /* ping means a ping request */ 1221 /* ping means a ping request */
1218 command = 3; 1222 command = 3;
@@ -1263,7 +1267,7 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1263 * This is useful for debugging with an IDE. 1267 * This is useful for debugging with an IDE.
1264 */ 1268 */
1265 char user_index[32]; 1269 char user_index[32];
1266 itoa ((int) from_user_id, user_index, 10); 1270 snprintf (user_index, 32, "%d", (int) cu->user_id);
1267 size_t user_index_len = strlen (user_index); 1271 size_t user_index_len = strlen (user_index);
1268 size_t data_len = user_index_len + frame_len - j + 9; 1272 size_t data_len = user_index_len + frame_len - j + 9;
1269 char*data = (char*) malloc (data_len + 1); 1273 char*data = (char*) malloc (data_len + 1);
@@ -1300,7 +1304,7 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1300 * The difference is the prefix "private" 1304 * The difference is the prefix "private"
1301 */ 1305 */
1302 char user_index[32]; 1306 char user_index[32];
1303 itoa ((int) from_user_id, user_index, 10); 1307 snprintf (user_index, 32, "%d", (int) cu->user_id);
1304 size_t user_index_len = strlen (user_index); 1308 size_t user_index_len = strlen (user_index);
1305 size_t data_len = user_index_len + frame_len - j + 9; 1309 size_t data_len = user_index_len + frame_len - j + 9;
1306 char*data = (char*) malloc (data_len + 1); 1310 char*data = (char*) malloc (data_len + 1);
@@ -1538,14 +1542,10 @@ connecteduser_parse_received_websocket_stream (struct ConnectedUser*cu,
1538 char result_text[240]; 1542 char result_text[240];
1539 strcpy (result_text, 1543 strcpy (result_text,
1540 "ping|"); 1544 "ping|");
1541 itoa ((int) cu->user_id, 1545 snprintf (result_text + 5, 235, "%d", (int) cu->user_id);
1542 result_text + 5,
1543 10);
1544 strcat (result_text, 1546 strcat (result_text,
1545 "|"); 1547 "|");
1546 itoa (ping, 1548 snprintf (result_text + strlen (result_text), 240 - strlen (result_text), "%d", (int) ping);
1547 result_text + strlen (result_text),
1548 10);
1549 chat_addmessage (0, 1549 chat_addmessage (0,
1550 0, 1550 0,
1551 result_text, 1551 result_text,
@@ -1627,9 +1627,7 @@ connecteduser_send_messages (void*cls)
1627 ++cu->ping_counter; 1627 ++cu->ping_counter;
1628 strcpy (cu->ping_message, 1628 strcpy (cu->ping_message,
1629 "libmicrohttpdchatserverpingdata"); 1629 "libmicrohttpdchatserverpingdata");
1630 itoa (cu->ping_counter, 1630 snprintf (cu->ping_message + 31, 97, "%d", (int) cu->ping_counter);
1631 cu->ping_message + 31,
1632 10);
1633 cu->ping_message_len = strlen (cu->ping_message); 1631 cu->ping_message_len = strlen (cu->ping_message);
1634 char*frame_data = NULL; 1632 char*frame_data = NULL;
1635 size_t frame_len = 0; 1633 size_t frame_len = 0;
@@ -1750,7 +1748,7 @@ connecteduser_receive_messages (void *cls)
1750 { 1748 {
1751 char user_name[32]; 1749 char user_name[32];
1752 strcpy (user_name, "User"); 1750 strcpy (user_name, "User");
1753 itoa ((int) cu->user_id, user_name + 4, 10); 1751 snprintf (user_name + 4, 28, "%d", (int) cu->user_id);
1754 cu->user_name_len = strlen (user_name); 1752 cu->user_name_len = strlen (user_name);
1755 cu->user_name = malloc (cu->user_name_len + 1); 1753 cu->user_name = malloc (cu->user_name_len + 1);
1756 if (NULL == cu->user_name) 1754 if (NULL == cu->user_name)
@@ -1831,7 +1829,7 @@ connecteduser_receive_messages (void *cls)
1831 for (size_t i = 0; i < user_count; ++i) 1829 for (size_t i = 0; i < user_count; ++i)
1832 { 1830 {
1833 char user_index[32]; 1831 char user_index[32];
1834 itoa ((int) users[i]->user_id, user_index, 10); 1832 snprintf (user_index, 32, "%d", (int) users[i]->user_id);
1835 size_t user_index_len = strlen (user_index); 1833 size_t user_index_len = strlen (user_index);
1836 struct UserInit iu; 1834 struct UserInit iu;
1837 iu.user_init_len = user_index_len + users[i]->user_name_len + 10; 1835 iu.user_init_len = user_index_len + users[i]->user_name_len + 10;
@@ -1891,15 +1889,16 @@ connecteduser_receive_messages (void *cls)
1891 " /ping <user> - sends a ping to the specified user\n" \ 1889 " /ping <user> - sends a ping to the specified user\n" \
1892 " /name <name> - changes your name to the specified name\n" \ 1890 " /name <name> - changes your name to the specified name\n" \
1893 " /disconnect - disconnects your websocket\n\n" \ 1891 " /disconnect - disconnects your websocket\n\n" \
1894 "All messages, which does not start a slash, are regular messages, which will be sent to selected user.\n\n" \ 1892 "All messages, which does not start with a slash, " \
1893 "are regular messages and will be sent to the selected user.\n\n" \
1895 "Have fun!"; 1894 "Have fun!";
1896 int r = MHD_websocket_encode_text (cu->ws, 1895 MHD_websocket_encode_text (cu->ws,
1897 welcome_msg, 1896 welcome_msg,
1898 strlen (welcome_msg), 1897 strlen (welcome_msg),
1899 MHD_WEBSOCKET_FRAGMENTATION_NONE, 1898 MHD_WEBSOCKET_FRAGMENTATION_NONE,
1900 &frame_data, 1899 &frame_data,
1901 &frame_len, 1900 &frame_len,
1902 NULL); 1901 NULL);
1903 send_all (cu, 1902 send_all (cu,
1904 frame_data, 1903 frame_data,
1905 frame_len); 1904 frame_len);
@@ -2132,7 +2131,7 @@ upgrade_handler (void *cls,
2132 * @param ptr A pointer for request specific data 2131 * @param ptr A pointer for request specific data
2133 * @return MHD_YES on success or MHD_NO on error. 2132 * @return MHD_YES on success or MHD_NO on error.
2134 */ 2133 */
2135static int 2134static enum MHD_Result
2136access_handler (void *cls, 2135access_handler (void *cls,
2137 struct MHD_Connection *connection, 2136 struct MHD_Connection *connection,
2138 const char *url, 2137 const char *url,
@@ -2185,118 +2184,88 @@ access_handler (void *cls,
2185 * Furthermore it must be a HTTP/1.1 or higher GET request. 2184 * Furthermore it must be a HTTP/1.1 or higher GET request.
2186 * See: https://tools.ietf.org/html/rfc6455#section-4.2.1 2185 * See: https://tools.ietf.org/html/rfc6455#section-4.2.1
2187 * 2186 *
2188 * To ease this example we skip the following checks: 2187 * To make this example portable we skip the Host check
2189 * - Whether the HTTP version is 1.1 or newer
2190 * - Whether Connection is Upgrade, because this header may
2191 * contain multiple values.
2192 * - The requested Host (because we don't know)
2193 */ 2188 */
2194 2189
2190 char is_valid = 1;
2191 const char* value = NULL;
2192 char sec_websocket_accept[29];
2193
2195 /* check whether an websocket upgrade is requested */ 2194 /* check whether an websocket upgrade is requested */
2196 const char*value = MHD_lookup_connection_value (connection, 2195 if (0 != MHD_websocket_check_http_version (version))
2197 MHD_HEADER_KIND,
2198 MHD_HTTP_HEADER_UPGRADE);
2199 if ((0 == value) || (0 != stricmp (value, "websocket")))
2200 { 2196 {
2201 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2197 is_valid = 0;
2202 PAGE_INVALID_WEBSOCKET_REQUEST), 2198 }
2203 PAGE_INVALID_WEBSOCKET_REQUEST, 2199 value = MHD_lookup_connection_value (connection,
2204 MHD_RESPMEM_PERSISTENT); 2200 MHD_HEADER_KIND,
2205 ret = MHD_queue_response (connection, 2201 MHD_HTTP_HEADER_CONNECTION);
2206 MHD_HTTP_BAD_REQUEST, 2202 if (0 != MHD_websocket_check_connection_header (value))
2207 response); 2203 {
2208 MHD_destroy_response (response); 2204 is_valid = 0;
2209 return ret; 2205 }
2206 value = MHD_lookup_connection_value (connection,
2207 MHD_HEADER_KIND,
2208 MHD_HTTP_HEADER_UPGRADE);
2209 if (0 != MHD_websocket_check_upgrade_header (value))
2210 {
2211 is_valid = 0;
2210 } 2212 }
2211
2212 /* check the protocol version */
2213 value = MHD_lookup_connection_value (connection, 2213 value = MHD_lookup_connection_value (connection,
2214 MHD_HEADER_KIND, 2214 MHD_HEADER_KIND,
2215 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION); 2215 MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
2216 if ((0 == value) || (0 != stricmp (value, "13"))) 2216 if (0 != MHD_websocket_check_version_header (value))
2217 { 2217 {
2218 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2218 is_valid = 0;
2219 PAGE_INVALID_WEBSOCKET_REQUEST),
2220 PAGE_INVALID_WEBSOCKET_REQUEST,
2221 MHD_RESPMEM_PERSISTENT);
2222 ret = MHD_queue_response (connection,
2223 MHD_HTTP_BAD_REQUEST,
2224 response);
2225 MHD_destroy_response (response);
2226 return ret;
2227 } 2219 }
2228 2220 value = MHD_lookup_connection_value (connection,
2229 /* read the websocket key (required for the response) */ 2221 MHD_HEADER_KIND,
2230 value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
2231 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY); 2222 MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
2232 if (0 == value) 2223 if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
2233 { 2224 {
2234 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2225 is_valid = 0;
2235 PAGE_INVALID_WEBSOCKET_REQUEST),
2236 PAGE_INVALID_WEBSOCKET_REQUEST,
2237 MHD_RESPMEM_PERSISTENT);
2238 ret = MHD_queue_response (connection,
2239 MHD_HTTP_BAD_REQUEST,
2240 response);
2241 MHD_destroy_response (response);
2242 return ret;
2243 } 2226 }
2244 2227
2245 /* generate the response accept header */ 2228 if (1 == is_valid)
2246 char sec_websocket_accept[29];
2247 if (0 != MHD_websocket_create_accept (value, sec_websocket_accept))
2248 { 2229 {
2249 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2230 /* create the response for upgrade */
2250 PAGE_INVALID_WEBSOCKET_REQUEST), 2231 response = MHD_create_response_for_upgrade (&upgrade_handler,
2251 PAGE_INVALID_WEBSOCKET_REQUEST, 2232 NULL);
2252 MHD_RESPMEM_PERSISTENT); 2233
2234 /**
2235 * For the response we need at least the following headers:
2236 * 1. "Connection: Upgrade"
2237 * 2. "Upgrade: websocket"
2238 * 3. "Sec-WebSocket-Accept: <base64value>"
2239 * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept_header.
2240 * It requires the value of the Sec-WebSocket-Key header of the request.
2241 * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
2242 */
2243 MHD_add_response_header (response,
2244 MHD_HTTP_HEADER_CONNECTION,
2245 "Upgrade");
2246 MHD_add_response_header (response,
2247 MHD_HTTP_HEADER_UPGRADE,
2248 "websocket");
2249 MHD_add_response_header (response,
2250 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
2251 sec_websocket_accept);
2253 ret = MHD_queue_response (connection, 2252 ret = MHD_queue_response (connection,
2254 MHD_HTTP_BAD_REQUEST, 2253 MHD_HTTP_SWITCHING_PROTOCOLS,
2255 response); 2254 response);
2256 MHD_destroy_response (response); 2255 MHD_destroy_response (response);
2257 return ret;
2258 } 2256 }
2259 2257 else
2260 /* only for this example: don't accept incoming connection when we are already shutting down */
2261 if (0 != disconnect_all)
2262 { 2258 {
2259 /* return error page */
2263 struct MHD_Response*response = MHD_create_response_from_buffer (strlen ( 2260 struct MHD_Response*response = MHD_create_response_from_buffer (strlen (
2264 PAGE_INVALID_WEBSOCKET_REQUEST), 2261 PAGE_INVALID_WEBSOCKET_REQUEST),
2265 PAGE_INVALID_WEBSOCKET_REQUEST, 2262 PAGE_INVALID_WEBSOCKET_REQUEST,
2266 MHD_RESPMEM_PERSISTENT); 2263 MHD_RESPMEM_PERSISTENT);
2267 ret = MHD_queue_response (connection, 2264 ret = MHD_queue_response (connection,
2268 MHD_HTTP_SERVICE_UNAVAILABLE, 2265 MHD_HTTP_BAD_REQUEST,
2269 response); 2266 response);
2270 MHD_destroy_response (response); 2267 MHD_destroy_response (response);
2271 return ret;
2272 } 2268 }
2273
2274 /* create the response for upgrade */
2275 response = MHD_create_response_for_upgrade (&upgrade_handler,
2276 NULL);
2277
2278 /**
2279 * For the response we need at least the following headers:
2280 * 1. "Connection: Upgrade"
2281 * 2. "Upgrade: websocket"
2282 * 3. "Sec-WebSocket-Accept: <base64value>"
2283 * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept.
2284 * It requires the value of the Sec-WebSocket-Key header of the request.
2285 * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
2286 */
2287 MHD_add_response_header (response,
2288 MHD_HTTP_HEADER_CONNECTION,
2289 "Upgrade");
2290 MHD_add_response_header (response,
2291 MHD_HTTP_HEADER_UPGRADE,
2292 "websocket");
2293 MHD_add_response_header (response,
2294 MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
2295 sec_websocket_accept);
2296 ret = MHD_queue_response (connection,
2297 MHD_HTTP_SWITCHING_PROTOCOLS,
2298 response);
2299 MHD_destroy_response (response);
2300 } 2269 }
2301 else 2270 else
2302 { 2271 {
@@ -2353,7 +2322,7 @@ main (int argc,
2353 MHD_OPTION_END); 2322 MHD_OPTION_END);
2354#endif 2323#endif
2355 2324
2356 if (d == NULL) 2325 if (NULL == d)
2357 return 1; 2326 return 1;
2358 (void) getc (stdin); 2327 (void) getc (stdin);
2359 2328
diff --git a/src/include/microhttpd_ws.h b/src/include/microhttpd_ws.h
index bfbd550a..f19c140d 100644
--- a/src/include/microhttpd_ws.h
+++ b/src/include/microhttpd_ws.h
@@ -51,29 +51,27 @@ struct MHD_WebSocketStream;
51enum MHD_WEBSOCKET_FLAG 51enum MHD_WEBSOCKET_FLAG
52{ 52{
53 /** 53 /**
54 * The websocket is used by the server (default). 54 * The websocket stream is initialized in server mode (default).
55 * Thus all outgoing payload will not be "masked". 55 * Thus all outgoing payload will not be "masked".
56 * All incoming payload must be masked. 56 * All incoming payload must be masked.
57 * This cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT 57 * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT
58 */ 58 */
59 MHD_WEBSOCKET_FLAG_SERVER = 0, 59 MHD_WEBSOCKET_FLAG_SERVER = 0,
60 /** 60 /**
61 * The websocket is used by the client 61 * The websocket stream is initialized in client mode.
62 * (not used if you provide the server). 62 * You will usually never use that mode in combination with libmicrohttpd,
63 * Thus all outgoing payload will be "masked" (XOR-ed with random values).