[packages/libmicrohttpd] - updated to 0.9.74 - added missing patch (two missing files from git)

qboosh qboosh at pld-linux.org
Fri Dec 24 11:11:34 CET 2021


commit 6fc60a3e1d13b92611226a9f8683f0641e00d9b5
Author: Jakub Bogusz <qboosh at pld-linux.org>
Date:   Fri Dec 24 11:12:17 2021 +0100

    - updated to 0.9.74
    - added missing patch (two missing files from git)

 libmicrohttpd-missing.patch | 1344 +++++++++++++++++++++++++++++++++++++++++++
 libmicrohttpd.spec          |    7 +-
 2 files changed, 1349 insertions(+), 2 deletions(-)
---
diff --git a/libmicrohttpd.spec b/libmicrohttpd.spec
index aec94bd..0f453b2 100644
--- a/libmicrohttpd.spec
+++ b/libmicrohttpd.spec
@@ -5,13 +5,14 @@
 Summary:	Embeded HTTP server library
 Summary(pl.UTF-8):	Biblioteka wbudowanego serwera HTTP
 Name:		libmicrohttpd
-Version:	0.9.73
+Version:	0.9.74
 Release:	1
 License:	LGPL v2.1+
 Group:		Libraries
 Source0:	https://ftp.gnu.org/gnu/libmicrohttpd/%{name}-%{version}.tar.gz
-# Source0-md5:	2b15949b1633e4fa487e08cdcc97f0e3
+# Source0-md5:	93cdd74fc8b807215c93a9a4f2346f86
 Patch0:		%{name}-info.patch
+Patch1:		%{name}-missing.patch
 URL:		http://www.gnu.org/software/libmicrohttpd/
 BuildRequires:	autoconf >= 2.64
 BuildRequires:	automake >= 1:1.11
@@ -20,6 +21,7 @@ BuildRequires:	gnutls-devel >= 2.8.6
 BuildRequires:	libgcrypt-devel >= 1.2.4
 BuildRequires:	libmagic-devel
 BuildRequires:	libtool >= 2:2.4.0
+BuildRequires:	pkgconfig
 BuildRequires:	texinfo
 %if %{with tests}
 BuildRequires:	curl-devel >= 7.16.4
@@ -66,6 +68,7 @@ Biblioteka statyczna libmicrohttpd.
 %prep
 %setup -q
 %patch0 -p1
+%patch1 -p1
 
 %build
 %{__libtoolize}
