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