]> git.pld-linux.org Git - packages/libmicrohttpd.git/blob - libmicrohttpd-missing.patch
- updated to 0.9.74
[packages/libmicrohttpd.git] / libmicrohttpd-missing.patch
1 --- libmicrohttpd-0.9.74/doc/chapters/websocket.inc.orig        1970-01-01 01:00:00.000000000 +0100
2 +++ libmicrohttpd-0.9.74/doc/chapters/websocket.inc     2021-12-23 21:39:46.569468012 +0100
3 @@ -0,0 +1,886 @@
4 +Websockets are a genuine way to implement push notifications,
5 +where the server initiates the communication while the client can be idle.
6 +Usually a HTTP communication is half-duplex and always requested by the client,
7 +but websockets are full-duplex and only initialized by the client.
8 +In the further communication both sites can use the websocket at any time
9 +to send data to the other site.
10 +
11 +To initialize a websocket connection the client sends a special HTTP request
12 +to the server and initializes
13 +a handshake between client and server which switches from the HTTP protocol
14 +to the websocket protocol.
15 +Thus both the server as well as the client must support websockets.
16 +If proxys are used, they must support websockets too.
17 +In this chapter we take a look on server and client, but with a focus on
18 +the server with @emph{libmicrohttpd}.
19 +
20 +Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
21 +which is required for switching from the HTTP protocol.
22 +Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
23 +to support the websocket protocol.
24 +
25 +@heading Upgrading connections with libmicrohttpd
26 +
27 +To support websockets we need to enable upgrading of HTTP connections first.
28 +This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
29 +@code{MHD_start_daemon()}.
30 +
31 +
32 +@verbatim
33 +daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
34 +                           MHD_USE_THREAD_PER_CONNECTION |
35 +                           MHD_ALLOW_UPGRADE |
36 +                           MHD_USE_ERROR_LOG,
37 +                           PORT, NULL, NULL,
38 +                           &access_handler, NULL,
39 +                           MHD_OPTION_END);
40 +@end verbatim
41 +@noindent
42 +
43 +
44 +The next step is to turn a specific request into an upgraded connection.
45 +This done in our @code{access_handler} by calling
46 +@code{MHD_create_response_for_upgrade()}.
47 +An @code{upgrade_handler} will be passed to perform the low-level actions
48 +on the socket.
49 +
50 +@emph{Please note that the socket here is just a regular socket as provided
51 +by the operating system.
52 +To use it as a websocket, some more steps from the following
53 +chapters are required.}
54 +
55 +
56 +@verbatim
57 +static enum MHD_Result
58 +access_handler (void *cls,
59 +                struct MHD_Connection *connection,
60 +                const char *url,
61 +                const char *method,
62 +                const char *version,
63 +                const char *upload_data,
64 +                size_t *upload_data_size,
65 +                void **ptr)
66 +{
67 +  /* ... */
68 +  /* some code to decide whether to upgrade or not */
69 +  /* ... */
70 +
71 +  /* create the response for upgrade */
72 +  response = MHD_create_response_for_upgrade (&upgrade_handler,
73 +                                              NULL);
74 +
75 +  /* ... */
76 +  /* additional headers, etc. */
77 +  /* ... */
78 +
79 +  ret = MHD_queue_response (connection,
80 +                            MHD_HTTP_SWITCHING_PROTOCOLS,
81 +                            response);
82 +  MHD_destroy_response (response);
83 +
84 +  return ret;
85 +}
86 +@end verbatim
87 +@noindent
88 +
89 +
90 +In the @code{upgrade_handler} we receive the low-level socket,
91 +which is used for the communication with the specific client.
92 +In addition to the low-level socket we get:
93 +@itemize @bullet
94 +@item
95 +Some data, which has been read too much while @emph{libmicrohttpd} was
96 +switching the protocols.
97 +This value is usually empty, because it would mean that the client
98 +has sent data before the handshake was complete.
99 +
100 +@item
101 +A @code{struct MHD_UpgradeResponseHandle} which is used to perform
102 +special actions like closing, corking or uncorking the socket.
103 +These commands are executed by passing the handle
104 +to @code{MHD_upgrade_action()}.
105 +
106 +
107 +@end itemize
108 +
109 +Depending of the flags specified while calling @code{MHD_start_deamon()}
110 +our @code{upgrade_handler} is either executed in the same thread
111 +as our deamon or in a thread specific for each connection.
112 +If it is executed in the same thread then @code{upgrade_handler} is
113 +a blocking call for our webserver and
114 +we should finish it as fast as possible (i. e. by creating a thread and
115 +passing the information there).
116 +If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
117 +@code{MHD_start_daemon()} then a separate thread is used and
118 +thus our @code{upgrade_handler} needs not to start a separate thread.
119 +
120 +An @code{upgrade_handler}, which is called with a separate thread
121 +per connection, could look like this:
122 +
123 +
124 +@verbatim
125 +static void
126 +upgrade_handler (void *cls,
127 +                 struct MHD_Connection *connection,
128 +                 void *con_cls,
129 +                 const char *extra_in,
130 +                 size_t extra_in_size,
131 +                 MHD_socket fd,
132 +                 struct MHD_UpgradeResponseHandle *urh)
133 +{
134 +  /* ... */
135 +  /* do something with the socket `fd` like `recv()` or `send()` */
136 +  /* ... */
137 +
138 +  /* close the socket when it is not needed anymore */
139 +  MHD_upgrade_action (urh,
140 +                      MHD_UPGRADE_ACTION_CLOSE);
141 +}
142 +@end verbatim
143 +@noindent
144 +
145 +
146 +This is all you need to know for upgrading connections
147 +with @emph{libmicrohttpd}.
148 +The next chapters focus on using the websocket protocol
149 +with @emph{libmicrohttpd_ws}.
150 +
151 +
152 +@heading Websocket handshake with libmicrohttpd_ws
153 +
154 +To request a websocket connection the client must send
155 +the following information with the HTTP request:
156 +
157 +@itemize @bullet
158 +@item
159 +A @code{GET} request must be sent.
160 +
161 +@item
162 +The version of the HTTP protocol must be 1.1 or higher.
163 +
164 +@item
165 +A @code{Host} header field must be sent
166 +
167 +@item
168 +A @code{Upgrade} header field containing the keyword "websocket"
169 +(case-insensitive).
170 +Please note that the client could pass multiple protocols separated by comma.
171 +
172 +@item
173 +A @code{Connection} header field that includes the token "Upgrade"
174 +(case-insensitive).
175 +Please note that the client could pass multiple tokens separated by comma.
176 +
177 +@item
178 +A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
179 +The decoded the value is 16 bytes long
180 +and has been generated randomly by the client.
181 +
182 +@item
183 +A @code{Sec-WebSocket-Version} header field with the value "13".
184 +
185 +@end itemize
186 +
187 +
188 +Optionally the client can also send the following information:
189 +
190 +
191 +@itemize @bullet
192 +@item
193 +A @code{Origin} header field can be used to determine the source
194 +of the client (i. e. the website).
195 +
196 +@item
197 +A @code{Sec-WebSocket-Protocol} header field can contain a list
198 +of supported protocols by the client, which can be sent over the websocket.
199 +
200 +@item
201 +A @code{Sec-WebSocket-Extensions} header field which may contain extensions
202 +to the websocket protocol. The extensions must be registered by IANA.
203 +
204 +@end itemize
205 +
206 +
207 +A valid example request from the client could look like this:
208 +
209 +
210 +@verbatim
211 +GET /chat HTTP/1.1
212 +Host: server.example.com
213 +Upgrade: websocket
214 +Connection: Upgrade
215 +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
216 +Sec-WebSocket-Version: 13
217 +@end verbatim
218 +@noindent
219 +
220 +
221 +To complete the handshake the server must respond with
222 +some specific response headers:
223 +
224 +@itemize @bullet
225 +@item
226 +The HTTP response code @code{101 Switching Protocols} must be answered.
227 +
228 +@item
229 +An @code{Upgrade} header field containing the value "websocket" must be sent.
230 +
231 +@item
232 +A @code{Connection} header field containing the value "Upgrade" must be sent.
233 +
234 +@item
235 +A @code{Sec-WebSocket-Accept} header field containing a value, which
236 +has been calculated from the @code{Sec-WebSocket-Key} request header field,
237 +must be sent.
238 +
239 +@end itemize
240 +
241 +
242 +Optionally the server may send following headers:
243 +
244 +
245 +@itemize @bullet
246 +@item
247 +A @code{Sec-WebSocket-Protocol} header field containing a protocol
248 +of the list specified in the corresponding request header field.
249 +
250 +@item
251 +A @code{Sec-WebSocket-Extension} header field containing all used extensions
252 +of the list specified in the corresponding request header field.
253 +
254 +@end itemize
255 +
256 +
257 +A valid websocket HTTP response could look like this:
258 +
259 +@verbatim
260 +HTTP/1.1 101 Switching Protocols
261 +Upgrade: websocket
262 +Connection: Upgrade
263 +Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
264 +@end verbatim
265 +@noindent
266 +
267 +
268 +To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
269 +some helper functions for the @code{access_handler} callback function:
270 +
271 +@itemize @bullet
272 +@item
273 +@code{MHD_websocket_check_http_version()} checks whether the HTTP version
274 +is 1.1 or above.
275 +
276 +@item
277 +@code{MHD_websocket_check_connection_header()} checks whether the value
278 +of the @code{Connection} request header field contains
279 +an "Upgrade" token (case-insensitive).
280 +
281 +@item
282 +@code{MHD_websocket_check_upgrade_header()} checks whether the value
283 +of the @code{Upgrade} request header field contains
284 +the "websocket" keyword (case-insensitive).
285 +
286 +@item
287 +@code{MHD_websocket_check_version_header()} checks whether the value
288 +of the @code{Sec-WebSocket-Version} request header field is "13".
289 +
290 +@item
291 +@code{MHD_websocket_create_accept_header()} takes the value from
292 +the @code{Sec-WebSocket-Key} request header and calculates the value
293 +for the @code{Sec-WebSocket-Accept} response header field.
294 +
295 +@end itemize
296 +
297 +
298 +The @code{access_handler} example of the previous chapter can now be
299 +extended with these helper functions to perform the websocket handshake:
300 +
301 +@verbatim
302 +static enum MHD_Result
303 +access_handler (void *cls,
304 +                struct MHD_Connection *connection,
305 +                const char *url,
306 +                const char *method,
307 +                const char *version,
308 +                const char *upload_data,
309 +                size_t *upload_data_size,
310 +                void **ptr)
311 +{
312 +  static int aptr;
313 +  struct MHD_Response *response;
314 +  int ret;
315 +
316 +  (void) cls;               /* Unused. Silent compiler warning. */
317 +  (void) upload_data;       /* Unused. Silent compiler warning. */
318 +  (void) upload_data_size;  /* Unused. Silent compiler warning. */
319 +
320 +  if (0 != strcmp (method, "GET"))
321 +    return MHD_NO;              /* unexpected method */
322 +  if (&aptr != *ptr)
323 +  {
324 +    /* do never respond on first call */
325 +    *ptr = &aptr;
326 +    return MHD_YES;
327 +  }
328 +  *ptr = NULL;                  /* reset when done */
329 +
330 +  if (0 == strcmp (url, "/"))
331 +  {
332 +    /* Default page for visiting the server */
333 +    struct MHD_Response *response = MHD_create_response_from_buffer (
334 +                                      strlen (PAGE),
335 +                                      PAGE,
336 +                                      MHD_RESPMEM_PERSISTENT);
337 +    ret = MHD_queue_response (connection,
338 +                              MHD_HTTP_OK,
339 +                              response);
340 +    MHD_destroy_response (response);
341 +  }
342 +  else if (0 == strcmp (url, "/chat"))
343 +  {
344 +    char is_valid = 1;
345 +    const char* value = NULL;
346 +    char sec_websocket_accept[29];
347 +
348 +    if (0 != MHD_websocket_check_http_version (version))
349 +    {
350 +      is_valid = 0;
351 +    }
352 +    value = MHD_lookup_connection_value (connection,
353 +                                         MHD_HEADER_KIND,
354 +                                         MHD_HTTP_HEADER_CONNECTION);
355 +    if (0 != MHD_websocket_check_connection_header (value))
356 +    {
357 +      is_valid = 0;
358 +    }
359 +    value = MHD_lookup_connection_value (connection,
360 +                                         MHD_HEADER_KIND,
361 +                                         MHD_HTTP_HEADER_UPGRADE);
362 +    if (0 != MHD_websocket_check_upgrade_header (value))
363 +    {
364 +      is_valid = 0;
365 +    }
366 +    value = MHD_lookup_connection_value (connection,
367 +                                         MHD_HEADER_KIND,
368 +                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
369 +    if (0 != MHD_websocket_check_version_header (value))
370 +    {
371 +      is_valid = 0;
372 +    }
373 +    value = MHD_lookup_connection_value (connection,
374 +                                         MHD_HEADER_KIND,
375 +                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
376 +    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
377 +    {
378 +      is_valid = 0;
379 +    }
380 +
381 +    if (1 == is_valid)
382 +    {
383 +      /* upgrade the connection */
384 +      response = MHD_create_response_for_upgrade (&upgrade_handler,
385 +                                                  NULL);
386 +      MHD_add_response_header (response,
387 +                               MHD_HTTP_HEADER_CONNECTION,
388 +                               "Upgrade");
389 +      MHD_add_response_header (response,
390 +                               MHD_HTTP_HEADER_UPGRADE,
391 +                               "websocket");
392 +      MHD_add_response_header (response,
393 +                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
394 +                               sec_websocket_accept);
395 +      ret = MHD_queue_response (connection,
396 +                                MHD_HTTP_SWITCHING_PROTOCOLS,
397 +                                response);
398 +      MHD_destroy_response (response);
399 +    }
400 +    else
401 +    {
402 +      /* return error page */
403 +      struct MHD_Response*response = MHD_create_response_from_buffer (
404 +                                       strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
405 +                                       PAGE_INVALID_WEBSOCKET_REQUEST,
406 +                                       MHD_RESPMEM_PERSISTENT);
407 +      ret = MHD_queue_response (connection,
408 +                                MHD_HTTP_BAD_REQUEST,
409 +                                response);
410 +      MHD_destroy_response (response);
411 +    }
412 +  }
413 +  else
414 +  {
415 +    struct MHD_Response*response = MHD_create_response_from_buffer (
416 +                                     strlen (PAGE_NOT_FOUND),
417 +                                     PAGE_NOT_FOUND,
418 +                                     MHD_RESPMEM_PERSISTENT);
419 +    ret = MHD_queue_response (connection,
420 +                              MHD_HTTP_NOT_FOUND,
421 +                              response);
422 +    MHD_destroy_response (response);
423 +  }
424 +
425 +  return ret;
426 +}
427 +@end verbatim
428 +@noindent
429 +
430 +Please note that we skipped the check of the Host header field here,
431 +because we don't know the host for this example.
432 +
433 +@heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
434 +
435 +Once the websocket connection is established you can receive/send frame data
436 +with the low-level socket functions @code{recv()} and @code{send()}.
437 +The frame data which goes over the low-level socket is encoded according
438 +to the websocket protocol.
439 +To use received payload data, you need to decode the frame data first.
440 +To send payload data, you need to encode it into frame data first.
441 +
442 +@emph{libmicrohttpd_ws} provides serveral functions for encoding of
443 +payload data and decoding of frame data:
444 +
445 +@itemize @bullet
446 +@item
447 +@code{MHD_websocket_decode()} decodes received frame data.
448 +The payload data may be of any kind, depending upon what the client has sent.
449 +So this decode function is used for all kind of frames and returns
450 +the frame type along with the payload data.
451 +
452 +@item
453 +@code{MHD_websocket_encode_text()} encodes text.
454 +The text must be encoded with UTF-8.
455 +
456 +@item
457 +@code{MHD_websocket_encode_binary()} encodes binary data.
458 +
459 +@item
460 +@code{MHD_websocket_encode_ping()} encodes a ping request to
461 +check whether the websocket is still valid and to test latency.
462 +
463 +@item
464 +@code{MHD_websocket_encode_ping()} encodes a pong response to
465 +answer a received ping request.
466 +
467 +@item
468 +@code{MHD_websocket_encode_close()} encodes a close request.
469 +
470 +@item
471 +@code{MHD_websocket_free()} frees data returned by the encode/decode functions.
472 +
473 +@end itemize
474 +
475 +Since you could receive or send fragmented data (i. e. due to a too
476 +small buffer passed to @code{recv}) all of these encode/decode
477 +functions require a pointer to a @code{struct MHD_WebSocketStream} passed
478 +as argument.
479 +In this structure @emph{libmicrohttpd_ws} stores information
480 +about encoding/decoding of the particular websocket.
481 +For each websocket you need a unique @code{struct MHD_WebSocketStream}
482 +to encode/decode with this library.
483 +
484 +To create or destroy @code{struct MHD_WebSocketStream}
485 +we have additional functions:
486 +
487 +@itemize @bullet
488 +@item
489 +@code{MHD_websocket_stream_init()} allocates and initializes
490 +a new @code{struct MHD_WebSocketStream}.
491 +You can specify some options here to alter the behavior of the websocket stream.
492 +
493 +@item
494 +@code{MHD_websocket_stream_free()} frees a previously allocated
495 +@code{struct MHD_WebSocketStream}.
496 +
497 +@end itemize
498 +
499 +With these encode/decode functions we can improve our @code{upgrade_handler}
500 +callback function from an earlier example to a working websocket:
501 +
502 +
503 +@verbatim
504 +static void
505 +upgrade_handler (void *cls,
506 +                 struct MHD_Connection *connection,
507 +                 void *con_cls,
508 +                 const char *extra_in,
509 +                 size_t extra_in_size,
510 +                 MHD_socket fd,
511 +                 struct MHD_UpgradeResponseHandle *urh)
512 +{
513 +  /* make the socket blocking (operating-system-dependent code) */
514 +  make_blocking (fd);
515 +
516 +  /* create a websocket stream for this connection */
517 +  struct MHD_WebSocketStream* ws;
518 +  int result = MHD_websocket_stream_init (&ws,
519 +                                          0,
520 +                                          0);
521 +  if (0 != result)
522 +  {
523 +    /* Couldn't create the websocket stream.
524 +     * So we close the socket and leave
525 +     */
526 +    MHD_upgrade_action (urh,
527 +                        MHD_UPGRADE_ACTION_CLOSE);
528 +    return;
529 +  }
530 +
531 +  /* Let's wait for incoming data */
532 +  const size_t buf_len = 256;
533 +  char buf[buf_len];
534 +  ssize_t got;
535 +  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
536 +  {
537 +    got = recv (fd,
538 +                buf,
539 +                buf_len,
540 +                0);
541 +    if (0 >= got)
542 +    {
543 +      /* the TCP/IP socket has been closed */
544 +      break;
545 +    }
546 +
547 +    /* parse the entire received data */
548 +    size_t buf_offset = 0;
549 +    while (buf_offset < (size_t) got)
550 +    {
551 +      size_t new_offset = 0;
552 +      char *frame_data = NULL;
553 +      size_t frame_len  = 0;
554 +      int status = MHD_websocket_decode (ws,
555 +                                         buf + buf_offset,
556 +                                         ((size_t) got) - buf_offset,
557 +                                         &new_offset,
558 +                                         &frame_data,
559 +                                         &frame_len);
560 +      if (0 > status)
561 +      {
562 +        /* an error occurred and the connection must be closed */
563 +        if (NULL != frame_data)
564 +        {
565 +          MHD_websocket_free (ws, frame_data);
566 +        }
567 +        break;
568 +      }
569 +      else
570 +      {
571 +        buf_offset += new_offset;
572 +        if (0 < status)
573 +        {
574 +          /* the frame is complete */
575 +          switch (status)
576 +          {
577 +          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
578 +            /* The client has sent some text.
579 +             * We will display it and answer with a text frame.
580 +             */
581 +            if (NULL != frame_data)
582 +            {
583 +              printf ("Received message: %s\n", frame_data);
584 +              MHD_websocket_free (ws, frame_data);
585 +              frame_data = NULL;
586 +            }
587 +            result = MHD_websocket_encode_text (ws,
588 +                                                "Hello",
589 +                                                5,  /* length of "Hello" */
590 +                                                0,
591 +                                                &frame_data,
592 +                                                &frame_len,
593 +                                                NULL);
594 +            if (0 == result)
595 +            {
596 +              send_all (fd,
597 +                        frame_data,
598 +                        frame_len);
599 +            }
600 +            break;
601 +
602 +          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
603 +            /* if we receive a close frame, we will respond with one */
604 +            MHD_websocket_free (ws,
605 +                                frame_data);
606 +            frame_data = NULL;
607 +
608 +            result = MHD_websocket_encode_close (ws,
609 +                                                 0,
610 +                                                 NULL,
611 +                                                 0,
612 +                                                 &frame_data,
613 +                                                 &frame_len);
614 +            if (0 == result)
615 +            {
616 +              send_all (fd,
617 +                        frame_data,
618 +                        frame_len);
619 +            }
620 +            break;
621 +
622 +          case MHD_WEBSOCKET_STATUS_PING_FRAME:
623 +            /* if we receive a ping frame, we will respond */
624 +            /* with the corresponding pong frame */
625 +            {
626 +              char *pong = NULL;
627 +              size_t pong_len = 0;
628 +              result = MHD_websocket_encode_pong (ws,
629 +                                                  frame_data,
630 +                                                  frame_len,
631 +                                                  &pong,
632 +                                                  &pong_len);
633 +              if (0 == result)
634 +              {
635 +                send_all (fd,
636 +                          pong,
637 +                          pong_len);
638 +              }
639 +              MHD_websocket_free (ws,
640 +                                  pong);
641 +            }
642 +            break;
643 +
644 +          default:
645 +            /* Other frame types are ignored
646 +             * in this minimal example.
647 +             * This is valid, because they become
648 +             * automatically skipped if we receive them unexpectedly
649 +             */
650 +            break;
651 +          }
652 +        }
653 +        if (NULL != frame_data)
654 +        {
655 +          MHD_websocket_free (ws, frame_data);
656 +        }
657 +      }
658 +    }
659 +  }
660 +
661 +  /* free the websocket stream */
662 +  MHD_websocket_stream_free (ws);
663 +
664 +  /* close the socket when it is not needed anymore */
665 +  MHD_upgrade_action (urh,
666 +                      MHD_UPGRADE_ACTION_CLOSE);
667 +}
668 +
669 +/* This helper function is used for the case that
670 + * we need to resend some data
671 + */
672 +static void
673 +send_all (MHD_socket fd,
674 +          const char *buf,
675 +          size_t len)
676 +{
677 +  ssize_t ret;
678 +  size_t off;
679 +
680 +  for (off = 0; off < len; off += ret)
681 +  {
682 +    ret = send (fd,
683 +                &buf[off],
684 +                (int) (len - off),
685 +                0);
686 +    if (0 > ret)
687 +    {
688 +      if (EAGAIN == errno)
689 +      {
690 +        ret = 0;
691 +        continue;
692 +      }
693 +      break;
694 +    }
695 +    if (0 == ret)
696 +      break;
697 +  }
698 +}
699 +
700 +/* This helper function contains operating-system-dependent code and
701 + * is used to make a socket blocking.
702 + */
703 +static void
704 +make_blocking (MHD_socket fd)
705 +{
706 +#if defined(MHD_POSIX_SOCKETS)
707 +  int flags;
708 +
709 +  flags = fcntl (fd, F_GETFL);
710 +  if (-1 == flags)
711 +    return;
712 +  if ((flags & ~O_NONBLOCK) != flags)
713 +    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
714 +      abort ();
715 +#elif defined(MHD_WINSOCK_SOCKETS)
716 +  unsigned long flags = 0;
717 +
718 +  ioctlsocket (fd, FIONBIO, &flags);
719 +#endif /* MHD_WINSOCK_SOCKETS */
720 +}
721 +
722 +@end verbatim
723 +@noindent
724 +
725 +
726 +Please note that the websocket in this example is only half-duplex.
727 +It waits until the blocking @code{recv()} call returns and
728 +only does then something.
729 +In this example all frame types are decoded by @emph{libmicrohttpd_ws},
730 +but we only do something when a text, ping or close frame is received.
731 +Binary and pong frames are ignored in our code.
732 +This is legit, because the server is only required to implement at
733 +least support for ping frame or close frame (the other frame types
734 +could be skipped in theory, because they don't require an answer).
735 +The pong frame doesn't require an answer and whether text frames or
736 +binary frames get an answer simply belongs to your server application.
737 +So this is a valid minimal example.
738 +
739 +Until this point you've learned everything you need to basically
740 +use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
741 +These libraries offer much more functions for some specific cases.
742 +
743 +
744 +The further chapters of this tutorial focus on some specific problems
745 +and the client site programming.
746 +
747 +
748 +@heading Using full-duplex websockets
749 +
750 +To use full-duplex websockets you can simply create two threads
751 +per websocket connection.
752 +One of these threads is used for receiving data with
753 +a blocking @code{recv()} call and the other thread is triggered
754 +by the application internal codes and sends the data.
755 +
756 +A full-duplex websocket example is implemented in the example file
757 +@code{websocket_chatserver_example.c}.
758 +
759 +@heading Error handling
760 +
761 +The most functions of @emph{libmicrohttpd_ws} return a value
762 +of @code{enum MHD_WEBSOCKET_STATUS}.
763 +The values of this enumeration can be converted into an integer
764 +and have an easy interpretation:
765 +
766 +@itemize @bullet
767 +@item
768 +If the value is less than zero an error occurred and the call has failed.
769 +Check the enumeration values for more specific information.
770 +
771 +@item
772 +If the value is equal to zero, the call succeeded.
773 +
774 +@item
775 +If the value is greater than zero, the call succeeded and the value
776 +specifies the decoded frame type.
777 +Currently positive values are only returned by @code{MHD_websocket_decode()}
778 +(of the functions with this return enumeration type).
779 +
780 +@end itemize
781 +
782 +A websocket stream can also get broken when invalid frame data is received.
783 +Also the other site could send a close frame which puts the stream into
784 +a state where it may not be used for regular communication.
785 +Whether a stream has become broken, can be checked with
786 +@code{MHD_websocket_stream_is_valid()}.
787 +
788 +
789 +@heading Fragmentation
790 +
791 +In addition to the regular TCP/IP fragmentation the websocket protocol also
792 +supports fragmentation.
793 +Fragmentation could be used for continuous payload data such as video data
794 +from a webcam.
795 +Whether or not you want to receive fragmentation is specified upon
796 +initialization of the websocket stream.
797 +If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
798 +of @code{MHD_websocket_stream_init()} then you can receive fragments.
799 +If you don't pass this flag (in the most cases you just pass zero as flags)
800 +then you don't want to handle fragments on your own.
801 +@emph{libmicrohttpd_ws} removes then the fragmentation for you
802 +in the background.
803 +You only get the completely assembled frames.
804 +
805 +Upon encoding you specify whether or not you want to create a fragmented frame
806 +by passing a flag to the corresponding encode function.
807 +Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
808 +can be used for fragmentation, because the other frame types may
809 +not be fragmented.
810 +Encoding fragmented frames is independent of
811 +the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
812 +
813 +@heading Quick guide to websockets in JavaScript
814 +
815 +Websockets are supported in all modern web browsers.
816 +You initialize a websocket connection by creating an instance of
817 +the @code{WebSocket} class provided by the web browser.
818 +
819 +There are some simple rules for using websockets in the browser:
820 +
821 +@itemize @bullet
822 +@item
823 +When you initialize the instance of the websocket class you must pass an URL.
824 +The URL must either start with @code{ws://}
825 +(for not encrypted websocket protocol) or @code{wss://}
826 +(for TLS-encrypted websocket protocol).
827 +
828 +@strong{IMPORTANT:} If your website is accessed via @code{https://}
829 +then you are in a security context, which means that you are only allowed to
830 +access other secure protocols.
831 +So you can only use @code{wss://} for websocket connections then.
832 +If you try to @code{ws://} instead then your websocket connection will
833 +automatically fail.
834 +
835 +@item
836 +The WebSocket class uses events to handle the receiving of data.
837 +JavaScript is per definition a single-threaded language so
838 +the receiving events will never overlap.
839 +Sending is done directly by calling a method of the instance of
840 +the WebSocket class.
841 +
842 +@end itemize
843 +
844 +
845 +Here is a short example for receiving/sending data to the same host
846 +as the website is running on:
847 +
848 +@verbatim
849 +<!DOCTYPE html>
850 +<html>
851 +<head>
852 +<meta charset="UTF-8">
853 +<title>Websocket Demo</title>
854 +<script>
855 +
856 +let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
857 +          window.location.host + '/chat';
858 +let socket = null;
859 +
860 +window.onload = function(event) {
861 +  socket = new WebSocket(url);
862 +  socket.onopen = function(event) {
863 +    document.write('The websocket connection has been established.<br>');
864 +
865 +    // Send some text
866 +    socket.send('Hello from JavaScript!');
867 +  }
868 +
869 +  socket.onclose = function(event) {
870 +    document.write('The websocket connection has been closed.<br>');
871 +  }
872 +
873 +  socket.onerror = function(event) {
874 +    document.write('An error occurred during the websocket communication.<br>');
875 +  }
876 +
877 +  socket.onmessage = function(event) {
878 +    document.write('Websocket message received: ' + event.data + '<br>');
879 +  }
880 +}
881 +
882 +</script>
883 +</head>
884 +<body>
885 +</body>
886 +</html>
887 +
888 +@end verbatim
889 +@noindent
890 --- libmicrohttpd-0.9.74/doc/examples/websocket.c.orig  1970-01-01 01:00:00.000000000 +0100
891 +++ libmicrohttpd-0.9.74/doc/examples/websocket.c       2021-12-23 22:49:44.536725673 +0100
892 @@ -0,0 +1,452 @@
893 +/* Feel free to use this example code in any way
894 +   you see fit (Public Domain) */
895 +
896 +#include <sys/types.h>
897 +#ifndef _WIN32
898 +#include <sys/select.h>
899 +#include <sys/socket.h>
900 +#include <fcntl.h>
901 +#else
902 +#include <winsock2.h>
903 +#endif
904 +#include <microhttpd.h>
905 +#include <microhttpd_ws.h>
906 +#include <time.h>
907 +#include <string.h>
908 +#include <stdlib.h>
909 +#include <stdio.h>
910 +#include <errno.h>
911 +
912 +#define PORT 80
913 +
914 +#define PAGE \
915 +  "<!DOCTYPE html>\n" \
916 +  "<html>\n" \
917 +  "<head>\n" \
918 +  "<meta charset=\"UTF-8\">\n" \
919 +  "<title>Websocket Demo</title>\n" \
920 +  "<script>\n" \
921 +  "\n" \
922 +  "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
923 +    "  + '://' +\n" \
924 +  "          window.location.host + '/chat';\n" \
925 +  "let socket = null;\n" \
926 +  "\n" \
927 +  "window.onload = function(event) {\n" \
928 +  "  socket = new WebSocket(url);\n" \
929 +  "  socket.onopen = function(event) {\n" \
930 +  "    document.write('The websocket connection has been " \
931 +    "established.<br>');\n" \
932 +  "\n" \
933 +  "    // Send some text\n" \
934 +  "    socket.send('Hello from JavaScript!');\n" \
935 +  "  }\n" \
936 +  "\n" \
937 +  "  socket.onclose = function(event) {\n" \
938 +  "    document.write('The websocket connection has been closed.<br>');\n" \
939 +  "  }\n" \
940 +  "\n" \
941 +  "  socket.onerror = function(event) {\n" \
942 +  "    document.write('An error occurred during the websocket " \
943 +    "communication.<br>');\n" \
944 +  "  }\n" \
945 +  "\n" \
946 +  "  socket.onmessage = function(event) {\n" \
947 +  "    document.write('Websocket message received: ' + " \
948 +    "event.data + '<br>');\n" \
949 +  "  }\n" \
950 +  "}\n" \
951 +  "\n" \
952 +  "</script>\n" \
953 +  "</head>\n" \
954 +  "<body>\n" \
955 +  "</body>\n" \
956 +  "</html>"
957 +
958 +#define PAGE_NOT_FOUND \
959 +  "404 Not Found"
960 +
961 +#define PAGE_INVALID_WEBSOCKET_REQUEST \
962 +  "Invalid WebSocket request!"
963 +
964 +static void
965 +send_all (MHD_socket fd,
966 +          const char *buf,
967 +          size_t len);
968 +
969 +static void
970 +make_blocking (MHD_socket fd);
971 +
972 +static void
973 +upgrade_handler (void *cls,
974 +                 struct MHD_Connection *connection,
975 +                 void *con_cls,
976 +                 const char *extra_in,
977 +                 size_t extra_in_size,
978 +                 MHD_socket fd,
979 +                 struct MHD_UpgradeResponseHandle *urh)
980 +{
981 +  /* make the socket blocking (operating-system-dependent code) */
982 +  make_blocking (fd);
983 +
984 +  /* create a websocket stream for this connection */
985 +  struct MHD_WebSocketStream *ws;
986 +  int result = MHD_websocket_stream_init (&ws,
987 +                                          0,
988 +                                          0);
989 +  if (0 != result)
990 +  {
991 +    /* Couldn't create the websocket stream.
992 +     * So we close the socket and leave
993 +     */
994 +    MHD_upgrade_action (urh,
995 +                        MHD_UPGRADE_ACTION_CLOSE);
996 +    return;
997 +  }
998 +
999 +  /* Let's wait for incoming data */
1000 +  const size_t buf_len = 256;
1001 +  char buf[buf_len];
1002 +  ssize_t got;
1003 +  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
1004 +  {
1005 +    got = recv (fd,
1006 +                buf,
1007 +                buf_len,
1008 +                0);
1009 +    if (0 >= got)
1010 +    {
1011 +      /* the TCP/IP socket has been closed */
1012 +      break;
1013 +    }
1014 +
1015 +    /* parse the entire received data */
1016 +    size_t buf_offset = 0;
1017 +    while (buf_offset < (size_t) got)
1018 +    {
1019 +      size_t new_offset = 0;
1020 +      char *frame_data = NULL;
1021 +      size_t frame_len  = 0;
1022 +      int status = MHD_websocket_decode (ws,
1023 +                                         buf + buf_offset,
1024 +                                         ((size_t) got) - buf_offset,
1025 +                                         &new_offset,
1026 +                                         &frame_data,
1027 +                                         &frame_len);
1028 +      if (0 > status)
1029 +      {
1030 +        /* an error occurred and the connection must be closed */
1031 +        if (NULL != frame_data)
1032 +        {
1033 +          MHD_websocket_free (ws, frame_data);
1034 +        }
1035 +        break;
1036 +      }
1037 +      else
1038 +      {
1039 +        buf_offset += new_offset;
1040 +        if (0 < status)
1041 +        {
1042 +          /* the frame is complete */
1043 +          switch (status)
1044 +          {
1045 +          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
1046 +            /* The client has sent some text.
1047 +             * We will display it and answer with a text frame.
1048 +             */
1049 +            if (NULL != frame_data)
1050 +            {
1051 +              printf ("Received message: %s\n", frame_data);
1052 +              MHD_websocket_free (ws, frame_data);
1053 +              frame_data = NULL;
1054 +            }
1055 +            result = MHD_websocket_encode_text (ws,
1056 +                                                "Hello",
1057 +                                                5,  /* length of "Hello" */
1058 +                                                0,
1059 +                                                &frame_data,
1060 +                                                &frame_len,
1061 +                                                NULL);
1062 +            if (0 == result)
1063 +            {
1064 +              send_all (fd,
1065 +                        frame_data,
1066 +                        frame_len);
1067 +            }
1068 +            break;
1069 +
1070 +          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
1071 +            /* if we receive a close frame, we will respond with one */
1072 +            MHD_websocket_free (ws,
1073 +                                frame_data);
1074 +            frame_data = NULL;
1075 +
1076 +            result = MHD_websocket_encode_close (ws,
1077 +                                                 0,
1078 +                                                 NULL,
1079 +                                                 0,
1080 +                                                 &frame_data,
1081 +                                                 &frame_len);
1082 +            if (0 == result)
1083 +            {
1084 +              send_all (fd,
1085 +                        frame_data,
1086 +                        frame_len);
1087 +            }
1088 +            break;
1089 +
1090 +          case MHD_WEBSOCKET_STATUS_PING_FRAME:
1091 +            /* if we receive a ping frame, we will respond */
1092 +            /* with the corresponding pong frame */
1093 +            {
1094 +              char *pong = NULL;
1095 +              size_t pong_len = 0;
1096 +              result = MHD_websocket_encode_pong (ws,
1097 +                                                  frame_data,
1098 +                                                  frame_len,
1099 +                                                  &pong,
1100 +                                                  &pong_len);
1101 +              if (0 == result)
1102 +              {
1103 +                send_all (fd,
1104 +                          pong,
1105 +                          pong_len);
1106 +              }
1107 +              MHD_websocket_free (ws,
1108 +                                  pong);
1109 +            }
1110 +            break;
1111 +
1112 +          default:
1113 +            /* Other frame types are ignored
1114 +             * in this minimal example.
1115 +             * This is valid, because they become
1116 +             * automatically skipped if we receive them unexpectedly
1117 +             */
1118 +            break;
1119 +          }
1120 +        }
1121 +        if (NULL != frame_data)
1122 +        {
1123 +          MHD_websocket_free (ws, frame_data);
1124 +        }
1125 +      }
1126 +    }
1127 +  }
1128 +
1129 +  /* free the websocket stream */
1130 +  MHD_websocket_stream_free (ws);
1131 +
1132 +  /* close the socket when it is not needed anymore */
1133 +  MHD_upgrade_action (urh,
1134 +                      MHD_UPGRADE_ACTION_CLOSE);
1135 +}
1136 +
1137 +
1138 +/* This helper function is used for the case that
1139 + * we need to resend some data
1140 + */
1141 +static void
1142 +send_all (MHD_socket fd,
1143 +          const char *buf,
1144 +          size_t len)
1145 +{
1146 +  ssize_t ret;
1147 +  size_t off;
1148 +
1149 +  for (off = 0; off < len; off += ret)
1150 +  {
1151 +    ret = send (fd,
1152 +                &buf[off],
1153 +                (int) (len - off),
1154 +                0);
1155 +    if (0 > ret)
1156 +    {
1157 +      if (EAGAIN == errno)
1158 +      {
1159 +        ret = 0;
1160 +        continue;
1161 +      }
1162 +      break;
1163 +    }
1164 +    if (0 == ret)
1165 +      break;
1166 +  }
1167 +}
1168 +
1169 +
1170 +/* This helper function contains operating-system-dependent code and
1171 + * is used to make a socket blocking.
1172 + */
1173 +static void
1174 +make_blocking (MHD_socket fd)
1175 +{
1176 +#ifndef _WIN32
1177 +  int flags;
1178 +
1179 +  flags = fcntl (fd, F_GETFL);
1180 +  if (-1 == flags)
1181 +    abort ();
1182 +  if ((flags & ~O_NONBLOCK) != flags)
1183 +    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
1184 +      abort ();
1185 +#else  /* _WIN32 */
1186 +  unsigned long flags = 0;
1187 +
1188 +  if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
1189 +    abort ();
1190 +#endif /* _WIN32 */
1191 +}
1192 +
1193 +
1194 +static enum MHD_Result
1195 +access_handler (void *cls,
1196 +                struct MHD_Connection *connection,
1197 +                const char *url,
1198 +                const char *method,
1199 +                const char *version,
1200 +                const char *upload_data,
1201 +                size_t *upload_data_size,
1202 +                void **ptr)
1203 +{
1204 +  static int aptr;
1205 +  struct MHD_Response *response;
1206 +  int ret;
1207 +
1208 +  (void) cls;               /* Unused. Silent compiler warning. */
1209 +  (void) upload_data;       /* Unused. Silent compiler warning. */
1210 +  (void) upload_data_size;  /* Unused. Silent compiler warning. */
1211 +
1212 +  if (0 != strcmp (method, "GET"))
1213 +    return MHD_NO;              /* unexpected method */
1214 +  if (&aptr != *ptr)
1215 +  {
1216 +    /* do never respond on first call */
1217 +    *ptr = &aptr;
1218 +    return MHD_YES;
1219 +  }
1220 +  *ptr = NULL;                  /* reset when done */
1221 +
1222 +  if (0 == strcmp (url, "/"))
1223 +  {
1224 +    /* Default page for visiting the server */
1225 +    struct MHD_Response *response = MHD_create_response_from_buffer (
1226 +      strlen (PAGE),
1227 +      PAGE,
1228 +      MHD_RESPMEM_PERSISTENT);
1229 +    ret = MHD_queue_response (connection,
1230 +                              MHD_HTTP_OK,
1231 +                              response);
1232 +    MHD_destroy_response (response);
1233 +  }
1234 +  else if (0 == strcmp (url, "/chat"))
1235 +  {
1236 +    char is_valid = 1;
1237 +    const char *value = NULL;
1238 +    char sec_websocket_accept[29];
1239 +
1240 +    if (0 != MHD_websocket_check_http_version (version))
1241 +    {
1242 +      is_valid = 0;
1243 +    }
1244 +    value = MHD_lookup_connection_value (connection,
1245 +                                         MHD_HEADER_KIND,
1246 +                                         MHD_HTTP_HEADER_CONNECTION);
1247 +    if (0 != MHD_websocket_check_connection_header (value))
1248 +    {
1249 +      is_valid = 0;
1250 +    }
1251 +    value = MHD_lookup_connection_value (connection,
1252 +                                         MHD_HEADER_KIND,
1253 +                                         MHD_HTTP_HEADER_UPGRADE);
1254 +    if (0 != MHD_websocket_check_upgrade_header (value))
1255 +    {
1256 +      is_valid = 0;
1257 +    }
1258 +    value = MHD_lookup_connection_value (connection,
1259 +                                         MHD_HEADER_KIND,
1260 +                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
1261 +    if (0 != MHD_websocket_check_version_header (value))
1262 +    {
1263 +      is_valid = 0;
1264 +    }
1265 +    value = MHD_lookup_connection_value (connection,
1266 +                                         MHD_HEADER_KIND,
1267 +                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
1268 +    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
1269 +    {
1270 +      is_valid = 0;
1271 +    }
1272 +
1273 +    if (1 == is_valid)
1274 +    {
1275 +      /* upgrade the connection */
1276 +      response = MHD_create_response_for_upgrade (&upgrade_handler,
1277 +                                                  NULL);
1278 +      MHD_add_response_header (response,
1279 +                               MHD_HTTP_HEADER_CONNECTION,
1280 +                               "Upgrade");
1281 +      MHD_add_response_header (response,
1282 +                               MHD_HTTP_HEADER_UPGRADE,
1283 +                               "websocket");
1284 +      MHD_add_response_header (response,
1285 +                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
1286 +                               sec_websocket_accept);
1287 +      ret = MHD_queue_response (connection,
1288 +                                MHD_HTTP_SWITCHING_PROTOCOLS,
1289 +                                response);
1290 +      MHD_destroy_response (response);
1291 +    }
1292 +    else
1293 +    {
1294 +      /* return error page */
1295 +      struct MHD_Response *response = MHD_create_response_from_buffer (
1296 +        strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
1297 +        PAGE_INVALID_WEBSOCKET_REQUEST,
1298 +        MHD_RESPMEM_PERSISTENT);
1299 +      ret = MHD_queue_response (connection,
1300 +                                MHD_HTTP_BAD_REQUEST,
1301 +                                response);
1302 +      MHD_destroy_response (response);
1303 +    }
1304 +  }
1305 +  else
1306 +  {
1307 +    struct MHD_Response *response = MHD_create_response_from_buffer (
1308 +      strlen (PAGE_NOT_FOUND),
1309 +      PAGE_NOT_FOUND,
1310 +      MHD_RESPMEM_PERSISTENT);
1311 +    ret = MHD_queue_response (connection,
1312 +                              MHD_HTTP_NOT_FOUND,
1313 +                              response);
1314 +    MHD_destroy_response (response);
1315 +  }
1316 +
1317 +  return ret;
1318 +}
1319 +
1320 +
1321 +int
1322 +main (int argc,
1323 +      char *const *argv)
1324 +{
1325 +  (void) argc;               /* Unused. Silent compiler warning. */
1326 +  (void) argv;               /* Unused. Silent compiler warning. */
1327 +  struct MHD_Daemon *daemon;
1328 +
1329 +  daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
1330 +                             | MHD_USE_THREAD_PER_CONNECTION
1331 +                             | MHD_ALLOW_UPGRADE
1332 +                             | MHD_USE_ERROR_LOG,
1333 +                             PORT, NULL, NULL,
1334 +                             &access_handler, NULL,
1335 +                             MHD_OPTION_END);
1336 +
1337 +  if (NULL == daemon)
1338 +    return 1;
1339 +  (void) getc (stdin);
1340 +
1341 +  MHD_stop_daemon (daemon);
1342 +
1343 +  return 0;
1344 +}
This page took 0.157444 seconds and 3 git commands to generate.