diff --git a/libmicrohttpd-missing.patch b/libmicrohttpd-missing.patch
new file mode 100644
index 0000000..b47fd08
--- /dev/null
+++ b/libmicrohttpd-missing.patch
@@ -0,0 +1,1344 @@
+--- libmicrohttpd-0.9.74/doc/chapters/websocket.inc.orig	1970-01-01 01:00:00.000000000 +0100
++++ libmicrohttpd-0.9.74/doc/chapters/websocket.inc	2021-12-23 21:39:46.569468012 +0100
+@@ -0,0 +1,886 @@
++Websockets are a genuine way to implement push notifications,
++where the server initiates the communication while the client can be idle.
++Usually a HTTP communication is half-duplex and always requested by the client,
++but websockets are full-duplex and only initialized by the client.
++In the further communication both sites can use the websocket at any time
++to send data to the other site.
++
++To initialize a websocket connection the client sends a special HTTP request
++to the server and initializes
++a handshake between client and server which switches from the HTTP protocol
++to the websocket protocol.
++Thus both the server as well as the client must support websockets.
++If proxys are used, they must support websockets too.
++In this chapter we take a look on server and client, but with a focus on
++the server with @emph{libmicrohttpd}.
++
++Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
++which is required for switching from the HTTP protocol.
++Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
++to support the websocket protocol.
++
++ at heading Upgrading connections with libmicrohttpd
++
++To support websockets we need to enable upgrading of HTTP connections first.
++This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
++ at code{MHD_start_daemon()}.
++
++
++ at verbatim
++daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
++                           MHD_USE_THREAD_PER_CONNECTION |
++                           MHD_ALLOW_UPGRADE |
++                           MHD_USE_ERROR_LOG,
++                           PORT, NULL, NULL,
++                           &access_handler, NULL,
++                           MHD_OPTION_END);
++ at end verbatim
++ at noindent
++
++
++The next step is to turn a specific request into an upgraded connection.
++This done in our @code{access_handler} by calling
++ at code{MHD_create_response_for_upgrade()}.
++An @code{upgrade_handler} will be passed to perform the low-level actions
++on the socket.
++
++ at emph{Please note that the socket here is just a regular socket as provided
++by the operating system.
++To use it as a websocket, some more steps from the following
++chapters are required.}
++
++
++ at verbatim
++static enum MHD_Result
++access_handler (void *cls,
++                struct MHD_Connection *connection,
++                const char *url,
++                const char *method,
++                const char *version,
++                const char *upload_data,
++                size_t *upload_data_size,
++                void **ptr)
++{
++  /* ... */
++  /* some code to decide whether to upgrade or not */
++  /* ... */
++
++  /* create the response for upgrade */
++  response = MHD_create_response_for_upgrade (&upgrade_handler,
++                                              NULL);
++
++  /* ... */
++  /* additional headers, etc. */
++  /* ... */
++
++  ret = MHD_queue_response (connection,
++                            MHD_HTTP_SWITCHING_PROTOCOLS,
++                            response);
++  MHD_destroy_response (response);
++
++  return ret;
++}
++ at end verbatim
++ at noindent
++
++
++In the @code{upgrade_handler} we receive the low-level socket,
++which is used for the communication with the specific client.
++In addition to the low-level socket we get:
++ at itemize @bullet
++ at item
++Some data, which has been read too much while @emph{libmicrohttpd} was
++switching the protocols.
++This value is usually empty, because it would mean that the client
++has sent data before the handshake was complete.
++
++ at item
++A @code{struct MHD_UpgradeResponseHandle} which is used to perform
++special actions like closing, corking or uncorking the socket.
++These commands are executed by passing the handle
++to @code{MHD_upgrade_action()}.
++
++
++ at end itemize
++
++Depending of the flags specified while calling @code{MHD_start_deamon()}
++our @code{upgrade_handler} is either executed in the same thread
++as our deamon or in a thread specific for each connection.
++If it is executed in the same thread then @code{upgrade_handler} is
++a blocking call for our webserver and
++we should finish it as fast as possible (i. e. by creating a thread and
++passing the information there).
++If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
++ at code{MHD_start_daemon()} then a separate thread is used and
++thus our @code{upgrade_handler} needs not to start a separate thread.
++
++An @code{upgrade_handler}, which is called with a separate thread
++per connection, could look like this:
++
++
++ at verbatim
++static void
++upgrade_handler (void *cls,
++                 struct MHD_Connection *connection,
++                 void *con_cls,
++                 const char *extra_in,
++                 size_t extra_in_size,
++                 MHD_socket fd,
++                 struct MHD_UpgradeResponseHandle *urh)
++{
++  /* ... */
++  /* do something with the socket `fd` like `recv()` or `send()` */
++  /* ... */
++
++  /* close the socket when it is not needed anymore */
++  MHD_upgrade_action (urh,
++                      MHD_UPGRADE_ACTION_CLOSE);
++}
++ at end verbatim
++ at noindent
++
++
++This is all you need to know for upgrading connections
++with @emph{libmicrohttpd}.
++The next chapters focus on using the websocket protocol
++with @emph{libmicrohttpd_ws}.
++
++
++ at heading Websocket handshake with libmicrohttpd_ws
++
++To request a websocket connection the client must send
++the following information with the HTTP request:
++
++ at itemize @bullet
++ at item
++A @code{GET} request must be sent.
++
++ at item
++The version of the HTTP protocol must be 1.1 or higher.
++
++ at item
++A @code{Host} header field must be sent
++
++ at item
++A @code{Upgrade} header field containing the keyword "websocket"
++(case-insensitive).
++Please note that the client could pass multiple protocols separated by comma.
++
++ at item
++A @code{Connection} header field that includes the token "Upgrade"
++(case-insensitive).
++Please note that the client could pass multiple tokens separated by comma.
++
++ at item
++A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
++The decoded the value is 16 bytes long
++and has been generated randomly by the client.
++
++ at item
++A @code{Sec-WebSocket-Version} header field with the value "13".
++
++ at end itemize
++
++
++Optionally the client can also send the following information:
++
++
++ at itemize @bullet
++ at item
++A @code{Origin} header field can be used to determine the source
++of the client (i. e. the website).
++
++ at item
++A @code{Sec-WebSocket-Protocol} header field can contain a list
++of supported protocols by the client, which can be sent over the websocket.
++
++ at item
++A @code{Sec-WebSocket-Extensions} header field which may contain extensions
++to the websocket protocol. The extensions must be registered by IANA.
++
++ at end itemize
++
++
++A valid example request from the client could look like this:
++
++
++ at verbatim
++GET /chat HTTP/1.1
++Host: server.example.com
++Upgrade: websocket
++Connection: Upgrade
++Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
++Sec-WebSocket-Version: 13
++ at end verbatim
++ at noindent
++
++
++To complete the handshake the server must respond with
++some specific response headers:
++
++ at itemize @bullet
++ at item
++The HTTP response code @code{101 Switching Protocols} must be answered.
++
++ at item
++An @code{Upgrade} header field containing the value "websocket" must be sent.
++
++ at item
++A @code{Connection} header field containing the value "Upgrade" must be sent.
++
++ at item
++A @code{Sec-WebSocket-Accept} header field containing a value, which
++has been calculated from the @code{Sec-WebSocket-Key} request header field,
++must be sent.
++
++ at end itemize
++
++
++Optionally the server may send following headers:
++
++
++ at itemize @bullet
++ at item
++A @code{Sec-WebSocket-Protocol} header field containing a protocol
++of the list specified in the corresponding request header field.
++
++ at item
++A @code{Sec-WebSocket-Extension} header field containing all used extensions
++of the list specified in the corresponding request header field.
++
++ at end itemize
++
++
++A valid websocket HTTP response could look like this:
++
++ at verbatim
++HTTP/1.1 101 Switching Protocols
++Upgrade: websocket
++Connection: Upgrade
++Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
++ at end verbatim
++ at noindent
++
++
++To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
++some helper functions for the @code{access_handler} callback function:
++
++ at itemize @bullet
++ at item
++ at code{MHD_websocket_check_http_version()} checks whether the HTTP version
++is 1.1 or above.
++
++ at item
++ at code{MHD_websocket_check_connection_header()} checks whether the value
++of the @code{Connection} request header field contains
++an "Upgrade" token (case-insensitive).
++
++ at item
++ at code{MHD_websocket_check_upgrade_header()} checks whether the value
++of the @code{Upgrade} request header field contains
++the "websocket" keyword (case-insensitive).
++
++ at item
++ at code{MHD_websocket_check_version_header()} checks whether the value
++of the @code{Sec-WebSocket-Version} request header field is "13".
++
++ at item
++ at code{MHD_websocket_create_accept_header()} takes the value from
++the @code{Sec-WebSocket-Key} request header and calculates the value
++for the @code{Sec-WebSocket-Accept} response header field.
++
++ at end itemize
++
++
++The @code{access_handler} example of the previous chapter can now be
++extended with these helper functions to perform the websocket handshake:
++
++ at verbatim
++static enum MHD_Result
++access_handler (void *cls,
++                struct MHD_Connection *connection,
++                const char *url,
++                const char *method,
++                const char *version,
++                const char *upload_data,
++                size_t *upload_data_size,
++                void **ptr)
++{
++  static int aptr;
++  struct MHD_Response *response;
++  int ret;
++
++  (void) cls;               /* Unused. Silent compiler warning. */
++  (void) upload_data;       /* Unused. Silent compiler warning. */
++  (void) upload_data_size;  /* Unused. Silent compiler warning. */
++
++  if (0 != strcmp (method, "GET"))
++    return MHD_NO;              /* unexpected method */
++  if (&aptr != *ptr)
++  {
++    /* do never respond on first call */
++    *ptr = &aptr;
++    return MHD_YES;
++  }
++  *ptr = NULL;                  /* reset when done */
++
++  if (0 == strcmp (url, "/"))
++  {
++    /* Default page for visiting the server */
++    struct MHD_Response *response = MHD_create_response_from_buffer (
++                                      strlen (PAGE),
++                                      PAGE,
++                                      MHD_RESPMEM_PERSISTENT);
++    ret = MHD_queue_response (connection,
++                              MHD_HTTP_OK,
++                              response);
++    MHD_destroy_response (response);
++  }
++  else if (0 == strcmp (url, "/chat"))
++  {
++    char is_valid = 1;
++    const char* value = NULL;
++    char sec_websocket_accept[29];
++
++    if (0 != MHD_websocket_check_http_version (version))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_CONNECTION);
++    if (0 != MHD_websocket_check_connection_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_UPGRADE);
++    if (0 != MHD_websocket_check_upgrade_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
++    if (0 != MHD_websocket_check_version_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
++    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
++    {
++      is_valid = 0;
++    }
++
++    if (1 == is_valid)
++    {
++      /* upgrade the connection */
++      response = MHD_create_response_for_upgrade (&upgrade_handler,
++                                                  NULL);
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_CONNECTION,
++                               "Upgrade");
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_UPGRADE,
++                               "websocket");
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
++                               sec_websocket_accept);
++      ret = MHD_queue_response (connection,
++                                MHD_HTTP_SWITCHING_PROTOCOLS,
++                                response);
++      MHD_destroy_response (response);
++    }
++    else
++    {
++      /* return error page */
++      struct MHD_Response*response = MHD_create_response_from_buffer (
++                                       strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
++                                       PAGE_INVALID_WEBSOCKET_REQUEST,
++                                       MHD_RESPMEM_PERSISTENT);
++      ret = MHD_queue_response (connection,
++                                MHD_HTTP_BAD_REQUEST,
++                                response);
++      MHD_destroy_response (response);
++    }
++  }
++  else
++  {
++    struct MHD_Response*response = MHD_create_response_from_buffer (
++                                     strlen (PAGE_NOT_FOUND),
++                                     PAGE_NOT_FOUND,
++                                     MHD_RESPMEM_PERSISTENT);
++    ret = MHD_queue_response (connection,
++                              MHD_HTTP_NOT_FOUND,
++                              response);
++    MHD_destroy_response (response);
++  }
++
++  return ret;
++}
++ at end verbatim
++ at noindent
++
++Please note that we skipped the check of the Host header field here,
++because we don't know the host for this example.
++
++ at heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
++
++Once the websocket connection is established you can receive/send frame data
++with the low-level socket functions @code{recv()} and @code{send()}.
++The frame data which goes over the low-level socket is encoded according
++to the websocket protocol.
++To use received payload data, you need to decode the frame data first.
++To send payload data, you need to encode it into frame data first.
++
++ at emph{libmicrohttpd_ws} provides serveral functions for encoding of
++payload data and decoding of frame data:
++
++ at itemize @bullet
++ at item
++ at code{MHD_websocket_decode()} decodes received frame data.
++The payload data may be of any kind, depending upon what the client has sent.
++So this decode function is used for all kind of frames and returns
++the frame type along with the payload data.
++
++ at item
++ at code{MHD_websocket_encode_text()} encodes text.
++The text must be encoded with UTF-8.
++
++ at item
++ at code{MHD_websocket_encode_binary()} encodes binary data.
++
++ at item
++ at code{MHD_websocket_encode_ping()} encodes a ping request to
++check whether the websocket is still valid and to test latency.
++
++ at item
++ at code{MHD_websocket_encode_ping()} encodes a pong response to
++answer a received ping request.
++
++ at item
++ at code{MHD_websocket_encode_close()} encodes a close request.
++
++ at item
++ at code{MHD_websocket_free()} frees data returned by the encode/decode functions.
++
++ at end itemize
++
++Since you could receive or send fragmented data (i. e. due to a too
++small buffer passed to @code{recv}) all of these encode/decode
++functions require a pointer to a @code{struct MHD_WebSocketStream} passed
++as argument.
++In this structure @emph{libmicrohttpd_ws} stores information
++about encoding/decoding of the particular websocket.
++For each websocket you need a unique @code{struct MHD_WebSocketStream}
++to encode/decode with this library.
++
++To create or destroy @code{struct MHD_WebSocketStream}
++we have additional functions:
++
++ at itemize @bullet
++ at item
++ at code{MHD_websocket_stream_init()} allocates and initializes
++a new @code{struct MHD_WebSocketStream}.
++You can specify some options here to alter the behavior of the websocket stream.
++
++ at item
++ at code{MHD_websocket_stream_free()} frees a previously allocated
++ at code{struct MHD_WebSocketStream}.
++
++ at end itemize
++
++With these encode/decode functions we can improve our @code{upgrade_handler}
++callback function from an earlier example to a working websocket:
++
++
++ at verbatim
++static void
++upgrade_handler (void *cls,
++                 struct MHD_Connection *connection,
++                 void *con_cls,
++                 const char *extra_in,
++                 size_t extra_in_size,
++                 MHD_socket fd,
++                 struct MHD_UpgradeResponseHandle *urh)
++{
++  /* make the socket blocking (operating-system-dependent code) */
++  make_blocking (fd);
++
++  /* create a websocket stream for this connection */
++  struct MHD_WebSocketStream* ws;
++  int result = MHD_websocket_stream_init (&ws,
++                                          0,
++                                          0);
++  if (0 != result)
++  {
++    /* Couldn't create the websocket stream.
++     * So we close the socket and leave
++     */
++    MHD_upgrade_action (urh,
++                        MHD_UPGRADE_ACTION_CLOSE);
++    return;
++  }
++
++  /* Let's wait for incoming data */
++  const size_t buf_len = 256;
++  char buf[buf_len];
++  ssize_t got;
++  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
++  {
++    got = recv (fd,
++                buf,
++                buf_len,
++                0);
++    if (0 >= got)
++    {
++      /* the TCP/IP socket has been closed */
++      break;
++    }
++
++    /* parse the entire received data */
++    size_t buf_offset = 0;
++    while (buf_offset < (size_t) got)
++    {
++      size_t new_offset = 0;
++      char *frame_data = NULL;
++      size_t frame_len  = 0;
++      int status = MHD_websocket_decode (ws,
++                                         buf + buf_offset,
++                                         ((size_t) got) - buf_offset,
++                                         &new_offset,
++                                         &frame_data,
++                                         &frame_len);
++      if (0 > status)
++      {
++        /* an error occurred and the connection must be closed */
++        if (NULL != frame_data)
++        {
++          MHD_websocket_free (ws, frame_data);
++        }
++        break;
++      }
++      else
++      {
++        buf_offset += new_offset;
++        if (0 < status)
++        {
++          /* the frame is complete */
++          switch (status)
++          {
++          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
++            /* The client has sent some text.
++             * We will display it and answer with a text frame.
++             */
++            if (NULL != frame_data)
++            {
++              printf ("Received message: %s\n", frame_data);
++              MHD_websocket_free (ws, frame_data);
++              frame_data = NULL;
++            }
++            result = MHD_websocket_encode_text (ws,
++                                                "Hello",
++                                                5,  /* length of "Hello" */
++                                                0,
++                                                &frame_data,
++                                                &frame_len,
++                                                NULL);
++            if (0 == result)
++            {
++              send_all (fd,
++                        frame_data,
++                        frame_len);
++            }
++            break;
++
++          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
++            /* if we receive a close frame, we will respond with one */
++            MHD_websocket_free (ws,
++                                frame_data);
++            frame_data = NULL;
++
++            result = MHD_websocket_encode_close (ws,
++                                                 0,
++                                                 NULL,
++                                                 0,
++                                                 &frame_data,
++                                                 &frame_len);
++            if (0 == result)
++            {
++              send_all (fd,
++                        frame_data,
++                        frame_len);
++            }
++            break;
++
++          case MHD_WEBSOCKET_STATUS_PING_FRAME:
++            /* if we receive a ping frame, we will respond */
++            /* with the corresponding pong frame */
++            {
++              char *pong = NULL;
++              size_t pong_len = 0;
++              result = MHD_websocket_encode_pong (ws,
++                                                  frame_data,
++                                                  frame_len,
++                                                  &pong,
++                                                  &pong_len);
++              if (0 == result)
++              {
++                send_all (fd,
++                          pong,
++                          pong_len);
++              }
++              MHD_websocket_free (ws,
++                                  pong);
++            }
++            break;
++
++          default:
++            /* Other frame types are ignored
++             * in this minimal example.
++             * This is valid, because they become
++             * automatically skipped if we receive them unexpectedly
++             */
++            break;
++          }
++        }
++        if (NULL != frame_data)
++        {
++          MHD_websocket_free (ws, frame_data);
++        }
++      }
++    }
++  }
++
++  /* free the websocket stream */
++  MHD_websocket_stream_free (ws);
++
++  /* close the socket when it is not needed anymore */
++  MHD_upgrade_action (urh,
++                      MHD_UPGRADE_ACTION_CLOSE);
++}
++
++/* This helper function is used for the case that
++ * we need to resend some data
++ */
++static void
++send_all (MHD_socket fd,
++          const char *buf,
++          size_t len)
++{
++  ssize_t ret;
++  size_t off;
++
++  for (off = 0; off < len; off += ret)
++  {
++    ret = send (fd,
++                &buf[off],
++                (int) (len - off),
++                0);
++    if (0 > ret)
++    {
++      if (EAGAIN == errno)
++      {
++        ret = 0;
++        continue;
++      }
++      break;
++    }
++    if (0 == ret)
++      break;
++  }
++}
++
++/* This helper function contains operating-system-dependent code and
++ * is used to make a socket blocking.
++ */
++static void
++make_blocking (MHD_socket fd)
++{
++#if defined(MHD_POSIX_SOCKETS)
++  int flags;
++
++  flags = fcntl (fd, F_GETFL);
++  if (-1 == flags)
++    return;
++  if ((flags & ~O_NONBLOCK) != flags)
++    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
++      abort ();
++#elif defined(MHD_WINSOCK_SOCKETS)
++  unsigned long flags = 0;
++
++  ioctlsocket (fd, FIONBIO, &flags);
++#endif /* MHD_WINSOCK_SOCKETS */
++}
++
++ at end verbatim
++ at noindent
++
++
++Please note that the websocket in this example is only half-duplex.
++It waits until the blocking @code{recv()} call returns and
++only does then something.
++In this example all frame types are decoded by @emph{libmicrohttpd_ws},
++but we only do something when a text, ping or close frame is received.
++Binary and pong frames are ignored in our code.
++This is legit, because the server is only required to implement at
++least support for ping frame or close frame (the other frame types
++could be skipped in theory, because they don't require an answer).
++The pong frame doesn't require an answer and whether text frames or
++binary frames get an answer simply belongs to your server application.
++So this is a valid minimal example.
++
++Until this point you've learned everything you need to basically
++use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
++These libraries offer much more functions for some specific cases.
++
++
++The further chapters of this tutorial focus on some specific problems
++and the client site programming.
++
++
++ at heading Using full-duplex websockets
++
++To use full-duplex websockets you can simply create two threads
++per websocket connection.
++One of these threads is used for receiving data with
++a blocking @code{recv()} call and the other thread is triggered
++by the application internal codes and sends the data.
++
++A full-duplex websocket example is implemented in the example file
++ at code{websocket_chatserver_example.c}.
++
++ at heading Error handling
++
++The most functions of @emph{libmicrohttpd_ws} return a value
++of @code{enum MHD_WEBSOCKET_STATUS}.
++The values of this enumeration can be converted into an integer
++and have an easy interpretation:
++
++ at itemize @bullet
++ at item
++If the value is less than zero an error occurred and the call has failed.
++Check the enumeration values for more specific information.
++
++ at item
++If the value is equal to zero, the call succeeded.
++
++ at item
++If the value is greater than zero, the call succeeded and the value
++specifies the decoded frame type.
++Currently positive values are only returned by @code{MHD_websocket_decode()}
++(of the functions with this return enumeration type).
++
++ at end itemize
++
++A websocket stream can also get broken when invalid frame data is received.
++Also the other site could send a close frame which puts the stream into
++a state where it may not be used for regular communication.
++Whether a stream has become broken, can be checked with
++ at code{MHD_websocket_stream_is_valid()}.
++
++
++ at heading Fragmentation
++
++In addition to the regular TCP/IP fragmentation the websocket protocol also
++supports fragmentation.
++Fragmentation could be used for continuous payload data such as video data
++from a webcam.
++Whether or not you want to receive fragmentation is specified upon
++initialization of the websocket stream.
++If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
++of @code{MHD_websocket_stream_init()} then you can receive fragments.
++If you don't pass this flag (in the most cases you just pass zero as flags)
++then you don't want to handle fragments on your own.
++ at emph{libmicrohttpd_ws} removes then the fragmentation for you
++in the background.
++You only get the completely assembled frames.
++
++Upon encoding you specify whether or not you want to create a fragmented frame
++by passing a flag to the corresponding encode function.
++Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
++can be used for fragmentation, because the other frame types may
++not be fragmented.
++Encoding fragmented frames is independent of
++the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
++
++ at heading Quick guide to websockets in JavaScript
++
++Websockets are supported in all modern web browsers.
++You initialize a websocket connection by creating an instance of
++the @code{WebSocket} class provided by the web browser.
++
++There are some simple rules for using websockets in the browser:
++
++ at itemize @bullet
++ at item
++When you initialize the instance of the websocket class you must pass an URL.
++The URL must either start with @code{ws://}
++(for not encrypted websocket protocol) or @code{wss://}
++(for TLS-encrypted websocket protocol).
++
++ at strong{IMPORTANT:} If your website is accessed via @code{https://}
++then you are in a security context, which means that you are only allowed to
++access other secure protocols.
++So you can only use @code{wss://} for websocket connections then.
++If you try to @code{ws://} instead then your websocket connection will
++automatically fail.
++
++ at item
++The WebSocket class uses events to handle the receiving of data.
++JavaScript is per definition a single-threaded language so
++the receiving events will never overlap.
++Sending is done directly by calling a method of the instance of
++the WebSocket class.
++
++ at end itemize
++
++
++Here is a short example for receiving/sending data to the same host
++as the website is running on:
++
++ at verbatim
++<!DOCTYPE html>
++<html>
++<head>
++<meta charset="UTF-8">
++<title>Websocket Demo</title>
++<script>
++
++let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
++          window.location.host + '/chat';
++let socket = null;
++
++window.onload = function(event) {
++  socket = new WebSocket(url);
++  socket.onopen = function(event) {
++    document.write('The websocket connection has been established.<br>');
++
++    // Send some text
++    socket.send('Hello from JavaScript!');
++  }
++
++  socket.onclose = function(event) {
++    document.write('The websocket connection has been closed.<br>');
++  }
++
++  socket.onerror = function(event) {
++    document.write('An error occurred during the websocket communication.<br>');
++  }
++
++  socket.onmessage = function(event) {
++    document.write('Websocket message received: ' + event.data + '<br>');
++  }
++}
++
++</script>
++</head>
++<body>
++</body>
++</html>
++
++ at end verbatim
++ at noindent
+--- libmicrohttpd-0.9.74/doc/examples/websocket.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ libmicrohttpd-0.9.74/doc/examples/websocket.c	2021-12-23 22:49:44.536725673 +0100
+@@ -0,0 +1,452 @@
++/* Feel free to use this example code in any way
++   you see fit (Public Domain) */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/select.h>
++#include <sys/socket.h>
++#include <fcntl.h>
++#else
++#include <winsock2.h>
++#endif
++#include <microhttpd.h>
++#include <microhttpd_ws.h>
++#include <time.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <errno.h>
++
++#define PORT 80
++
++#define PAGE \
++  "<!DOCTYPE html>\n" \
++  "<html>\n" \
++  "<head>\n" \
++  "<meta charset=\"UTF-8\">\n" \
++  "<title>Websocket Demo</title>\n" \
++  "<script>\n" \
++  "\n" \
++  "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
++    "  + '://' +\n" \
++  "          window.location.host + '/chat';\n" \
++  "let socket = null;\n" \
++  "\n" \
++  "window.onload = function(event) {\n" \
++  "  socket = new WebSocket(url);\n" \
++  "  socket.onopen = function(event) {\n" \
++  "    document.write('The websocket connection has been " \
++    "established.<br>');\n" \
++  "\n" \
++  "    // Send some text\n" \
++  "    socket.send('Hello from JavaScript!');\n" \
++  "  }\n" \
++  "\n" \
++  "  socket.onclose = function(event) {\n" \
++  "    document.write('The websocket connection has been closed.<br>');\n" \
++  "  }\n" \
++  "\n" \
++  "  socket.onerror = function(event) {\n" \
++  "    document.write('An error occurred during the websocket " \
++    "communication.<br>');\n" \
++  "  }\n" \
++  "\n" \
++  "  socket.onmessage = function(event) {\n" \
++  "    document.write('Websocket message received: ' + " \
++    "event.data + '<br>');\n" \
++  "  }\n" \
++  "}\n" \
++  "\n" \
++  "</script>\n" \
++  "</head>\n" \
++  "<body>\n" \
++  "</body>\n" \
++  "</html>"
++
++#define PAGE_NOT_FOUND \
++  "404 Not Found"
++
++#define PAGE_INVALID_WEBSOCKET_REQUEST \
++  "Invalid WebSocket request!"
++
++static void
++send_all (MHD_socket fd,
++          const char *buf,
++          size_t len);
++
++static void
++make_blocking (MHD_socket fd);
++
++static void
++upgrade_handler (void *cls,
++                 struct MHD_Connection *connection,
++                 void *con_cls,
++                 const char *extra_in,
++                 size_t extra_in_size,
++                 MHD_socket fd,
++                 struct MHD_UpgradeResponseHandle *urh)
++{
++  /* make the socket blocking (operating-system-dependent code) */
++  make_blocking (fd);
++
++  /* create a websocket stream for this connection */
++  struct MHD_WebSocketStream *ws;
++  int result = MHD_websocket_stream_init (&ws,
++                                          0,
++                                          0);
++  if (0 != result)
++  {
++    /* Couldn't create the websocket stream.
++     * So we close the socket and leave
++     */
++    MHD_upgrade_action (urh,
++                        MHD_UPGRADE_ACTION_CLOSE);
++    return;
++  }
++
++  /* Let's wait for incoming data */
++  const size_t buf_len = 256;
++  char buf[buf_len];
++  ssize_t got;
++  while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
++  {
++    got = recv (fd,
++                buf,
++                buf_len,
++                0);
++    if (0 >= got)
++    {
++      /* the TCP/IP socket has been closed */
++      break;
++    }
++
++    /* parse the entire received data */
++    size_t buf_offset = 0;
++    while (buf_offset < (size_t) got)
++    {
++      size_t new_offset = 0;
++      char *frame_data = NULL;
++      size_t frame_len  = 0;
++      int status = MHD_websocket_decode (ws,
++                                         buf + buf_offset,
++                                         ((size_t) got) - buf_offset,
++                                         &new_offset,
++                                         &frame_data,
++                                         &frame_len);
++      if (0 > status)
++      {
++        /* an error occurred and the connection must be closed */
++        if (NULL != frame_data)
++        {
++          MHD_websocket_free (ws, frame_data);
++        }
++        break;
++      }
++      else
++      {
++        buf_offset += new_offset;
++        if (0 < status)
++        {
++          /* the frame is complete */
++          switch (status)
++          {
++          case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
++            /* The client has sent some text.
++             * We will display it and answer with a text frame.
++             */
++            if (NULL != frame_data)
++            {
++              printf ("Received message: %s\n", frame_data);
++              MHD_websocket_free (ws, frame_data);
++              frame_data = NULL;
++            }
++            result = MHD_websocket_encode_text (ws,
++                                                "Hello",
++                                                5,  /* length of "Hello" */
++                                                0,
++                                                &frame_data,
++                                                &frame_len,
++                                                NULL);
++            if (0 == result)
++            {
++              send_all (fd,
++                        frame_data,
++                        frame_len);
++            }
++            break;
++
++          case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
++            /* if we receive a close frame, we will respond with one */
++            MHD_websocket_free (ws,
++                                frame_data);
++            frame_data = NULL;
++
++            result = MHD_websocket_encode_close (ws,
++                                                 0,
++                                                 NULL,
++                                                 0,
++                                                 &frame_data,
++                                                 &frame_len);
++            if (0 == result)
++            {
++              send_all (fd,
++                        frame_data,
++                        frame_len);
++            }
++            break;
++
++          case MHD_WEBSOCKET_STATUS_PING_FRAME:
++            /* if we receive a ping frame, we will respond */
++            /* with the corresponding pong frame */
++            {
++              char *pong = NULL;
++              size_t pong_len = 0;
++              result = MHD_websocket_encode_pong (ws,
++                                                  frame_data,
++                                                  frame_len,
++                                                  &pong,
++                                                  &pong_len);
++              if (0 == result)
++              {
++                send_all (fd,
++                          pong,
++                          pong_len);
++              }
++              MHD_websocket_free (ws,
++                                  pong);
++            }
++            break;
++
++          default:
++            /* Other frame types are ignored
++             * in this minimal example.
++             * This is valid, because they become
++             * automatically skipped if we receive them unexpectedly
++             */
++            break;
++          }
++        }
++        if (NULL != frame_data)
++        {
++          MHD_websocket_free (ws, frame_data);
++        }
++      }
++    }
++  }
++
++  /* free the websocket stream */
++  MHD_websocket_stream_free (ws);
++
++  /* close the socket when it is not needed anymore */
++  MHD_upgrade_action (urh,
++                      MHD_UPGRADE_ACTION_CLOSE);
++}
++
++
++/* This helper function is used for the case that
++ * we need to resend some data
++ */
++static void
++send_all (MHD_socket fd,
++          const char *buf,
++          size_t len)
++{
++  ssize_t ret;
++  size_t off;
++
++  for (off = 0; off < len; off += ret)
++  {
++    ret = send (fd,
++                &buf[off],
++                (int) (len - off),
++                0);
++    if (0 > ret)
++    {
++      if (EAGAIN == errno)
++      {
++        ret = 0;
++        continue;
++      }
++      break;
++    }
++    if (0 == ret)
++      break;
++  }
++}
++
++
++/* This helper function contains operating-system-dependent code and
++ * is used to make a socket blocking.
++ */
++static void
++make_blocking (MHD_socket fd)
++{
++#ifndef _WIN32
++  int flags;
++
++  flags = fcntl (fd, F_GETFL);
++  if (-1 == flags)
++    abort ();
++  if ((flags & ~O_NONBLOCK) != flags)
++    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
++      abort ();
++#else  /* _WIN32 */
++  unsigned long flags = 0;
++
++  if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
++    abort ();
++#endif /* _WIN32 */
++}
++
++
++static enum MHD_Result
++access_handler (void *cls,
++                struct MHD_Connection *connection,
++                const char *url,
++                const char *method,
++                const char *version,
++                const char *upload_data,
++                size_t *upload_data_size,
++                void **ptr)
++{
++  static int aptr;
++  struct MHD_Response *response;
++  int ret;
++
++  (void) cls;               /* Unused. Silent compiler warning. */
++  (void) upload_data;       /* Unused. Silent compiler warning. */
++  (void) upload_data_size;  /* Unused. Silent compiler warning. */
++
++  if (0 != strcmp (method, "GET"))
++    return MHD_NO;              /* unexpected method */
++  if (&aptr != *ptr)
++  {
++    /* do never respond on first call */
++    *ptr = &aptr;
++    return MHD_YES;
++  }
++  *ptr = NULL;                  /* reset when done */
++
++  if (0 == strcmp (url, "/"))
++  {
++    /* Default page for visiting the server */
++    struct MHD_Response *response = MHD_create_response_from_buffer (
++      strlen (PAGE),
++      PAGE,
++      MHD_RESPMEM_PERSISTENT);
++    ret = MHD_queue_response (connection,
++                              MHD_HTTP_OK,
++                              response);
++    MHD_destroy_response (response);
++  }
++  else if (0 == strcmp (url, "/chat"))
++  {
++    char is_valid = 1;
++    const char *value = NULL;
++    char sec_websocket_accept[29];
++
++    if (0 != MHD_websocket_check_http_version (version))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_CONNECTION);
++    if (0 != MHD_websocket_check_connection_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_UPGRADE);
++    if (0 != MHD_websocket_check_upgrade_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
++    if (0 != MHD_websocket_check_version_header (value))
++    {
++      is_valid = 0;
++    }
++    value = MHD_lookup_connection_value (connection,
++                                         MHD_HEADER_KIND,
++                                         MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
++    if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
++    {
++      is_valid = 0;
++    }
++
++    if (1 == is_valid)
++    {
++      /* upgrade the connection */
++      response = MHD_create_response_for_upgrade (&upgrade_handler,
++                                                  NULL);
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_CONNECTION,
++                               "Upgrade");
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_UPGRADE,
++                               "websocket");
++      MHD_add_response_header (response,
++                               MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
++                               sec_websocket_accept);
++      ret = MHD_queue_response (connection,
++                                MHD_HTTP_SWITCHING_PROTOCOLS,
++                                response);
++      MHD_destroy_response (response);
++    }
++    else
++    {
++      /* return error page */
++      struct MHD_Response *response = MHD_create_response_from_buffer (
++        strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
++        PAGE_INVALID_WEBSOCKET_REQUEST,
++        MHD_RESPMEM_PERSISTENT);
++      ret = MHD_queue_response (connection,
++                                MHD_HTTP_BAD_REQUEST,
++                                response);
++      MHD_destroy_response (response);
++    }
++  }
++  else
++  {
++    struct MHD_Response *response = MHD_create_response_from_buffer (
++      strlen (PAGE_NOT_FOUND),
++      PAGE_NOT_FOUND,
++      MHD_RESPMEM_PERSISTENT);
++    ret = MHD_queue_response (connection,
++                              MHD_HTTP_NOT_FOUND,
++                              response);
++    MHD_destroy_response (response);
++  }
++
++  return ret;
++}
++
++
++int
++main (int argc,
++      char *const *argv)
++{
++  (void) argc;               /* Unused. Silent compiler warning. */
++  (void) argv;               /* Unused. Silent compiler warning. */
++  struct MHD_Daemon *daemon;
++
++  daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
++                             | MHD_USE_THREAD_PER_CONNECTION
++                             | MHD_ALLOW_UPGRADE
++                             | MHD_USE_ERROR_LOG,
++                             PORT, NULL, NULL,
++                             &access_handler, NULL,
++                             MHD_OPTION_END);
++
++  if (NULL == daemon)
++    return 1;
++  (void) getc (stdin);
++
++  MHD_stop_daemon (daemon);
++
++  return 0;
++}
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/libmicrohttpd.git/commitdiff/6fc60a3e1d13b92611226a9f8683f0641e00d9b5




More information about the pld-cvs-commit mailing list