libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

libtest_convenience.c (14737B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2024 Christian Grothoff
      5   Copyright (C) 2025 Evgeny Grin (Karlson2k)
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 
     40 /**
     41  * @file libtest_convenience.c
     42  * @brief convenience functions for libtest users
     43  * @author Christian Grothoff
     44  */
     45 #include "libtest.h"
     46 #include <pthread.h>
     47 #include <fcntl.h>
     48 #include <unistd.h>
     49 #include <errno.h>
     50 #ifdef MHD_SUPPORT_EPOLL
     51 #  include <sys/epoll.h>
     52 #endif
     53 #include <curl/curl.h>
     54 
     55 
     56 const char *
     57 MHDT_server_setup_minimal (const void *cls,
     58                            struct MHD_Daemon *d)
     59 {
     60   const struct MHD_DaemonOptionAndValue *options = cls;
     61 
     62   if (MHD_SC_OK !=
     63       MHD_daemon_set_options (
     64         d,
     65         options,
     66         MHD_OPTIONS_ARRAY_MAX_SIZE))
     67     return "Failed to configure threading mode!";
     68   if (MHD_SC_OK !=
     69       MHD_DAEMON_SET_OPTIONS (
     70         d,
     71         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
     72                                 0)))
     73     return "Failed to bind to port 0!";
     74   return NULL;
     75 }
     76 
     77 
     78 /**
     79  * Setup TLS at @a d for the given backend @a be.
     80  *
     81  * @return NULL on success, otherwise error message
     82  */
     83 static const char *
     84 server_setup_tls (struct MHD_Daemon *d,
     85                   enum MHD_TlsBackend be)
     86 {
     87   static const char *mem_cert =
     88     "-----BEGIN CERTIFICATE-----\n\
     89 MIIDjTCCAnWgAwIBAgIUKkxAx2lVnvYcaNqBpJmTgXh1/VgwDQYJKoZIhvcNAQEL\n\
     90 BQAwVjELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxDzANBgNV\n\
     91 BAcMBkJvc3RvbjENMAsGA1UECgwEUm9vdDEPMA0GA1UEAwwGY2EuZ251MB4XDTI0\n\
     92 MTEyOTEyNDUyOFoXDTM0MTEyNzEyNDUyOFowVjELMAkGA1UEBhMCVVMxFjAUBgNV\n\
     93 BAgMDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcMBkJvc3RvbjENMAsGA1UECgwEUm9v\n\
     94 dDEPMA0GA1UEAwwGY2EuZ251MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n\
     95 AQEA23YSrcGIBgZf9bbzTnmYFy+4tM82kUhsVFKxWCNEMdKmhaeVvXogyd6Evq4P\n\
     96 NvBGdUABDtHp4pSEijrxWbn8sxddTznoT/8IOuHI0/PtwXYP/sHQ/HzekEUVKN2Z\n\
     97 NMbMUzQfaJyiIV5TrZlaBwHjQ+sRs8E56C3cQjkwuyjll2zDsEfmEnPimZRAL3kb\n\
     98 wW8VFfBcR2Id+a9xKjwlnB4eXQFAgYINoRgCtUOUxSeFgNnwkOUSqDknO6Xi47YZ\n\
     99 EdLlHyUnv5eX547xUkrYhfQuQwaqpGrjHf3GFoysN8P9kd2f1qsJKtQcUbF9DDeZ\n\
    100 6ya47X/LBO8QflMsVjb1V3oz9QIDAQABo1MwUTAdBgNVHQ4EFgQUsvdZoX3RxdN6\n\
    101 wrONr31SOA9Qbc4wHwYDVR0jBBgwFoAUsvdZoX3RxdN6wrONr31SOA9Qbc4wDwYD\n\
    102 VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAS3PyV7crGk9brqU90aML\n\
    103 2TWkjgzFb3/nASnpvVYiqyiV4neGiEjtDy7eVlqP6GlD2pYcVodY+ly9wNlo85/h\n\
    104 YfgCFFl37tMG7PpRac2qBqaSn1DpwsCb08LjRrOvoaRffWUikSoZmsYDlaCgl9nT\n\
    105 pGtIrz0BSoyu5mHalIZTVQOrbkNBNK6ZgnYy2iWuiLa5Z1xzKpsRBRaKJc1pcQE/\n\
    106 QVbPdCiyGQMPEVn/KHxitlycFoee/fA+izXVdstVwfig2DoMvrlGZvEkN1ER3Yz4\n\
    107 QPJ6HzOsBQL1F+YhnMCQfc2rpcwxAWf8JMy6jsCq42KGq53tkWqHyQ6Zu2SiLRYk\n\
    108 CA==\n\
    109 -----END CERTIFICATE-----";
    110   static const char *mem_key  =
    111     "-----BEGIN ENCRYPTED PRIVATE KEY-----\n\
    112 MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQJ1VSHi+akaaVYO3O\n\
    113 H7I0EAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIZlNzQR1bh4IEggTI\n\
    114 8U86bfGmyAXXSi/R/l3G8ziZFyHrRE5Q/Q3uUW/jyUpe+S0gMRPqwW3V542ForbH\n\
    115 IH/Aa+KVxlwmsq0jlheCQewj9qZMQGuqa3iTl/OfCcuGMfsuQs2HsutoDMdEYuBI\n\
    116 6yOqNIrRvSHunZILLDpKz/AmCO6JnRiAwiSqPBixE5M+cm1qc7dy024REiW9l9K6\n\
    117 Hth9A0iYc94CUyUfHFj4CEkCNqk533Z2Ktkk3RQJnx5ORQG0iBJvoFiVODFKnoAk\n\
    118 Ge2HNrJH3bVvhQ+p8A/L4VmnWUCfcTyqgzo887WXRxORya6gcWWtrcEJGUbLh8sL\n\
    119 /mXFYj/0kEllIY+fHOmSx94I3GwBkQKER/CeOCIp+C392Pujgzrz23pdq20uIt3d\n\
    120 FCgbnIB+5IwOwQcqCkTYa1+Y5qCa6eFLgd8PXGTDyFwP4BHfG6WT/ctHQFi8vnXV\n\
    121 D1S726do1mA6CFE3DYmi45sf+Te2Xb346xk1GTSWtxGh9y4FblFDAWva4oTuvxPR\n\
    122 IDseBhXBsIqnOy1gb/5cGj0SIOQzqR1qlg4igv3UZFC8cVl+fNnngDBiX+nTYQVm\n\
    123 rDyxTzcX9txPSNpLyYRdNHwLGpzZAMoN46bUFnxt0cvRWN6MA7j1r0TYWBZKJ7b7\n\
    124 Yt/SuYsqSE0UJQEJz4QcQnlxu3qu4HJl7dOlto3fa42MWTkOcNr9XinHmKCZ9oYZ\n\
    125 PYNTggRGMXlqm66KmHWDqXqw9CeufprHq15SIJJR8v4SlvEZr+YlYQeHRI4E+FDA\n\
    126 mEFZy/U3ZL7ZHSDsEvpeBzIJkWxHobt57BIxYHE8KN0ZIz/mJZTxljacblFWnJRb\n\
    127 AUXTfrRZn3lGX+4WA6Biilwyxb71slCKaiz28C55Hnj1UwoUF8vNA3G2FGAX5Wk0\n\
    128 m3J2SoCHtJQYc/3lEC7zR9i3/F/7vgRxZMUWt/y6KRYq8ZnoQl3Eo2yvJYX/z7I6\n\
    129 JyqexAx3OvA+frN3rbO/o/k6w9333Smi0QxZzDM9tHn1BAgAtmyC1lizzKn7hDYK\n\
    130 o/eaPeatILbS0a/bHJBbP/R53keVr0hJ3MWK2nb/DV5Dl9j4Z6sHpo3P9L+Kq06y\n\
    131 G9q7NhBd7cxGq4AkCp+eSjqTvwgOX1PtAry00TUmzisLz8gIYutwJqbfZGL8WpR/\n\
    132 /wnLQXuM/tPLdQNy+PZeTQnPFwWQeZz4VgkMRhHV2xDw0mpzE+cdD204+YjHVdMH\n\
    133 D4MNrDlUmKM0OVoYgXd9YyLKzYVgW95GvY1X0SxTlIUuDiRv/SqRsurPFkSG457d\n\
    134 zmTUny1NRsnbv9bTXqt1Xewqsylyu02N1dZvjIzBnYMVYXl0r4aej1VNEXozQtWO\n\
    135 YRfWaZ29dXwZqUzd83ETQvhI4mZbwAlHbqm/CoyY6Vw4Am8hGa7II134lz2b3tkr\n\
    136 F1zBkvzzl6+HXewGOEjm+YorDMtfADiU/hkkykWq01NG3QSwk7jaKieb5Rlou53d\n\
    137 IXJQBw0KW5UrgbIFqMjpSZz1jdALBKsV+dw0wvCQ8BVXZm3zZpsV+0E4Z0sdj3TI\n\
    138 UbkFqQ6GpoxB25UUUlLZhBbtKy7dheuPBk0HowitYlo1kLVA/JiFB4qbdf5X/9Tm\n\
    139 XRkN+T0orEgy7rBQa7dJN9bdLj+dS5q8\n\
    140 -----END ENCRYPTED PRIVATE KEY-----";
    141 
    142   if (MHD_SC_OK !=
    143       MHD_DAEMON_SET_OPTIONS (
    144         d,
    145         MHD_D_OPTION_TLS (be)))
    146     return "Failed to enable TLS!";
    147   if (MHD_SC_OK !=
    148       MHD_DAEMON_SET_OPTIONS (
    149         d,
    150         MHD_D_OPTION_TLS_CERT_KEY (mem_cert,
    151                                    mem_key,
    152                                    "masterword")))
    153     return "Failed to enable TLS!";
    154   return NULL;
    155 }
    156 
    157 
    158 const char *
    159 MHDT_server_setup_tls (const void *cls,
    160                        struct MHD_Daemon *d)
    161 {
    162   const struct MHD_DaemonOptionAndValue *options = cls;
    163   const char *err;
    164 
    165   err = MHDT_server_setup_minimal (options,
    166                                    d);
    167   if (NULL != err)
    168     return err;
    169   err = server_setup_tls (d,
    170                           MHD_TLS_BACKEND_ANY);
    171   if (NULL != err)
    172     return err;
    173   return NULL;
    174 }
    175 
    176 
    177 const char *
    178 MHDT_server_setup_gnutls (const void *cls,
    179                           struct MHD_Daemon *d)
    180 {
    181   const struct MHD_DaemonOptionAndValue *options = cls;
    182   const char *err;
    183 
    184   err = MHDT_server_setup_minimal (options,
    185                                    d);
    186   if (NULL != err)
    187     return err;
    188   err = server_setup_tls (d,
    189                           MHD_TLS_BACKEND_GNUTLS);
    190   if (NULL != err)
    191     return err;
    192   return NULL;
    193 }
    194 
    195 
    196 const char *
    197 MHDT_server_setup_openssl (const void *cls,
    198                            struct MHD_Daemon *d)
    199 {
    200   const struct MHD_DaemonOptionAndValue *options = cls;
    201   const char *err;
    202 
    203   err = MHDT_server_setup_minimal (options,
    204                                    d);
    205   if (NULL != err)
    206     return err;
    207   err = server_setup_tls (d,
    208                           MHD_TLS_BACKEND_OPENSSL);
    209   if (NULL != err)
    210     return err;
    211   return NULL;
    212 }
    213 
    214 
    215 void
    216 MHDT_server_run_minimal (void *cls,
    217                          int finsig,
    218                          struct MHD_Daemon *d)
    219 {
    220   fd_set r;
    221   char c;
    222 
    223   (void) cls; /* Unused */
    224   (void) d;   /* Unused */
    225 
    226   FD_ZERO (&r);
    227   FD_SET (finsig, &r);
    228   while (1)
    229   {
    230     if ( (-1 ==
    231           select (finsig + 1,
    232                   &r,
    233                   NULL,
    234                   NULL,
    235                   NULL)) &&
    236          (EAGAIN != errno) )
    237     {
    238       fprintf (stderr,
    239                "Failure waiting on termination signal: %s\n",
    240                strerror (errno));
    241       break;
    242     }
    243     if (FD_ISSET (finsig,
    244                   &r))
    245       break;
    246   }
    247   if ( (FD_ISSET (finsig,
    248                   &r)) &&
    249        (1 != read (finsig,
    250                    &c,
    251                    1)) )
    252   {
    253     fprintf (stderr,
    254              "Failed to drain termination signal\n");
    255   }
    256 }
    257 
    258 
    259 void
    260 MHDT_server_run_blocking (void *cls,
    261                           int finsig,
    262                           struct MHD_Daemon *d)
    263 {
    264   fd_set r;
    265   char c;
    266 
    267   (void) cls; /* Unused */
    268   (void) d;   /* Unused */
    269 
    270   FD_ZERO (&r);
    271   FD_SET (finsig, &r);
    272   while (1)
    273   {
    274     struct timeval timeout = {
    275       .tv_usec = 1000 /* 1000 microseconds */
    276     };
    277 
    278     if ( (-1 ==
    279           select (finsig + 1,
    280                   &r,
    281                   NULL,
    282                   NULL,
    283                   &timeout)) &&
    284          (EAGAIN != errno) )
    285     {
    286       fprintf (stderr,
    287                "Failure waiting on termination signal: %s\n",
    288                strerror (errno));
    289       break;
    290     }
    291 #ifdef FIXME
    292     if (MHD_SC_OK !=
    293         MHD_daemon_process_blocking (d,
    294                                      1000))
    295     {
    296       fprintf (stderr,
    297                "Failure running MHD_daemon_process_blocking()\n");
    298       break;
    299     }
    300 #else
    301     abort ();
    302 #endif
    303   }
    304   if ( (FD_ISSET (finsig,
    305                   &r)) &&
    306        (1 != read (finsig,
    307                    &c,
    308                    1)) )
    309   {
    310     fprintf (stderr,
    311              "Failed to drain termination signal\n");
    312   }
    313 }
    314 
    315 
    316 #ifdef MHD_SUPPORT_EPOLL
    317 static int my_epoll_fd = -1;
    318 
    319 
    320 /**
    321  * The callback for registration/de-registration of the sockets to watch.
    322  *
    323  * This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(),
    324  * #MHD_daemon_add_connection().
    325  *
    326  * @param cls the closure
    327  * @param fd the socket to watch
    328  * @param watch_for the states of the @a fd to watch, if set to
    329  *                  #MHD_FD_STATE_NONE the socket must be de-registred
    330  * @param app_cntx_old the old application defined context for the socket,
    331  *                     NULL if @a fd socket was not registered before
    332  * @param ecb_cntx the context handle to be used
    333  *                 with #MHD_daemon_event_update()
    334  * @return NULL if error (to connection will be aborted),
    335  *         or the new socket context
    336  * @ingroup event
    337  */
    338 static void *
    339 update_fd (
    340   void *cls,
    341   MHD_Socket fd,
    342   enum MHD_FdState watch_for,
    343   MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
    344   struct MHD_EventUpdateContext *ecb_cntx)
    345 {
    346   struct epoll_event ev;
    347 
    348   if (watch_for == MHD_FD_STATE_NONE)
    349   {
    350     epoll_ctl (my_epoll_fd,
    351                EPOLL_CTL_DEL,
    352                fd,
    353                &ev /* for Linux 2.6.9-compatibility */);
    354     return NULL;
    355   }
    356   ev.data.ptr = ecb_cntx;
    357   ev.events = 0;
    358   if (0 != (watch_for & MHD_FD_STATE_RECV))
    359     ev.events |= EPOLLIN;
    360   if (0 != (watch_for & MHD_FD_STATE_SEND))
    361     ev.events |= EPOLLOUT;
    362   if (0 != (watch_for & MHD_FD_STATE_EXCEPT))
    363     ev.events |= EPOLLHUP;
    364   if (0 !=
    365       epoll_ctl (my_epoll_fd,
    366                  NULL == app_cntx_old
    367                  ? EPOLL_CTL_ADD
    368                  : EPOLL_CTL_MOD,
    369                  fd,
    370                  &ev))
    371   {
    372     fprintf (stderr,
    373              "epoll_ctl failed: %s\n",
    374              strerror (errno));
    375     return NULL;
    376   }
    377   return ecb_cntx;
    378 }
    379 
    380 
    381 const char *
    382 MHDT_server_setup_external (const void *cls,
    383                             struct MHD_Daemon *d)
    384 {
    385   (void) cls;
    386   if (MHD_SC_OK !=
    387       MHD_DAEMON_SET_OPTIONS (
    388         d,
    389         MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd,
    390                                                       NULL)))
    391     return "Failed to configure external mode!";
    392   if (MHD_SC_OK !=
    393       MHD_DAEMON_SET_OPTIONS (
    394         d,
    395         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
    396                                 0)))
    397     return "Failed to bind to port 0!";
    398   my_epoll_fd = epoll_create1 (0);
    399 
    400   return NULL;
    401 }
    402 
    403 
    404 void
    405 MHDT_server_run_external (void *cls,
    406                           int finsig,
    407                           struct MHD_Daemon *d)
    408 {
    409   fd_set r;
    410 
    411   (void) cls; /* Unused */
    412   if (-1 == my_epoll_fd)
    413     abort ();
    414   while (1)
    415   {
    416     uint_fast64_t next_wait;
    417     struct timeval timeout;
    418 
    419     if (MHD_SC_OK !=
    420         MHD_daemon_process_reg_events (d,
    421                                        &next_wait))
    422     {
    423       fprintf (stderr,
    424                "MHD_daemon_process_reg_events() failed\n");
    425       break;
    426     }
    427     timeout.tv_sec = next_wait / 1000000;
    428     timeout.tv_usec = next_wait % 1000000;
    429 
    430     FD_ZERO (&r);
    431     FD_SET (finsig,
    432             &r);
    433     FD_SET (my_epoll_fd,
    434             &r);
    435     if ( (-1 ==
    436           select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1,
    437                   &r,
    438                   NULL,
    439                   NULL,
    440                   &timeout)) &&
    441          (EAGAIN != errno) )
    442     {
    443       fprintf (stderr,
    444                "Failure in select(): %s\n",
    445                strerror (errno));
    446       break;
    447     }
    448     if (FD_ISSET (finsig,
    449                   &r))
    450       break;
    451     if (FD_ISSET (my_epoll_fd,
    452                   &r))
    453     {
    454       int maxevents = 40;
    455       struct epoll_event events[maxevents];
    456       int n;
    457       int i;
    458 
    459       n = epoll_wait (my_epoll_fd,
    460                       events,
    461                       maxevents,
    462                       0);
    463       if (-1 == n)
    464       {
    465         fprintf (stderr,
    466                  "epoll_wait() failed: %s\n",
    467                  strerror (errno));
    468         break;
    469       }
    470       for (i = 0; i < n; i++)
    471       {
    472         enum MHD_FdState state = MHD_FD_STATE_NONE;
    473 
    474         if (0 != (events[i].events & EPOLLIN))
    475           MHD_FD_STATE_SET_RECV (state);
    476         if (0 != (events[i].events & EPOLLOUT))
    477           MHD_FD_STATE_SET_SEND (state);
    478         if (0 != (events[i].events & (EPOLLERR | EPOLLHUP)) )
    479           MHD_FD_STATE_SET_EXCEPT (state);
    480         MHD_daemon_event_update (d,
    481                                  events[i].data.ptr,
    482                                  state);
    483       }
    484     }
    485   }
    486 
    487   {
    488     char c;
    489 
    490     if ( (FD_ISSET (finsig,
    491                     &r)) &&
    492          (1 != read (finsig,
    493                      &c,
    494                      1)) )
    495     {
    496       fprintf (stderr,
    497                "Failed to drain termination signal\n");
    498     }
    499   }
    500 
    501   close (my_epoll_fd);
    502   my_epoll_fd = -1;
    503 }
    504 
    505 
    506 #endif /* MHD_SUPPORT_EPOLL */