gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: implement #6460: serving of stat


From: gnunet
Subject: [taler-merchant] branch master updated: implement #6460: serving of static files
Date: Wed, 19 Aug 2020 19:27:30 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new d6f2827  implement #6460: serving of static files
d6f2827 is described below

commit d6f28270094db1ffa4e5526ce556e7fc23e8e9b9
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Wed Aug 19 19:27:28 2020 +0200

    implement #6460: serving of static files
---
 contrib/Makefile.am                           |   9 +-
 contrib/depleted_tip.en.must                  |   3 +-
 contrib/offer_refund.en.must                  |   3 +-
 contrib/offer_tip.en.must                     |   3 +-
 contrib/pure-min.css                          |  11 +
 contrib/request_payment.en.must               |   3 +-
 contrib/show_order_details.en.must            |   3 +-
 src/backend/Makefile.am                       |   2 +
 src/backend/taler-merchant-httpd.c            |   9 +
 src/backend/taler-merchant-httpd_statics.c    | 336 ++++++++++++++++++++++++++
 src/backend/taler-merchant-httpd_statics.h    |  49 ++++
 src/backend/taler-merchant-httpd_templating.c |   2 +-
 12 files changed, 420 insertions(+), 13 deletions(-)

diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index c362ddd..14826f9 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -1,8 +1,13 @@
-pkgdatadir = $(prefix)/share/taler/merchant/templates/
+tmplpkgdatadir = $(prefix)/share/taler/merchant/templates/
+staticpkgdatadir = $(prefix)/share/taler/merchant/static/
 
-dist_pkgdata_DATA = \
+dist_tmplpkgdata_DATA = \
   depleted_tip.en.must \
   offer_refund.en.must \
   offer_tip.en.must \
   request_payment.en.must \
   show_order_details.en.must
+
+# Note: if you update pure-min.css, you must also update the MUST templates 
above!
+dist_staticpkgdata_DATA = \
+  pure-min.css
diff --git a/contrib/depleted_tip.en.must b/contrib/depleted_tip.en.must
index 694bb72..795f4f1 100644
--- a/contrib/depleted_tip.en.must
+++ b/contrib/depleted_tip.en.must
@@ -23,9 +23,8 @@
     <meta http-equiv="refresh" content="1">
   </noscript>
   <title>Status of your tip</title>
-  <!-- FIXME: inline this? How to best serve this without using 3rd party? -->
   <link rel="stylesheet"
-        href="https://unpkg.com/purecss@2.0.3/build/pure-min.css";
+        href="/static/pure-min.css"
         
integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ"
         crossorigin="anonymous">
   <style>
diff --git a/contrib/offer_refund.en.must b/contrib/offer_refund.en.must
index 62766ad..c6cc9ae 100644
--- a/contrib/offer_refund.en.must
+++ b/contrib/offer_refund.en.must
@@ -23,9 +23,8 @@
     <meta http-equiv="refresh" content="1">
   </noscript>
   <title>Refund available for {{order_summary}}</title>
-  <!-- FIXME-6460: allow taler-merchant-httpd to serve this, so we do not use 
3rd party? -->
   <link rel="stylesheet"
-        href="https://unpkg.com/purecss@2.0.3/build/pure-min.css";
+        href="/static/pure-min.css"
         
integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ"
         crossorigin="anonymous">
   <style>
diff --git a/contrib/offer_tip.en.must b/contrib/offer_tip.en.must
index abc7169..b44f30c 100644
--- a/contrib/offer_tip.en.must
+++ b/contrib/offer_tip.en.must
@@ -23,9 +23,8 @@
     <meta http-equiv="refresh" content="1">
   </noscript>
   <title>Tip available</title>
-  <!-- FIXME-6460: allow taler-merchant-httpd to serve this, so we do not use 
3rd party? -->
   <link rel="stylesheet"
-        href="https://unpkg.com/purecss@2.0.3/build/pure-min.css";
+        href="/static/pure-min.css"
         
integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ"
         crossorigin="anonymous">
   <style>
