libmicrohttpd2

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

libtest_convenience.c (15893B)


      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 /**
     57  * Return the port to bind to. Usually zero (any), but can be
     58  * forced to a particular port via environment variables.
     59  */
     60 static uint16_t
     61 get_port (void)
     62 {
     63   const char *portenv;
     64 
     65   portenv = getenv ("MHD_TEST_FORCE_SERVER_PORT");
     66   if (NULL != portenv)
     67   {
     68     unsigned int i;
     69     char dummy;
     70 
     71     if ( (1 != sscanf (portenv,
     72                        "%u%c",
     73                        &i,
     74                        &dummy)) ||
     75          (i > 65535) )
     76     {
     77       fprintf (stderr,
     78                "Invalid port number specified in MHD_TEST_FORCE_PORT, using 0");
     79       return 0;
     80     }
     81     return (uint16_t) i;
     82   }
     83   return 0;
     84 }
     85 
     86 
     87 const char *
     88 MHDT_server_setup_minimal (const void *cls,
     89                            struct MHD_Daemon *d)
     90 {
     91   const struct MHD_DaemonOptionAndValue *options
     92     = (const struct MHD_DaemonOptionAndValue *) cls;
     93 
     94   if (MHD_SC_OK !=
     95       MHD_daemon_set_options (
     96         d,
     97         options,
     98         MHD_OPTIONS_ARRAY_MAX_SIZE))
     99     return "Failed to configure threading mode!";
    100   if (MHD_SC_OK !=
    101       MHD_DAEMON_SET_OPTIONS (
    102         d,
    103         MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (2000),
    104         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
    105                                 get_port ())))
    106     return "Failed to bind to port!";
    107   return NULL;
    108 }
    109 
    110 
    111 /**
    112  * Setup TLS at @a d for the given backend @a be.
    113  *
    114  * @return NULL on success, otherwise error message
    115  */
    116 static const char *
    117 server_setup_tls (struct MHD_Daemon *d,
    118                   enum MHD_TlsBackend be)
    119 {
    120   static const char *mem_cert =
    121     "-----BEGIN CERTIFICATE-----\n\
    122 MIIDjTCCAnWgAwIBAgIUKkxAx2lVnvYcaNqBpJmTgXh1/VgwDQYJKoZIhvcNAQEL\n\
    123 BQAwVjELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxDzANBgNV\n\
    124 BAcMBkJvc3RvbjENMAsGA1UECgwEUm9vdDEPMA0GA1UEAwwGY2EuZ251MB4XDTI0\n\
    125 MTEyOTEyNDUyOFoXDTM0MTEyNzEyNDUyOFowVjELMAkGA1UEBhMCVVMxFjAUBgNV\n\
    126 BAgMDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcMBkJvc3RvbjENMAsGA1UECgwEUm9v\n\
    127 dDEPMA0GA1UEAwwGY2EuZ251MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n\
    128 AQEA23YSrcGIBgZf9bbzTnmYFy+4tM82kUhsVFKxWCNEMdKmhaeVvXogyd6Evq4P\n\
    129 NvBGdUABDtHp4pSEijrxWbn8sxddTznoT/8IOuHI0/PtwXYP/sHQ/HzekEUVKN2Z\n\
    130 NMbMUzQfaJyiIV5TrZlaBwHjQ+sRs8E56C3cQjkwuyjll2zDsEfmEnPimZRAL3kb\n\
    131 wW8VFfBcR2Id+a9xKjwlnB4eXQFAgYINoRgCtUOUxSeFgNnwkOUSqDknO6Xi47YZ\n\
    132 EdLlHyUnv5eX547xUkrYhfQuQwaqpGrjHf3GFoysN8P9kd2f1qsJKtQcUbF9DDeZ\n\
    133 6ya47X/LBO8QflMsVjb1V3oz9QIDAQABo1MwUTAdBgNVHQ4EFgQUsvdZoX3RxdN6\n\
    134 wrONr31SOA9Qbc4wHwYDVR0jBBgwFoAUsvdZoX3RxdN6wrONr31SOA9Qbc4wDwYD\n\
    135 VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAS3PyV7crGk9brqU90aML\n\
    136 2TWkjgzFb3/nASnpvVYiqyiV4neGiEjtDy7eVlqP6GlD2pYcVodY+ly9wNlo85/h\n\
    137 YfgCFFl37tMG7PpRac2qBqaSn1DpwsCb08LjRrOvoaRffWUikSoZmsYDlaCgl9nT\n\
    138 pGtIrz0BSoyu5mHalIZTVQOrbkNBNK6ZgnYy2iWuiLa5Z1xzKpsRBRaKJc1pcQE/\n\
    139 QVbPdCiyGQMPEVn/KHxitlycFoee/fA+izXVdstVwfig2DoMvrlGZvEkN1ER3Yz4\n\
    140 QPJ6HzOsBQL1F+YhnMCQfc2rpcwxAWf8JMy6jsCq42KGq53tkWqHyQ6Zu2SiLRYk\n\
    141 CA==\n\
    142 -----END CERTIFICATE-----";
    143   static const char *mem_key  =
    144     "-----BEGIN ENCRYPTED PRIVATE KEY-----\n\
    145 MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQJ1VSHi+akaaVYO3O\n\
    146 H7I0EAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIZlNzQR1bh4IEggTI\n\
    147 8U86bfGmyAXXSi/R/l3G8ziZFyHrRE5Q/Q3uUW/jyUpe+S0gMRPqwW3V542ForbH\n\
    148 IH/Aa+KVxlwmsq0jlheCQewj9qZMQGuqa3iTl/OfCcuGMfsuQs2HsutoDMdEYuBI\n\
    149 6yOqNIrRvSHunZILLDpKz/AmCO6JnRiAwiSqPBixE5M+cm1qc7dy024REiW9l9K6\n\
    150 Hth9A0iYc94CUyUfHFj4CEkCNqk533Z2Ktkk3RQJnx5ORQG0iBJvoFiVODFKnoAk\n\
    151 Ge2HNrJH3bVvhQ+p8A/L4VmnWUCfcTyqgzo887WXRxORya6gcWWtrcEJGUbLh8sL\n\
    152 /mXFYj/0kEllIY+fHOmSx94I3GwBkQKER/CeOCIp+C392Pujgzrz23pdq20uIt3d\n\
    153 FCgbnIB+5IwOwQcqCkTYa1+Y5qCa6eFLgd8PXGTDyFwP4BHfG6WT/ctHQFi8vnXV\n\
    154 D1S726do1mA6CFE3DYmi45sf+Te2Xb346xk1GTSWtxGh9y4FblFDAWva4oTuvxPR\n\
    155 IDseBhXBsIqnOy1gb/5cGj0SIOQzqR1qlg4igv3UZFC8cVl+fNnngDBiX+nTYQVm\n\
    156 rDyxTzcX9txPSNpLyYRdNHwLGpzZAMoN46bUFnxt0cvRWN6MA7j1r0TYWBZKJ7b7\n\
    157 Yt/SuYsqSE0UJQEJz4QcQnlxu3qu4HJl7dOlto3fa42MWTkOcNr9XinHmKCZ9oYZ\n\
    158 PYNTggRGMXlqm66KmHWDqXqw9CeufprHq15SIJJR8v4SlvEZr+YlYQeHRI4E+FDA\n\
    159 mEFZy/U3ZL7ZHSDsEvpeBzIJkWxHobt57BIxYHE8KN0ZIz/mJZTxljacblFWnJRb\n\
    160 AUXTfrRZn3lGX+4WA6Biilwyxb71slCKaiz28C55Hnj1UwoUF8vNA3G2FGAX5Wk0\n\
    161 m3J2SoCHtJQYc/3lEC7zR9i3/F/7vgRxZMUWt/y6KRYq8ZnoQl3Eo2yvJYX/z7I6\n\
    162 JyqexAx3OvA+frN3rbO/o/k6w9333Smi0QxZzDM9tHn1BAgAtmyC1lizzKn7hDYK\n\
    163 o/eaPeatILbS0a/bHJBbP/R53keVr0hJ3MWK2nb/DV5Dl9j4Z6sHpo3P9L+Kq06y\n\
    164 G9q7NhBd7cxGq4AkCp+eSjqTvwgOX1PtAry00TUmzisLz8gIYutwJqbfZGL8WpR/\n\
    165 /wnLQXuM/tPLdQNy+PZeTQnPFwWQeZz4VgkMRhHV2xDw0mpzE+cdD204+YjHVdMH\n\
    166 D4MNrDlUmKM0OVoYgXd9YyLKzYVgW95GvY1X0SxTlIUuDiRv/SqRsurPFkSG457d\n\
    167 zmTUny1NRsnbv9bTXqt1Xewqsylyu02N1dZvjIzBnYMVYXl0r4aej1VNEXozQtWO\n\
    168 YRfWaZ29dXwZqUzd83ETQvhI4mZbwAlHbqm/CoyY6Vw4Am8hGa7II134lz2b3tkr\n\
    169 F1zBkvzzl6+HXewGOEjm+YorDMtfADiU/hkkykWq01NG3QSwk7jaKieb5Rlou53d\n\
    170 IXJQBw0KW5UrgbIFqMjpSZz1jdALBKsV+dw0wvCQ8BVXZm3zZpsV+0E4Z0sdj3TI\n\
    171 UbkFqQ6GpoxB25UUUlLZhBbtKy7dheuPBk0HowitYlo1kLVA/JiFB4qbdf5X/9Tm\n\
    172 XRkN+T0orEgy7rBQa7dJN9bdLj+dS5q8\n\
    173 -----END ENCRYPTED PRIVATE KEY-----";
    174 
    175   if (MHD_SC_OK !=
    176       MHD_DAEMON_SET_OPTIONS (
    177         d,
    178         MHD_D_OPTION_TLS (be)))
    179     return "Failed to enable TLS!";
    180   if (MHD_SC_OK !=
    181       MHD_DAEMON_SET_OPTIONS (
    182         d,
    183         MHD_D_OPTION_TLS_CERT_KEY (mem_cert,
    184                                    mem_key,
    185                                    "masterword")))
    186     return "Failed to enable TLS!";
    187   return NULL;
    188 }
    189 
    190 
    191 const char *
    192 MHDT_server_setup_tls (const void *cls,
    193                        struct MHD_Daemon *d)
    194 {
    195   const struct MHD_DaemonOptionAndValue *options
    196     = (const struct MHD_DaemonOptionAndValue *) cls;
    197   const char *err;
    198 
    199   err = MHDT_server_setup_minimal (options,
    200                                    d);
    201   if (NULL != err)
    202     return err;
    203   err = server_setup_tls (d,
    204                           MHD_TLS_BACKEND_ANY);
    205   if (NULL != err)
    206     return err;
    207   return NULL;
    208 }
    209 
    210 
    211 const char *
    212 MHDT_server_setup_gnutls (const void *cls,
    213                           struct MHD_Daemon *d)
    214 {
    215   const struct MHD_DaemonOptionAndValue *options
    216     = (const struct MHD_DaemonOptionAndValue *) cls;
    217   const char *err;
    218 
    219   err = MHDT_server_setup_minimal (options,
    220                                    d);
    221   if (NULL != err)
    222     return err;
    223   err = server_setup_tls (d,
    224                           MHD_TLS_BACKEND_GNUTLS);
    225   if (NULL != err)
    226     return err;
    227   return NULL;
    228 }
    229 
    230 
    231 const char *
    232 MHDT_server_setup_openssl (const void *cls,
    233                            struct MHD_Daemon *d)
    234 {
    235   const struct MHD_DaemonOptionAndValue *options
    236     = (const struct MHD_DaemonOptionAndValue *) cls;
    237   const char *err;
    238 
    239   err = MHDT_server_setup_minimal (options,
    240                                    d);
    241   if (NULL != err)
    242     return err;
    243   err = server_setup_tls (d,
    244                           MHD_TLS_BACKEND_OPENSSL);
    245   if (NULL != err)
    246     return err;
    247   return NULL;
    248 }
    249 
    250 
    251 void
    252 MHDT_server_run_minimal (void *cls,
    253                          int finsig,
    254                          struct MHD_Daemon *d)
    255 {
    256   fd_set r;
    257   char c;
    258 
    259   (void) cls; /* Unused */
    260   (void) d;   /* Unused */
    261 
    262   FD_ZERO (&r);
    263   FD_SET (finsig, &r);
    264   while (1)
    265   {
    266     if ( (-1 ==
    267           select (finsig + 1,
    268                   &r,
    269                   NULL,
    270                   NULL,
    271                   NULL)) &&
    272          (EAGAIN != errno) )
    273     {
    274       fprintf (stderr,
    275                "Failure waiting on termination signal: %s\n",
    276                strerror (errno));
    277       break;
    278     }
    279     if (FD_ISSET (finsig,
    280                   &r))
    281       break;
    282   }
    283   if ( (FD_ISSET (finsig,
    284                   &r)) &&
    285        (1 != read (finsig,
    286                    &c,
    287                    1)) )
    288   {
    289     fprintf (stderr,
    290              "Failed to drain termination signal\n");
    291   }
    292 }
    293 
    294 
    295 void
    296 MHDT_server_run_blocking (void *cls,
    297                           int finsig,
    298                           struct MHD_Daemon *d)
    299 {
    300   fd_set r;
    301   char c;
    302 
    303   (void) cls; /* Unused */
    304   (void) d;   /* Unused */
    305 
    306   FD_ZERO (&r);
    307   FD_SET (finsig, &r);
    308   while (1)
    309   {
    310     struct timeval timeout = {
    311       .tv_usec = 1000 /* 1000 microseconds */
    312     };
    313 
    314     if ( (-1 ==
    315           select (finsig + 1,
    316                   &r,
    317                   NULL,
    318                   NULL,
    319                   &timeout)) &&
    320          (EAGAIN != errno) )
    321     {
    322       fprintf (stderr,
    323                "Failure waiting on termination signal: %s\n",
    324                strerror (errno));
    325       break;
    326     }
    327 #ifdef FIXME
    328     if (MHD_SC_OK !=
    329         MHD_daemon_process_blocking (d,
    330                                      1000))
    331     {
    332       fprintf (stderr,
    333                "Failure running MHD_daemon_process_blocking()\n");
    334       break;
    335     }
    336 #else
    337     abort ();
    338 #endif
    339   }
    340   if ( (FD_ISSET (finsig,
    341                   &r)) &&
    342        (1 != read (finsig,
    343                    &c,
    344                    1)) )
    345   {
    346     fprintf (stderr,
    347              "Failed to drain termination signal\n");
    348   }
    349 }
    350 
    351 
    352 #ifdef MHD_SUPPORT_EPOLL
    353 static int my_epoll_fd = -1;
    354 
    355 
    356 /**
    357  * The callback for registration/de-registration of the sockets to watch.
    358  *
    359  * This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(),
    360  * #MHD_daemon_add_connection().
    361  *
    362  * @param cls the closure
    363  * @param fd the socket to watch
    364  * @param watch_for the states of the @a fd to watch, if set to
    365  *                  #MHD_FD_STATE_NONE the socket must be de-registred
    366  * @param app_cntx_old the old application defined context for the socket,
    367  *                     NULL if @a fd socket was not registered before
    368  * @param ecb_cntx the context handle to be used
    369  *                 with #MHD_daemon_event_update()
    370  * @return NULL if error (to connection will be aborted),
    371  *         or the new socket context
    372  * @ingroup event
    373  */
    374 static void *
    375 update_fd (
    376   void *cls,
    377   MHD_Socket fd,
    378   enum MHD_FdState watch_for,
    379   MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
    380   struct MHD_EventUpdateContext *ecb_cntx)
    381 {
    382   struct epoll_event ev;
    383 
    384   (void) cls;
    385   if (watch_for == MHD_FD_STATE_NONE)
    386   {
    387     epoll_ctl (my_epoll_fd,
    388                EPOLL_CTL_DEL,
    389                fd,
    390                &ev /* for Linux 2.6.9-compatibility */);
    391     return NULL;
    392   }
    393   ev.data.ptr = ecb_cntx;
    394   ev.events = 0;
    395   if (0 != (watch_for & MHD_FD_STATE_RECV))
    396     ev.events |= EPOLLIN;
    397   if (0 != (watch_for & MHD_FD_STATE_SEND))
    398     ev.events |= EPOLLOUT;
    399   if (0 != (watch_for & MHD_FD_STATE_EXCEPT))
    400     ev.events |= EPOLLHUP;
    401   if (0 !=
    402       epoll_ctl (my_epoll_fd,
    403                  NULL == app_cntx_old
    404                  ? EPOLL_CTL_ADD
    405                  : EPOLL_CTL_MOD,
    406                  fd,
    407                  &ev))
    408   {
    409     fprintf (stderr,
    410              "epoll_ctl failed: %s\n",
    411              strerror (errno));
    412     return NULL;
    413   }
    414   return ecb_cntx;
    415 }
    416 
    417 
    418 const char *
    419 MHDT_server_setup_external (const void *cls,
    420                             struct MHD_Daemon *d)
    421 {
    422   (void) cls;
    423   if (MHD_SC_OK !=
    424       MHD_DAEMON_SET_OPTIONS (
    425         d,
    426         MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd,
    427                                                       NULL)))
    428     return "Failed to configure external mode!";
    429   if (MHD_SC_OK !=
    430       MHD_DAEMON_SET_OPTIONS (
    431         d,
    432         MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (2000),
    433         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
    434                                 get_port ())))
    435     return "Failed to bind to port 0!";
    436   my_epoll_fd = epoll_create1 (0);
    437 
    438   return NULL;
    439 }
    440 
    441 
    442 void
    443 MHDT_server_run_external (void *cls,
    444                           int finsig,
    445                           struct MHD_Daemon *d)
    446 {
    447   fd_set r;
    448 
    449   (void) cls; /* Unused */
    450   if (-1 == my_epoll_fd)
    451     abort ();
    452   while (1)
    453   {
    454     uint_fast64_t next_wait;
    455     struct timeval timeout;
    456 
    457     if (MHD_SC_OK !=
    458         MHD_daemon_process_reg_events (d,
    459                                        &next_wait))
    460     {
    461       fprintf (stderr,
    462                "MHD_daemon_process_reg_events() failed\n");
    463       break;
    464     }
    465 #ifdef HAVE_TIME_H
    466     timeout.tv_sec = (time_t) (next_wait / 1000u);
    467 #else
    468     timeout.tv_sec = (long) (next_wait / 1000u);
    469 #endif
    470 #ifdef HAVE_SUSECONDS_T
    471     timeout.tv_usec = (suseconds_t) ((next_wait % 1000u) * 1000u);
    472 #else
    473     timeout.tv_usec = (long) ((next_wait % 1000u) * 1000u);
    474 #endif
    475 
    476     FD_ZERO (&r);
    477     FD_SET (finsig,
    478             &r);
    479     FD_SET (my_epoll_fd,
    480             &r);
    481     if ( (-1 ==
    482           select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1,
    483                   &r,
    484                   NULL,
    485                   NULL,
    486                   &timeout)) &&
    487          (EAGAIN != errno) )
    488     {
    489       fprintf (stderr,
    490                "Failure in select(): %s\n",
    491                strerror (errno));
    492       break;
    493     }
    494     if (FD_ISSET (finsig,
    495                   &r))
    496       break;
    497     if (FD_ISSET (my_epoll_fd,
    498                   &r))
    499     {
    500       int maxevents = 40;
    501       struct epoll_event events[maxevents];
    502       int n;
    503       int i;
    504 
    505       n = epoll_wait (my_epoll_fd,
    506                       events,
    507                       maxevents,
    508                       0);
    509       if (-1 == n)
    510       {
    511         fprintf (stderr,
    512                  "epoll_wait() failed: %s\n",
    513                  strerror (errno));
    514         break;
    515       }
    516       for (i = 0; i < n; i++)
    517       {
    518         enum MHD_FdState state = MHD_FD_STATE_NONE;
    519 
    520         if (0 != (events[i].events & EPOLLIN))
    521           MHD_FD_STATE_SET_RECV (state);
    522         if (0 != (events[i].events & EPOLLOUT))
    523           MHD_FD_STATE_SET_SEND (state);
    524         if (0 != (events[i].events & (EPOLLERR | EPOLLHUP)) )
    525           MHD_FD_STATE_SET_EXCEPT (state);
    526         MHD_daemon_event_update (
    527           d,
    528           (struct MHD_EventUpdateContext *) events[i].data.ptr,
    529           state);
    530       }
    531     }
    532   }
    533 
    534   {
    535     char c;
    536 
    537     if ( (FD_ISSET (finsig,
    538                     &r)) &&
    539          (1 != read (finsig,
    540                      &c,
    541                      1)) )
    542     {
    543       fprintf (stderr,
    544                "Failed to drain termination signal\n");
    545     }
    546   }
    547 
    548   close (my_epoll_fd);
    549   my_epoll_fd = -1;
    550 }
    551 
    552 
    553 #endif /* MHD_SUPPORT_EPOLL */