websocket.inc (27953B)
1 Websockets are a genuine way to implement push notifications, 2 where the server initiates the communication while the client can be idle. 3 Usually a HTTP communication is half-duplex and always requested by the client, 4 but websockets are full-duplex and only initialized by the client. 5 In the further communication both sites can use the websocket at any time 6 to send data to the other site. 7 8 To initialize a websocket connection the client sends a special HTTP request 9 to the server and initializes 10 a handshake between client and server which switches from the HTTP protocol 11 to the websocket protocol. 12 Thus both the server as well as the client must support websockets. 13 If proxys are used, they must support websockets too. 14 In this chapter we take a look on server and client, but with a focus on 15 the server with @emph{libmicrohttpd}. 16 17 Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests, 18 which is required for switching from the HTTP protocol. 19 Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added 20 to support the websocket protocol. 21 22 @heading Upgrading connections with libmicrohttpd 23 24 To support websockets we need to enable upgrading of HTTP connections first. 25 This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to 26 @code{MHD_start_daemon()}. 27 28 29 @verbatim 30 daemon = 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 41 The next step is to turn a specific request into an upgraded connection. 42 This done in our @code{access_handler} by calling 43 @code{MHD_create_response_for_upgrade()}. 44 An @code{upgrade_handler} will be passed to perform the low-level actions 45 on the socket. 46 47 @emph{Please note that the socket here is just a regular socket as provided 48 by the operating system. 49 To use it as a websocket, some more steps from the following 50 chapters are required.} 51 52 53 @verbatim 54 static enum MHD_Result 55 access_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 87 In the @code{upgrade_handler} we receive the low-level socket, 88 which is used for the communication with the specific client. 89 In addition to the low-level socket we get: 90 @itemize @bullet 91 @item 92 Some data, which has been read too much while @emph{libmicrohttpd} was 93 switching the protocols. 94 This value is usually empty, because it would mean that the client 95 has sent data before the handshake was complete. 96 97 @item 98 A @code{struct MHD_UpgradeResponseHandle} which is used to perform 99 special actions like closing, corking or uncorking the socket. 100 These commands are executed by passing the handle 101 to @code{MHD_upgrade_action()}. 102 103 104 @end itemize 105 106 Depending of the flags specified while calling @code{MHD_start_deamon()} 107 our @code{upgrade_handler} is either executed in the same thread 108 as our daemon or in a thread specific for each connection. 109 If it is executed in the same thread then @code{upgrade_handler} is 110 a blocking call for our webserver and 111 we should finish it as fast as possible (i. e. by creating a thread and 112 passing the information there). 113 If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to 114 @code{MHD_start_daemon()} then a separate thread is used and 115 thus our @code{upgrade_handler} needs not to start a separate thread. 116 117 An @code{upgrade_handler}, which is called with a separate thread 118 per connection, could look like this: 119 120 121 @verbatim 122 static void 123 upgrade_handler (void *cls, 124 struct MHD_Connection *connection, 125 void *req_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 143 This is all you need to know for upgrading connections 144 with @emph{libmicrohttpd}. 145 The next chapters focus on using the websocket protocol 146 with @emph{libmicrohttpd_ws}. 147 148 149 @heading Websocket handshake with libmicrohttpd_ws 150 151 To request a websocket connection the client must send 152 the following information with the HTTP request: 153 154 @itemize @bullet 155 @item 156 A @code{GET} request must be sent. 157 158 @item 159 The version of the HTTP protocol must be 1.1 or higher. 160 161 @item 162 A @code{Host} header field must be sent 163 164 @item 165 A @code{Upgrade} header field containing the keyword "websocket" 166 (case-insensitive). 167 Please note that the client could pass multiple protocols separated by comma. 168 169 @item 170 A @code{Connection} header field that includes the token "Upgrade" 171 (case-insensitive). 172 Please note that the client could pass multiple tokens separated by comma. 173 174 @item 175 A @code{Sec-WebSocket-Key} header field with a base64-encoded value. 176 The decoded the value is 16 bytes long 177 and has been generated randomly by the client. 178 179 @item 180 A @code{Sec-WebSocket-Version} header field with the value "13". 181 182 @end itemize 183 184 185 Optionally the client can also send the following information: 186 187 188 @itemize @bullet 189 @item 190 A @code{Origin} header field can be used to determine the source 191 of the client (i. e. the website). 192 193 @item 194 A @code{Sec-WebSocket-Protocol} header field can contain a list 195 of supported protocols by the client, which can be sent over the websocket. 196 197 @item 198 A @code{Sec-WebSocket-Extensions} header field which may contain extensions 199 to the websocket protocol. The extensions must be registered by IANA. 200 201 @end itemize 202 203 204 A valid example request from the client could look like this: 205 206 207 @verbatim 208 GET /chat HTTP/1.1 209 Host: server.example.com 210 Upgrade: websocket 211 Connection: Upgrade 212 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== 213 Sec-WebSocket-Version: 13 214 @end verbatim 215 @noindent 216 217 218 To complete the handshake the server must respond with 219 some specific response headers: 220 221 @itemize @bullet 222 @item 223 The HTTP response code @code{101 Switching Protocols} must be answered. 224 225 @item 226 An @code{Upgrade} header field containing the value "websocket" must be sent. 227 228 @item 229 A @code{Connection} header field containing the value "Upgrade" must be sent. 230 231 @item 232 A @code{Sec-WebSocket-Accept} header field containing a value, which 233 has been calculated from the @code{Sec-WebSocket-Key} request header field, 234 must be sent. 235 236 @end itemize 237 238 239 Optionally the server may send following headers: 240 241 242 @itemize @bullet 243 @item 244 A @code{Sec-WebSocket-Protocol} header field containing a protocol 245 of the list specified in the corresponding request header field. 246 247 @item 248 A @code{Sec-WebSocket-Extension} header field containing all used extensions 249 of the list specified in the corresponding request header field. 250 251 @end itemize 252 253 254 A valid websocket HTTP response could look like this: 255 256 @verbatim 257 HTTP/1.1 101 Switching Protocols 258 Upgrade: websocket 259 Connection: Upgrade 260 Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= 261 @end verbatim 262 @noindent 263 264 265 To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides 266 some 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 271 is 1.1 or above. 272 273 @item 274 @code{MHD_websocket_check_connection_header()} checks whether the value 275 of the @code{Connection} request header field contains 276 an "Upgrade" token (case-insensitive). 277 278 @item 279 @code{MHD_websocket_check_upgrade_header()} checks whether the value 280 of the @code{Upgrade} request header field contains 281 the "websocket" keyword (case-insensitive). 282 283 @item 284 @code{MHD_websocket_check_version_header()} checks whether the value 285 of the @code{Sec-WebSocket-Version} request header field is "13". 286 287 @item 288 @code{MHD_websocket_create_accept_header()} takes the value from 289 the @code{Sec-WebSocket-Key} request header and calculates the value 290 for the @code{Sec-WebSocket-Accept} response header field. 291 292 @end itemize 293 294 295 The @code{access_handler} example of the previous chapter can now be 296 extended with these helper functions to perform the websocket handshake: 297 298 @verbatim 299 static enum MHD_Result 300 access_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 enum MHD_Result 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 427 Please note that we skipped the check of the Host header field here, 428 because we don't know the host for this example. 429 430 @heading Decoding/encoding the websocket protocol with libmicrohttpd_ws 431 432 Once the websocket connection is established you can receive/send frame data 433 with the low-level socket functions @code{recv()} and @code{send()}. 434 The frame data which goes over the low-level socket is encoded according 435 to the websocket protocol. 436 To use received payload data, you need to decode the frame data first. 437 To send payload data, you need to encode it into frame data first. 438 439 @emph{libmicrohttpd_ws} provides several functions for encoding of 440 payload data and decoding of frame data: 441 442 @itemize @bullet 443 @item 444 @code{MHD_websocket_decode()} decodes received frame data. 445 The payload data may be of any kind, depending upon what the client has sent. 446 So this decode function is used for all kind of frames and returns 447 the frame type along with the payload data. 448 449 @item 450 @code{MHD_websocket_encode_text()} encodes text. 451 The 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 458 check whether the websocket is still valid and to test latency. 459 460 @item 461 @code{MHD_websocket_encode_ping()} encodes a pong response to 462 answer 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 472 Since you could receive or send fragmented data (i. e. due to a too 473 small buffer passed to @code{recv}) all of these encode/decode 474 functions require a pointer to a @code{struct MHD_WebSocketStream} passed 475 as argument. 476 In this structure @emph{libmicrohttpd_ws} stores information 477 about encoding/decoding of the particular websocket. 478 For each websocket you need a unique @code{struct MHD_WebSocketStream} 479 to encode/decode with this library. 480 481 To create or destroy @code{struct MHD_WebSocketStream} 482 we have additional functions: 483 484 @itemize @bullet 485 @item 486 @code{MHD_websocket_stream_init()} allocates and initializes 487 a new @code{struct MHD_WebSocketStream}. 488 You 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 496 With these encode/decode functions we can improve our @code{upgrade_handler} 497 callback function from an earlier example to a working websocket: 498 499 500 @verbatim 501 static void 502 upgrade_handler (void *cls, 503 struct MHD_Connection *connection, 504 void *req_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 */ 669 static void 670 send_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 */ 700 static void 701 make_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 723 Please note that the websocket in this example is only half-duplex. 724 It waits until the blocking @code{recv()} call returns and 725 only does then something. 726 In this example all frame types are decoded by @emph{libmicrohttpd_ws}, 727 but we only do something when a text, ping or close frame is received. 728 Binary and pong frames are ignored in our code. 729 This is legit, because the server is only required to implement at 730 least support for ping frame or close frame (the other frame types 731 could be skipped in theory, because they don't require an answer). 732 The pong frame doesn't require an answer and whether text frames or 733 binary frames get an answer simply belongs to your server application. 734 So this is a valid minimal example. 735 736 Until this point you've learned everything you need to basically 737 use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}. 738 These libraries offer much more functions for some specific cases. 739 740 741 The further chapters of this tutorial focus on some specific problems 742 and the client site programming. 743 744 745 @heading Using full-duplex websockets 746 747 To use full-duplex websockets you can simply create two threads 748 per websocket connection. 749 One of these threads is used for receiving data with 750 a blocking @code{recv()} call and the other thread is triggered 751 by the application internal codes and sends the data. 752 753 A full-duplex websocket example is implemented in the example file 754 @code{websocket_chatserver_example.c}. 755 756 @heading Error handling 757 758 The most functions of @emph{libmicrohttpd_ws} return a value 759 of @code{enum MHD_WEBSOCKET_STATUS}. 760 The values of this enumeration can be converted into an integer 761 and have an easy interpretation: 762 763 @itemize @bullet 764 @item 765 If the value is less than zero an error occurred and the call has failed. 766 Check the enumeration values for more specific information. 767 768 @item 769 If the value is equal to zero, the call succeeded. 770 771 @item 772 If the value is greater than zero, the call succeeded and the value 773 specifies the decoded frame type. 774 Currently positive values are only returned by @code{MHD_websocket_decode()} 775 (of the functions with this return enumeration type). 776 777 @end itemize 778 779 A websocket stream can also get broken when invalid frame data is received. 780 Also the other site could send a close frame which puts the stream into 781 a state where it may not be used for regular communication. 782 Whether a stream has become broken, can be checked with 783 @code{MHD_websocket_stream_is_valid()}. 784 785 786 @heading Fragmentation 787 788 In addition to the regular TCP/IP fragmentation the websocket protocol also 789 supports fragmentation. 790 Fragmentation could be used for continuous payload data such as video data 791 from a webcam. 792 Whether or not you want to receive fragmentation is specified upon 793 initialization of the websocket stream. 794 If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter 795 of @code{MHD_websocket_stream_init()} then you can receive fragments. 796 If you don't pass this flag (in the most cases you just pass zero as flags) 797 then you don't want to handle fragments on your own. 798 @emph{libmicrohttpd_ws} removes then the fragmentation for you 799 in the background. 800 You only get the completely assembled frames. 801 802 Upon encoding you specify whether or not you want to create a fragmented frame 803 by passing a flag to the corresponding encode function. 804 Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()} 805 can be used for fragmentation, because the other frame types may 806 not be fragmented. 807 Encoding fragmented frames is independent of 808 the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization. 809 810 @heading Quick guide to websockets in JavaScript 811 812 Websockets are supported in all modern web browsers. 813 You initialize a websocket connection by creating an instance of 814 the @code{WebSocket} class provided by the web browser. 815 816 There are some simple rules for using websockets in the browser: 817 818 @itemize @bullet 819 @item 820 When you initialize the instance of the websocket class you must pass an URL. 821 The 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://} 826 then you are in a security context, which means that you are only allowed to 827 access other secure protocols. 828 So you can only use @code{wss://} for websocket connections then. 829 If you try to @code{ws://} instead then your websocket connection will 830 automatically fail. 831 832 @item 833 The WebSocket class uses events to handle the receiving of data. 834 JavaScript is per definition a single-threaded language so 835 the receiving events will never overlap. 836 Sending is done directly by calling a method of the instance of 837 the WebSocket class. 838 839 @end itemize 840 841 842 Here is a short example for receiving/sending data to the same host 843 as 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 853 let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + 854 window.location.host + '/chat'; 855 let socket = null; 856 857 window.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