aboutsummaryrefslogtreecommitdiff
path: root/doc/chapters/websocket.inc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/chapters/websocket.inc')
-rw-r--r--doc/chapters/websocket.inc886
1 files changed, 886 insertions, 0 deletions
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