From 97a5ce5c22572c30e44b076ed51df6666fde25cc Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 21 Jan 2023 20:15:44 +0100 Subject: implement new spa loader logic --- src/backend/taler-merchant-httpd.c | 59 ++++++- src/backend/taler-merchant-httpd_spa.c | 245 +++++++++++++++++++++++------ src/backend/taler-merchant-httpd_spa.h | 10 +- src/backend/taler-merchant-httpd_statics.c | 10 +- src/backend/taler-merchant-httpd_statics.h | 4 +- 5 files changed, 272 insertions(+), 56 deletions(-) (limited to 'src/backend') diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 9366c1bf..8e1a0fc0 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -459,6 +459,49 @@ handle_server_options (const struct TMH_RequestHandler *rh, } +static MHD_RESULT +spa_redirect (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + const char *text = "Redirecting to /webui/"; + struct MHD_Response *response; + + response = MHD_create_response_from_buffer (strlen (text), + (void *) text, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + TALER_MHD_add_global_headers (response); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain")); + if (MHD_NO == + MHD_add_response_header (response, + MHD_HTTP_HEADER_LOCATION, + "/webui/")) + { + GNUNET_break (0); + MHD_destroy_response (response); + return MHD_NO; + } + + { + MHD_RESULT ret; + + ret = MHD_queue_response (connection, + MHD_HTTP_FOUND, + response); + MHD_destroy_response (response); + return ret; + } +} + + /** * Extract the token from authorization header value @a auth. * @@ -637,7 +680,7 @@ url_handler (void *cls, { .url_prefix = "/instances/", .method = MHD_HTTP_METHOD_DELETE, - .skip_instance = true, + .skip_instance = true, .default_only = true, .have_id_segment = true, .handler = &TMH_private_delete_instances_default_ID @@ -1034,6 +1077,15 @@ url_handler (void *cls, .method = MHD_HTTP_METHOD_GET, .mime_type = "text/html", .skip_instance = true, + .handler = &spa_redirect, + .response_code = MHD_HTTP_FOUND + }, + { + .url_prefix = "/webui/", + .method = MHD_HTTP_METHOD_GET, + .mime_type = "text/html", + .skip_instance = true, + .have_id_segment = true, .handler = &TMH_return_spa, .response_code = MHD_HTTP_OK }, @@ -1381,7 +1433,7 @@ url_handler (void *cls, { prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */ infix_url = slash + 1; - slash = strchr (&infix_url[1], '/'); + slash = strchr (infix_url, '/'); if (NULL == slash) { /* the infix was the rest */ @@ -1903,7 +1955,8 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - TMH_statics_init (); + /* /static/ is currently not used */ + /* (void) TMH_statics_init (); */ elen = TMH_EXCHANGES_init (config); if (GNUNET_SYSERR == elen) { diff --git a/src/backend/taler-merchant-httpd_spa.c b/src/backend/taler-merchant-httpd_spa.c index 0258f883..92cc5bf3 100644 --- a/src/backend/taler-merchant-httpd_spa.c +++ b/src/backend/taler-merchant-httpd_spa.c @@ -27,14 +27,47 @@ /** - * SPA, compressed. + * Resource from the WebUi. */ -static struct MHD_Response *zspa; +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 *zspa; + + /** + * SPA resource, vanilla. + */ + struct MHD_Response *spa; + +}; + + +/** + * Resources of the WebuUI, kept in a DLL. + */ +static struct WebuiFile *webui_head; /** - * SPA, vanilla. + * Resources of the WebuUI, kept in a DLL. */ -static struct MHD_Response *spa; +static struct WebuiFile *webui_tail; MHD_RESULT @@ -42,40 +75,95 @@ TMH_return_spa (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc) { + struct WebuiFile *w = NULL; + const char *infix = hc->infix; + + if ( (NULL == infix) || + (0 == strcmp (infix, + "")) ) + infix = "index.html"; + for (struct WebuiFile *pos = webui_head; + NULL != pos; + pos = pos->next) + if (0 == strcmp (infix, + pos->path)) + { + w = pos; + break; + } + if (NULL == w) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + hc->url); if ( (MHD_YES == TALER_MHD_can_compress (connection)) && - (NULL != zspa) ) + (NULL != w->zspa) ) return MHD_queue_response (connection, MHD_HTTP_OK, - zspa); + w->zspa); return MHD_queue_response (connection, MHD_HTTP_OK, - spa); + w->spa); } -int -TMH_spa_init () +/** + * Function called on each file to load for the WebUI. + * + * @param cls NULL + * @param dn name of the file to load + */ +static enum GNUNET_GenericReturnValue +build_webui (void *cls, + const char *dn) { - char *dn; int fd; struct stat sb; - + struct MHD_Response *zspa; + struct MHD_Response *spa; + const char *ext; + const char *mime; + struct { - char *path; - - path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); - if (NULL == path) + const char *ext; + const char *mime; + } mime_map[] = { { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_asprintf (&dn, - "%s/merchant/spa/spa.html", - path); - GNUNET_free (path); - } + .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 + }, + }; + (void) cls; /* finally open template */ fd = open (dn, O_RDONLY); @@ -84,7 +172,6 @@ TMH_spa_init () GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dn); - GNUNET_free (dn); return GNUNET_SYSERR; } if (0 != @@ -95,10 +182,21 @@ TMH_spa_init () "open", dn); GNUNET_break (0 == close (fd)); - GNUNET_free (dn); return GNUNET_SYSERR; } + mime = NULL; + ext = strrchr (dn, '.'); + GNUNET_assert (NULL != ext); + 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; @@ -110,7 +208,6 @@ TMH_spa_init () GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc"); GNUNET_break (0 == close (fd)); - GNUNET_free (dn); return GNUNET_SYSERR; } r = read (fd, @@ -124,7 +221,6 @@ TMH_spa_init () dn); GNUNET_free (in); GNUNET_break (0 == close (fd)); - GNUNET_free (dn); return GNUNET_SYSERR; } csize = (size_t) r; @@ -146,10 +242,11 @@ TMH_spa_init () MHD_destroy_response (zspa); zspa = NULL; } - GNUNET_break (MHD_YES == - MHD_add_response_header (zspa, - MHD_HTTP_HEADER_CONTENT_TYPE, - "text/html")); + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (zspa, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); } } else @@ -166,7 +263,6 @@ TMH_spa_init () "open", dn); GNUNET_break (0 == close (fd)); - GNUNET_free (dn); if (NULL != zspa) { MHD_destroy_response (zspa); @@ -174,11 +270,62 @@ TMH_spa_init () } return GNUNET_SYSERR; } + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (spa, + 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->spa = spa; + w->zspa = zspa; + GNUNET_CONTAINER_DLL_insert (webui_head, + webui_tail, + w); + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TMH_spa_init () +{ + char *dn; + + { + char *path; + + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + if (NULL == path) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_asprintf (&dn, + "%s/merchant-backoffice/", + path); + GNUNET_free (path); + } + + if (-1 == + GNUNET_DISK_directory_scan (dn, + &build_webui, + NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load WebUI from `%s'\n", + dn); + GNUNET_free (dn); + return GNUNET_SYSERR; + } GNUNET_free (dn); - GNUNET_break (MHD_YES == - MHD_add_response_header (spa, - MHD_HTTP_HEADER_CONTENT_TYPE, - "text/html")); return GNUNET_OK; } @@ -189,14 +336,24 @@ TMH_spa_init () void __attribute__ ((destructor)) get_spa_fini () { - if (NULL != spa) - { - MHD_destroy_response (spa); - spa = NULL; - } - if (NULL != zspa) + struct WebuiFile *w; + + while (NULL != (w = webui_head)) { - MHD_destroy_response (zspa); - zspa = NULL; + GNUNET_CONTAINER_DLL_remove (webui_head, + webui_tail, + w); + if (NULL != w->spa) + { + MHD_destroy_response (w->spa); + w->spa = NULL; + } + if (NULL != w->zspa) + { + MHD_destroy_response (w->zspa); + w->zspa = NULL; + } + GNUNET_free (w->path); + GNUNET_free (w); } } diff --git a/src/backend/taler-merchant-httpd_spa.h b/src/backend/taler-merchant-httpd_spa.h index 8a998c38..9450bdbd 100644 --- a/src/backend/taler-merchant-httpd_spa.h +++ b/src/backend/taler-merchant-httpd_spa.h @@ -24,8 +24,9 @@ #include #include "taler-merchant-httpd.h" + /** - * Return our single-page-app user interface (see contrib/spa.html). + * Return our single-page-app user interface (see contrib/spa/). * * @param rh request handler * @param connection the connection we act upon @@ -37,10 +38,13 @@ TMH_return_spa (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc); + /** - * Preload and compress SPA file. + * Preload and compress SPA files. + * + * @return #GNUNET_OK on success */ -int +enum GNUNET_GenericReturnValue TMH_spa_init (void); diff --git a/src/backend/taler-merchant-httpd_statics.c b/src/backend/taler-merchant-httpd_statics.c index 6b4ff351..72f81d85 100644 --- a/src/backend/taler-merchant-httpd_statics.c +++ b/src/backend/taler-merchant-httpd_statics.c @@ -15,7 +15,7 @@ */ /** * @file taler-merchant-httpd_statics.c - * @brief logic to load and complete HTML templates + * @brief logic to load and return static resource files by client language preference * @author Christian Grothoff */ #include "platform.h" @@ -144,7 +144,7 @@ TMH_return_static (const struct TMH_RequestHandler *rh, * #GNUNET_NO to stop iteration with no error, * #GNUNET_SYSERR to abort iteration with error! */ -static int +static enum GNUNET_GenericReturnValue load_static_file (void *cls, const char *filename) { @@ -275,7 +275,7 @@ load_static_file (void *cls, /** * Preload static files. */ -int +enum GNUNET_GenericReturnValue TMH_statics_init () { char *dn; @@ -291,7 +291,7 @@ TMH_statics_init () return GNUNET_SYSERR; } GNUNET_asprintf (&dn, - "%s/merchant/static/", + "%smerchant/static/", path); GNUNET_free (path); } @@ -300,7 +300,7 @@ TMH_statics_init () NULL); if (-1 == ret) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Could not load static resources from `%s': %s\n", dn, strerror (errno)); diff --git a/src/backend/taler-merchant-httpd_statics.h b/src/backend/taler-merchant-httpd_statics.h index 977a488d..ac3e2ca1 100644 --- a/src/backend/taler-merchant-httpd_statics.h +++ b/src/backend/taler-merchant-httpd_statics.h @@ -40,8 +40,10 @@ TMH_return_static (const struct TMH_RequestHandler *rh, /** * Preload static files. + * + * @return #GNUNET_OK on success */ -int +enum GNUNET_GenericReturnValue TMH_statics_init (void); -- cgit v1.2.3