exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mhd2_run.c (9586B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2019-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file mhd2_run.c
     18  * @brief API for running an MHD daemon with the
     19  *        GNUnet scheduler
     20  * @author Christian Grothoff
     21  */
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <jansson.h>
     24 #define MHD_APP_SOCKET_CNTX_TYPE struct SocketContext
     25 #include <microhttpd2.h>
     26 #include "taler/taler_mhd2_lib.h"
     27 
     28 
     29 /**
     30  * Context to track whatever MHD wants us to wait for.
     31  */
     32 struct SocketContext
     33 {
     34   /**
     35    * Task for this socket.
     36    */
     37   struct GNUNET_SCHEDULER_Task *mhd_rtask;
     38 
     39   /**
     40    * Task for this socket.
     41    */
     42   struct GNUNET_SCHEDULER_Task *mhd_wtask;
     43 
     44   /**
     45    * Internal handle to pass to MHD when ready.
     46    */
     47   struct MHD_EventUpdateContext *ecb_cntx;
     48 
     49   /**
     50    * Socket to watch for.
     51    */
     52   struct GNUNET_NETWORK_Handle *fd;
     53 
     54   /**
     55    * Daemon this socket is about.
     56    */
     57   struct DaemonEntry *de;
     58 };
     59 
     60 
     61 /**
     62  * Entry in list of HTTP servers we are running.
     63  */
     64 struct DaemonEntry
     65 {
     66   /**
     67    * Kept in a DLL.
     68    */
     69   struct DaemonEntry *next;
     70 
     71   /**
     72    * Kept in a DLL.
     73    */
     74   struct DaemonEntry *prev;
     75 
     76   /**
     77    * The actual daemon.
     78    */
     79   struct MHD_Daemon *mhd;
     80 
     81   /**
     82    * Task running the HTTP server.
     83    */
     84   struct GNUNET_SCHEDULER_Task *mhd_task;
     85 
     86   /**
     87    * Task waiting for timeout on the HTTP server.
     88    */
     89   struct GNUNET_SCHEDULER_Task *timeout_task;
     90 
     91   /**
     92    * Set to true if we should immediately MHD_run() again.
     93    */
     94   bool triggered;
     95 
     96 };
     97 
     98 
     99 /**
    100  * Head of list of HTTP servers.
    101  */
    102 static struct DaemonEntry *mhd_head;
    103 
    104 /**
    105  * Tail of list of HTTP servers.
    106  */
    107 static struct DaemonEntry *mhd_tail;
    108 
    109 
    110 /**
    111  * Function that queries MHD's select sets and
    112  * starts the task waiting for them.
    113  *
    114  * @param[in,out] de daemon to start tasks for
    115  */
    116 static void
    117 prepare_daemon (struct DaemonEntry *de);
    118 
    119 
    120 /**
    121  * Trigger MHD on timeout.
    122  *
    123  * @param cls a `struct DaemonEntry`
    124  */
    125 static void
    126 handle_timeout (void *cls)
    127 {
    128   struct DaemonEntry *de = cls;
    129 
    130   de->timeout_task = NULL;
    131   prepare_daemon (de);
    132 }
    133 
    134 
    135 static void
    136 prepare_daemon (struct DaemonEntry *de)
    137 {
    138   uint_fast64_t next_max_wait;
    139   enum MHD_StatusCode sc;
    140 
    141   sc = MHD_daemon_process_reg_events (de->mhd,
    142                                       &next_max_wait);
    143   if (MHD_SC_OK != sc)
    144   {
    145     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    146                 "MHD_daemon_process_reg_events failed: %d\n",
    147                 (int) sc);
    148     return;
    149   }
    150   if (MHD_WAIT_INDEFINITELY == next_max_wait)
    151     return;
    152   de->timeout_task
    153     = GNUNET_SCHEDULER_add_delayed (
    154         GNUNET_TIME_relative_multiply (
    155           GNUNET_TIME_UNIT_MICROSECONDS,
    156           next_max_wait),
    157         &handle_timeout,
    158         de);
    159 }
    160 
    161 
    162 /**
    163  * Call MHD to process pending requests and then go back
    164  * and schedule the next run.
    165  *
    166  * @param cls our `struct DaemonEntry *`
    167  */
    168 static void
    169 run_daemon (void *cls)
    170 {
    171   struct DaemonEntry *de = cls;
    172 
    173   de->mhd_task = NULL;
    174   prepare_daemon (de);
    175 }
    176 
    177 
    178 /**
    179  * Called whenever MHD should process read-events on the socket.
    180  *
    181  * @param cls a `struct SocketContext`
    182  */
    183 static void
    184 mhd_rready (void *cls)
    185 {
    186   struct SocketContext *sc = cls;
    187   struct DaemonEntry *de = sc->de;
    188 
    189   sc->mhd_rtask = NULL;
    190   MHD_daemon_event_update (de->mhd,
    191                            sc->ecb_cntx,
    192                            MHD_FD_STATE_RECV);
    193   if (NULL != de->mhd_task)
    194     GNUNET_SCHEDULER_cancel (de->mhd_task);
    195   de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
    196                                            de);
    197 }
    198 
    199 
    200 /**
    201  * Called whenever MHD should process write-events on the socket.
    202  *
    203  * @param cls a `struct SocketContext`
    204  */
    205 static void
    206 mhd_wready (void *cls)
    207 {
    208   struct SocketContext *sc = cls;
    209   struct DaemonEntry *de = sc->de;
    210 
    211   sc->mhd_wtask = NULL;
    212   MHD_daemon_event_update (de->mhd,
    213                            sc->ecb_cntx,
    214                            MHD_FD_STATE_SEND);
    215   if (NULL != de->mhd_task)
    216     GNUNET_SCHEDULER_cancel (de->mhd_task);
    217   de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
    218                                            de);
    219 }
    220 
    221 
    222 /**
    223  * Callback for registration/de-registration of the sockets to watch.
    224  *
    225  * @param cls our `struct DaemonEntry`
    226  * @param fd the socket to watch
    227  * @param watch_for the states of the @a fd to watch, if set to
    228  *                  #MHD_FD_STATE_NONE the socket must be de-registred
    229  * @param app_cntx_old the old application defined context for the socket,
    230  *                     NULL if @a fd socket was not registered before
    231  * @param ecb_cntx the context handle to be used
    232  *                 with #MHD_daemon_event_update()
    233  * @return NULL if error (to connection will be aborted),
    234  *         or the new socket context
    235  */
    236 static MHD_APP_SOCKET_CNTX_TYPE *
    237 socket_registration_update (
    238   void *cls,
    239   MHD_Socket fd,
    240   enum MHD_FdState watch_for,
    241   MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
    242   struct MHD_EventUpdateContext *ecb_cntx)
    243 {
    244   struct DaemonEntry *de = cls;
    245 
    246   if (NULL == app_cntx_old)
    247   {
    248     app_cntx_old = GNUNET_new (struct SocketContext);
    249     app_cntx_old->ecb_cntx = ecb_cntx;
    250     app_cntx_old->fd = GNUNET_NETWORK_socket_box_native (fd);
    251     app_cntx_old->de = de;
    252   }
    253   if (MHD_FD_STATE_NONE == watch_for)
    254   {
    255     if (NULL != app_cntx_old->mhd_rtask)
    256       GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_rtask);
    257     if (NULL != app_cntx_old->mhd_wtask)
    258       GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_wtask);
    259     GNUNET_NETWORK_socket_free_memory_only_ (app_cntx_old->fd);
    260     GNUNET_free (app_cntx_old);
    261     return NULL;
    262   }
    263   if ( (MHD_FD_STATE_RECV & watch_for) &&
    264        (NULL == app_cntx_old->mhd_rtask) )
    265   {
    266     app_cntx_old->mhd_rtask
    267       = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
    268                                        app_cntx_old->fd,
    269                                        &mhd_rready,
    270                                        app_cntx_old);
    271   }
    272   if ( (MHD_FD_STATE_SEND & watch_for) &&
    273        (NULL == app_cntx_old->mhd_wtask) )
    274   {
    275     app_cntx_old->mhd_wtask
    276       = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
    277                                         app_cntx_old->fd,
    278                                         &mhd_wready,
    279                                         app_cntx_old);
    280   }
    281   if ( (0 == (MHD_FD_STATE_RECV & watch_for)) &&
    282        (NULL != app_cntx_old->mhd_rtask) )
    283   {
    284     GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_rtask);
    285     app_cntx_old->mhd_rtask = NULL;
    286   }
    287   if ( (0 == (MHD_FD_STATE_SEND & watch_for)) &&
    288        (NULL != app_cntx_old->mhd_wtask) )
    289   {
    290     GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_wtask);
    291     app_cntx_old->mhd_wtask = NULL;
    292   }
    293   return app_cntx_old;
    294 }
    295 
    296 
    297 void
    298 TALER_MHD2_daemon_start (struct MHD_Daemon *daemon)
    299 {
    300   struct DaemonEntry *de;
    301   enum MHD_StatusCode sc;
    302 
    303   de = GNUNET_new (struct DaemonEntry);
    304   GNUNET_assert (MHD_SC_OK ==
    305                  MHD_DAEMON_SET_OPTIONS (
    306                    daemon,
    307                    MHD_D_OPTION_REREGISTER_ALL (true),
    308                    MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (
    309                      &socket_registration_update,
    310                      de)));
    311   de->mhd = daemon;
    312   sc = MHD_daemon_start (de->mhd);
    313   if (MHD_SC_OK != sc)
    314   {
    315     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    316                 "MHD_daemon_start failed: %d\n",
    317                 (int) sc);
    318     GNUNET_free (de);
    319     return;
    320   }
    321   GNUNET_CONTAINER_DLL_insert (mhd_head,
    322                                mhd_tail,
    323                                de);
    324   prepare_daemon (de);
    325 }
    326 
    327 
    328 void
    329 TALER_MHD2_daemons_halt (void)
    330 {
    331   for (struct DaemonEntry *de = mhd_head;
    332        NULL != de;
    333        de = de->next)
    334   {
    335     if (NULL != de->mhd_task)
    336     {
    337       GNUNET_SCHEDULER_cancel (de->mhd_task);
    338       de->mhd_task = NULL;
    339     }
    340     de->triggered = false;
    341   }
    342 }
    343 
    344 
    345 void
    346 TALER_MHD2_daemons_quiesce (void)
    347 {
    348   for (struct DaemonEntry *de = mhd_head;
    349        NULL != de;
    350        de = de->next)
    351   {
    352 #if FIXME_MHD2
    353     int fd;
    354 #endif
    355 
    356     if (NULL != de->mhd_task)
    357     {
    358       GNUNET_SCHEDULER_cancel (de->mhd_task);
    359       de->mhd_task = NULL;
    360     }
    361     de->triggered = false;
    362 #if FIXME_MHD2
    363     fd = MHD_daemon_quiesce (de->mhd);
    364     GNUNET_break (0 == close (fd));
    365 #endif
    366   }
    367 }
    368 
    369 
    370 void
    371 TALER_MHD2_daemons_destroy (void)
    372 {
    373   struct DaemonEntry *de;
    374 
    375   while (NULL != (de = mhd_head))
    376   {
    377     struct MHD_Daemon *mhd = de->mhd;
    378 
    379     if (NULL != de->mhd_task)
    380     {
    381       GNUNET_SCHEDULER_cancel (de->mhd_task);
    382       de->mhd_task = NULL;
    383     }
    384     MHD_daemon_destroy (mhd);
    385     GNUNET_CONTAINER_DLL_remove (mhd_head,
    386                                  mhd_tail,
    387                                  de);
    388     GNUNET_free (de);
    389   }
    390 }
    391 
    392 
    393 void
    394 TALER_MHD2_daemons_trigger (void)
    395 {
    396   for (struct DaemonEntry *de = mhd_head;
    397        NULL != de;
    398        de = de->next)
    399   {
    400     if (NULL != de->mhd_task)
    401     {
    402       GNUNET_SCHEDULER_cancel (de->mhd_task);
    403       de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
    404                                                de);
    405     }
    406     else
    407     {
    408       de->triggered = true;
    409     }
    410   }
    411 }
    412 
    413 
    414 /* end of mhd2_run.c */