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 */