diff --git a/contrib/pure-min.css b/contrib/pure-min.css
new file mode 100644
index 0000000..7b6e7b9
--- /dev/null
+++ b/contrib/pure-min.css
@@ -0,0 +1,11 @@
+/*!
+Pure v2.0.3
+Copyright 2013 Yahoo!
+Licensed under the BSD License.
+https://github.com/pure-css/pure/blob/master/LICENSE.md
+*/
+/*!
+normalize.css v | MIT License | git.io/normalize
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css 
*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em
 
0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline
 dotted;text-decoration:underline dotted}b,st [...]
\ No newline at end of file
diff --git a/contrib/request_payment.en.must b/contrib/request_payment.en.must
index d781905..1d666fe 100644
--- a/contrib/request_payment.en.must
+++ b/contrib/request_payment.en.must
@@ -23,9 +23,8 @@
     <meta http-equiv="refresh" content="1">
   </noscript>
   <title>Payment required for {{order_summary}}</title>
-  <!-- FIXME-6460: allow taler-merchant-httpd to serve this, so we do not use 
3rd party? -->
   <link rel="stylesheet"
-        href="https://unpkg.com/purecss@2.0.3/build/pure-min.css";
+        href="/static/pure-min.css"
         
integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ"
         crossorigin="anonymous">
   <style>
diff --git a/contrib/show_order_details.en.must 
b/contrib/show_order_details.en.must
index 71e6a56..3688585 100644
--- a/contrib/show_order_details.en.must
+++ b/contrib/show_order_details.en.must
@@ -23,9 +23,8 @@
     <meta http-equiv="refresh" content="1">
   </noscript>
   <title>Status of your order for {{order_summary}}</title>
-  <!-- FIXME: inline this? How to best serve this without using 3rd party? -->
   <link rel="stylesheet"
-        href="https://unpkg.com/purecss@2.0.3/build/pure-min.css";
+        href="/static/pure-min.css"
         
integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ"
         crossorigin="anonymous">
   <style>
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index af86034..60e8125 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -95,6 +95,8 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_qr.h \
   taler-merchant-httpd_reserves.c \
     taler-merchant-httpd_reserves.h \
+  taler-merchant-httpd_statics.c \
+    taler-merchant-httpd_statics.h \
   taler-merchant-httpd_templating.c \
     taler-merchant-httpd_templating.h
 taler_merchant_httpd_LDADD = \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 54b8d16..5c6924c 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -63,6 +63,7 @@
 #include "taler-merchant-httpd_post-orders-ID-refund.h"
 #include "taler-merchant-httpd_post-tips-ID-pickup.h"
 #include "taler-merchant-httpd_reserves.h"
+#include "taler-merchant-httpd_statics.h"
 #include "taler-merchant-httpd_templating.h"
 
 /**
@@ -1147,6 +1148,13 @@ url_handler (void *cls,
          to set a conservative bound for sane wallets */
       .max_upload = 1024 * 1024
     },
+    /* GET /static/ *: */
+    {
+      .url_prefix = "/static/",
+      .method = MHD_HTTP_METHOD_GET,
+      .have_id_segment = true,
+      .handler = &TMH_return_static
+    },
     {
       NULL
     }
@@ -1579,6 +1587,7 @@ run (void *cls,
                                             "FORCE_AUDIT"))
     TMH_force_audit = GNUNET_YES;
   TMH_templating_init ();
