libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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