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 (9614B)


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