diff options
author | Christian Grothoff <christian@grothoff.org> | 2024-06-03 13:37:56 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2024-07-29 12:18:42 +0200 |
commit | b65481ad8fae5fe7fad9cb0253449fdc80bf7ff8 (patch) | |
tree | 52622c63602cd70bca663e9aee745ff0898f2cae /src/mhd | |
parent | 4968c2ed31d8d76af3f2864a06c453982f3898ec (diff) |
add spa logic
Diffstat (limited to 'src/mhd')
-rw-r--r-- | src/mhd/Makefile.am | 3 | ||||
-rw-r--r-- | src/mhd/mhd_spa.c | 367 |
2 files changed, 369 insertions, 1 deletions
diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am index 703e34ca4..563ebf398 100644 --- a/src/mhd/Makefile.am +++ b/src/mhd/Makefile.am @@ -14,7 +14,8 @@ libtalermhd_la_SOURCES = \ mhd_legal.c \ mhd_parsing.c \ mhd_responses.c \ - mhd_run.c + mhd_run.c \ + mhd_spa.c libtalermhd_la_LDFLAGS = \ -version-info 3:1:3 \ -no-undefined diff --git a/src/mhd/mhd_spa.c b/src/mhd/mhd_spa.c new file mode 100644 index 000000000..c4aff281d --- /dev/null +++ b/src/mhd/mhd_spa.c @@ -0,0 +1,367 @@ +/* + This file is part of TALER + Copyright (C) 2020, 2023, 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file mhd_spa.c + * @brief logic to load single page apps + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_util.h" +#include "taler_mhd_lib.h" +#include <gnunet/gnunet_mhd_compat.h> + + +/** + * Resource from the WebUi. + */ +struct WebuiFile +{ + /** + * Kept in a DLL. + */ + struct WebuiFile *next; + + /** + * Kept in a DLL. + */ + struct WebuiFile *prev; + + /** + * Path this resource matches. + */ + char *path; + + /** + * SPA resource, compressed. + */ + struct MHD_Response *zresponse; + + /** + * SPA resource, vanilla. + */ + struct MHD_Response *response; + +}; + + +/** + * Resource from the WebUi. + */ +struct TALER_MHD_Spa +{ + /** + * Resources of the WebUI, kept in a DLL. + */ + struct WebuiFile *webui_head; + + /** + * Resources of the WebUI, kept in a DLL. + */ + struct WebuiFile *webui_tail; +}; + + +MHD_RESULT +TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa, + struct MHD_Connection *connection, + const char *path) +{ + struct WebuiFile *w = NULL; + + if ( (NULL == path) || + (0 == strcmp (path, + "")) ) + path = "index.html"; + for (struct WebuiFile *pos = spa->webui_head; + NULL != pos; + pos = pos->next) + if (0 == strcmp (path, + pos->path)) + { + w = pos; + break; + } + if (NULL == w) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + path); + if ( (MHD_YES == + TALER_MHD_can_compress (connection)) && + (NULL != w->zresponse) ) + return MHD_queue_response (connection, + MHD_HTTP_OK, + w->zresponse); + return MHD_queue_response (connection, + MHD_HTTP_OK, + w->response); +} + + +/** + * Function called on each file to load for the WebUI. + * + * @param cls the `struct TALER_MHD_Spa *` to build + * @param dn name of the file to load + */ +static enum GNUNET_GenericReturnValue +build_webui (void *cls, + const char *dn) +{ + static const struct + { + const char *ext; + const char *mime; + } mime_map[] = { + { + .ext = "css", + .mime = "text/css" + }, + { + .ext = "html", + .mime = "text/html" + }, + { + .ext = "js", + .mime = "text/javascript" + }, + { + .ext = "jpg", + .mime = "image/jpeg" + }, + { + .ext = "jpeg", + .mime = "image/jpeg" + }, + { + .ext = "png", + .mime = "image/png" + }, + { + .ext = "svg", + .mime = "image/svg+xml" + }, + { + .ext = NULL, + .mime = NULL + }, + }; + struct TALER_MHD_Spa *spa = cls; + int fd; + struct stat sb; + struct MHD_Response *zresponse = NULL; + struct MHD_Response *response; + const char *ext; + const char *mime; + + fd = open (dn, + O_RDONLY); + if (-1 == fd) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + return GNUNET_SYSERR; + } + if (0 != + fstat (fd, + &sb)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + + mime = NULL; + ext = strrchr (dn, '.'); + if (NULL == ext) + { + GNUNET_break (0 == close (fd)); + return GNUNET_OK; + } + ext++; + for (unsigned int i = 0; NULL != mime_map[i].ext; i++) + if (0 == strcasecmp (ext, + mime_map[i].ext)) + { + mime = mime_map[i].mime; + break; + } + + { + void *in; + ssize_t r; + size_t csize; + + in = GNUNET_malloc_large (sb.st_size); + if (NULL == in) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "malloc"); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + r = read (fd, + in, + sb.st_size); + if ( (-1 == r) || + (sb.st_size != (size_t) r) ) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "read", + dn); + GNUNET_free (in); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + csize = (size_t) r; + if (MHD_YES == + TALER_MHD_body_compress (&in, + &csize)) + { + zresponse = MHD_create_response_from_buffer (csize, + in, + MHD_RESPMEM_MUST_FREE); + if (NULL != zresponse) + { + if (MHD_NO == + MHD_add_response_header (zresponse, + MHD_HTTP_HEADER_CONTENT_ENCODING, + "deflate")) + { + GNUNET_break (0); + MHD_destroy_response (zresponse); + zresponse = NULL; + } + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (zresponse, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); + } + } + else + { + GNUNET_free (in); + } + } + + response = MHD_create_response_from_fd (sb.st_size, + fd); + if (NULL == response) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + GNUNET_break (0 == close (fd)); + if (NULL != zresponse) + { + MHD_destroy_response (zresponse); + zresponse = NULL; + } + return GNUNET_SYSERR; + } + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); + + { + struct WebuiFile *w; + const char *fn; + + fn = strrchr (dn, '/'); + GNUNET_assert (NULL != fn); + w = GNUNET_new (struct WebuiFile); + w->path = GNUNET_strdup (fn + 1); + w->response = response; + w->zresponse = zresponse; + GNUNET_CONTAINER_DLL_insert (spa->webui_head, + spa->webui_tail, + w); + } + return GNUNET_OK; +} + + +struct TALER_MHD_Spa * +TALER_MHD_spa_load (const char *dir) +{ + struct TALER_MHD_Spa *spa; + char *dn; + + { + char *path; + + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + if (NULL == path) + { + GNUNET_break (0); + return NULL; + } + GNUNET_asprintf (&dn, + "%s%s", + path, + dir); + GNUNET_free (path); + } + spa = GNUNET_new (struct TALER_MHD_Spa); + if (-1 == + GNUNET_DISK_directory_scan (dn, + &build_webui, + spa)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load WebUI from `%s'\n", + dn); + GNUNET_free (dn); + TALER_MHD_spa_free (spa); + return NULL; + } + GNUNET_free (dn); + return spa; +} + + +void +TALER_MHD_spa_free (struct TALER_MHD_Spa *spa) +{ + struct WebuiFile *w; + + while (NULL != (w = spa->webui_head)) + { + GNUNET_CONTAINER_DLL_remove (spa->webui_head, + spa->webui_tail, + w); + if (NULL != w->response) + { + MHD_destroy_response (w->response); + w->response = NULL; + } + if (NULL != w->zresponse) + { + MHD_destroy_response (w->zresponse); + w->zresponse = NULL; + } + GNUNET_free (w->path); + GNUNET_free (w); + } + GNUNET_free (spa); +} |