+  TMH_statics_init ();
   if (GNUNET_SYSERR ==
       TMH_EXCHANGES_init (config))
   {
diff --git a/src/backend/taler-merchant-httpd_statics.c 
b/src/backend/taler-merchant-httpd_statics.c
new file mode 100644
index 0000000..e8be2cb
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_statics.c
@@ -0,0 +1,336 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020 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 MERCHANTABILITY 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 merchant/backend/taler-merchant-httpd_statics.c
+ * @brief logic to load and complete HTML templates
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_mhd_lib.h>
+#include "taler-merchant-httpd_statics.h"
+#include "../mustach/mustach.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * Entry in a key-value array we use to cache templates.
+ */
+struct TVE
+{
+  /**
+   * A name, used as the key. NULL for the last entry.
+   */
+  char *name;
+
+  /**
+   * Language the template is in.
+   */
+  char *lang;
+
+  /**
+   * Pre-built reply.
+   */
+  struct MHD_Response *reply;
+
+};
+
+
+/**
+ * Array of templates loaded into RAM.
+ */
+static struct TVE *loaded;
+
+/**
+ * Length of the #loaded array.
+ */
+static unsigned int loaded_length;
+
+
+/**
+ * Load Mustach template into memory.  Note that we intentionally cache
+ * failures, that is if we ever failed to load a template, we will never try
+ * again.
+ *
+ * @param connection the connection we act upon
+ * @param name name of the template file to load
+ *        (MUST be a 'static' string in memory!)
+ * @return NULL on error, otherwise the template
+ */
+static const struct TVE *
+lookup_file (struct MHD_Connection *connection,
+             const char *name)
+{
+  struct TVE *best = NULL;
+  const char *lang;
+
+  lang = MHD_lookup_connection_value (connection,
+                                      MHD_HEADER_KIND,
+                                      MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
+  if (NULL == lang)
+    lang = "en";
+  /* find best match by language */
+  for (unsigned int i = 0; i<loaded_length; i++)
+  {
+    if (0 != strcmp (loaded[i].name,
+                     name))
+      continue; /* does not match by name */
+    if (NULL == loaded[i].lang) /* no language == always best match */
+      return &loaded[i];
+    if ( (NULL == best) ||
+         (TALER_language_matches (lang,
+                                  loaded[i].lang) >
+          TALER_language_matches (lang,
+                                  best->lang) ) )
+      best = &loaded[i];
+  }
+  if (NULL == best)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No static file found for `%s'\n",
+                name);
+    return NULL;
+  }
+  return best;
+}
+
+
+/**
+ * Check for the existence of a static file and return the result if it
+ * matches. Otherwise returns a "not found" page.
+ *
+ * @param connection the connection we act upon
+ * @param template basename of the template to load
+ * @param taler_uri value for "Taler:" header to set, or NULL
+ * @param kvc key value pairs to fill in
+ * @return #MHD_YES on success (reply queued), #MHD_NO on error (close 
connection)
+ */
+MHD_RESULT
+TMH_return_static (const struct TMH_RequestHandler *rh,
+                   struct MHD_Connection *connection,
+                   struct TMH_HandlerContext *hc)
+{
+  const struct TVE *tmpl;
+
+  tmpl = lookup_file (connection,
+                      hc->infix);
+  if (NULL == tmpl)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to load static file `%s'\n",
+                hc->infix);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       TALER_EC_ENDPOINT_UNKNOWN,
+                                       hc->infix);
+  }
+
+  return MHD_queue_response (connection,
+                             MHD_HTTP_OK,
+                             tmpl->reply);
+}
+
+
+/**
+ * Function called with a static file's filename.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ *  #GNUNET_NO to stop iteration with no error,
+ *  #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+load_static_file (void *cls,
+                  const char *filename)
+{
+  char *lang;
+  char *end;
+  int fd;
+  struct stat sb;
+  const char *name;
+  struct MHD_Response *reply;
+
+  if ('.' == filename[0])
+    return GNUNET_OK;
+  name = strrchr (filename,
+                  '/');
+  if (NULL == name)
+    name = filename;
+  else
+    name++;
+  lang = strchr (name,
+                 '.');
+  if (NULL == lang)
+    return GNUNET_OK; /* name must include _some_ extension */
+  lang++;
+  end = strchr (lang,
+                '.');
+  if (NULL == end)
+  {
+    /* language was not present, we ONLY have the extension */
+    end = lang;
+    lang = NULL;
+  }
+  /* finally open template */
+  fd = open (filename,
+             O_RDONLY);
+  if (-1 == fd)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              filename);
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      fstat (fd,
+             &sb))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              filename);
+    GNUNET_break (0 == close (fd));
+    return GNUNET_OK;
+  }
+
+  reply = MHD_create_response_from_fd (sb.st_size,
+                                       fd);
+  if (NULL == reply)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              filename);
+    GNUNET_break (0 == close (fd));
+    return GNUNET_OK;
+  }
+
+  {
+    static struct MimeMap
+    {
+      const char *ext;
+      const char *mime;
+    } mm[] = {
+      { .ext = ".css", .mime = "text/css" },
+      { .ext = ".js", .mime = "text/javascript" },
+      { .ext = ".html", .mime = "text/html" },
+      { .ext = ".htm", .mime = "text/html" },
+      { .ext = ".txt", .mime = "text/plain" },
+      { .ext = ".pdf", .mime = "application/pdf" },
+      { .ext = ".jpg", .mime = "image/jpeg" },
+      { .ext = ".jpeg", .mime = "image/jpeg" },
+      { .ext = ".png", .mime = "image/png" },
+      { .ext = ".apng", .mime = "image/apng" },
+      { .ext = ".gif", .mime = "image/gif" },
+      { .ext = ".svg", .mime = "image/svg+xml" },
+      { .ext = ".tiff", .mime = "image/tiff" },
+      { .ext = ".ico", .mime = "image/x-icon" },
+      { .ext = ".bmp", .mime = "image/bmp" },
+      { .ext = ".epub", .mime = "application/epub+zip" },
+      { .ext = ".xml", .mime = "text/xml" },
+      { .ext = NULL, .mime = NULL }
+    };
+    const char *mime;
+
+    mime = NULL;
+    for (unsigned int i = 0; NULL != mm[i].ext; i++)
+      if (0 == strcasecmp (mm[i].ext,
+                           end))
+      {
+        mime = mm[i].mime;
+        break;
+      }
+    if (NULL != mime)
+      GNUNET_break (MHD_NO !=
+                    MHD_add_response_header (reply,
+                                             MHD_HTTP_HEADER_CONTENT_TYPE,
+                                             mime));
+  }
+
+  GNUNET_array_grow (loaded,
+                     loaded_length,
+                     loaded_length + 1);
+  if (NULL != lang)
+  {
+    GNUNET_asprintf (&loaded[loaded_length - 1].name,
+                     "%.*s%s",
+                     (int) (lang - name) - 1,
+                     name,
+                     end);
+    loaded[loaded_length - 1].lang = GNUNET_strndup (lang,
+                                                     end - lang);
+  }
+  else
+  {
+    loaded[loaded_length - 1].name = GNUNET_strdup (name);
+  }
+  loaded[loaded_length - 1].reply = reply;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Preload static files.
+ */
+int
+TMH_statics_init ()
+{
+  char *dn;
+  int ret;
+
+  {
+    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/static/",
+                     path);
+    GNUNET_free (path);
+  }
+  ret = GNUNET_DISK_directory_scan (dn,
+                                    &load_static_file,
+                                    NULL);
+  GNUNET_free (dn);
+  if (-1 == ret)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Nicely shut down.
+ */
+void __attribute__ ((destructor))
+get_statics_fini ()
+{
+  for (unsigned int i = 0; i<loaded_length; i++)
+  {
+    GNUNET_free (loaded[i].name);
+    GNUNET_free (loaded[i].lang);
+    MHD_destroy_response (loaded[i].reply);
+  }
+  GNUNET_array_grow (loaded,
+                     loaded_length,
+                     0);
+}
diff --git a/src/backend/taler-merchant-httpd_statics.h 
b/src/backend/taler-merchant-httpd_statics.h
new file mode 100644
index 0000000..dac06a0
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_statics.h
@@ -0,0 +1,49 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020 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 MERCHANTABILITY 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 merchant/backend/taler-merchant-httpd_statics.h
+ * @brief logic to preload and serve static files
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_STATICS_H
+#define TALER_MERCHANT_HTTPD_STATICS_H
+
+#include <microhttpd.h>
+#include "taler-merchant-httpd.h"
+
+/**
+ * Check for the existence of a static file and return the result if it
+ * matches. Otherwise returns a "not found" page.
+ *
+ * @param connection the connection we act upon
+ * @param template basename of the template to load
+ * @param taler_uri value for "Taler:" header to set, or NULL
+ * @param kvc key value pairs to fill in
+ * @return #MHD_YES on success (reply queued), #MHD_NO on error (close 
connection)
+ */
+MHD_RESULT
+TMH_return_static (const struct TMH_RequestHandler *rh,
+                   struct MHD_Connection *connection,
+                   struct TMH_HandlerContext *hc);
+
+/**
+ * Preload static files.
+ */
+int
+TMH_statics_init (void);
+
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_templating.c 
b/src/backend/taler-merchant-httpd_templating.c
index 3fcdceb..1b480a6 100644
--- a/src/backend/taler-merchant-httpd_templating.c
+++ b/src/backend/taler-merchant-httpd_templating.c
@@ -470,7 +470,7 @@ TMH_templating_init ()
  * Nicely shut down.
  */
 void __attribute__ ((destructor))
-get_orders_fini ()
+templating_fini ()
 {
   for (unsigned int i = 0; i<loaded_length; i++)
   {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]