http_static: added last-modified header 18/41418/11
authorAdrian Villin <avillin@cisco.com>
Fri, 16 Aug 2024 13:23:28 +0000 (15:23 +0200)
committerFlorin Coras <florin.coras@gmail.com>
Thu, 22 Aug 2024 06:09:43 +0000 (06:09 +0000)
Type: improvement

Change-Id: I492df92ef25f9c0cd57fc8980500b58bebaa94c6
Signed-off-by: Adrian Villin <avillin@cisco.com>
extras/hs-test/http_test.go
src/plugins/http_static/http_cache.c
src/plugins/http_static/http_cache.h
src/plugins/http_static/static_server.c

index ffdcf93..872f4c2 100644 (file)
@@ -553,6 +553,8 @@ func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) {
 
        content := "<html><body><p>Hello</p></body></html>"
        content2 := "<html><body><p>Page</p></body></html>"
+       currentDate := time.Now().In(time.FixedZone("GMT", 0)).Format(http.TimeFormat)[:17]
+
        vpp := s.GetContainerByName("vpp").VppInstance
        vpp.Container.Exec("mkdir -p " + wwwRootPath)
        err := vpp.Container.CreateFile(wwwRootPath+"/index.html", content)
@@ -568,11 +570,16 @@ func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) {
        resp, err := client.Do(req)
        s.AssertNil(err, fmt.Sprint(err))
        defer resp.Body.Close()
+
        s.Log(DumpHttpResp(resp, true))
        s.AssertEqual(200, resp.StatusCode)
        s.AssertContains(resp.Header.Get("Content-Type"), "html")
        s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age)
+       // only checking date
+       s.AssertContains(resp.Header.Get("Last-Modified"), currentDate)
+       s.AssertEqual(len(resp.Header.Get("Last-Modified")), 29)
        s.AssertEqual(int64(len([]rune(content))), resp.ContentLength)
+
        body, err := io.ReadAll(resp.Body)
        s.AssertNil(err, fmt.Sprint(err))
        s.AssertEqual(string(body), content)
@@ -589,6 +596,7 @@ func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) {
        s.AssertContains(resp.Header.Get("Content-Type"), "html")
        s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age)
        s.AssertEqual(int64(len([]rune(content))), resp.ContentLength)
+
        body, err = io.ReadAll(resp.Body)
        s.AssertNil(err, fmt.Sprint(err))
        s.AssertEqual(string(body), content)
@@ -603,6 +611,7 @@ func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) {
        s.AssertContains(resp.Header.Get("Content-Type"), "html")
        s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age)
        s.AssertEqual(int64(len([]rune(content2))), resp.ContentLength)
+
        body, err = io.ReadAll(resp.Body)
        s.AssertNil(err, fmt.Sprint(err))
        s.AssertEqual(string(body), content2)
@@ -828,7 +837,7 @@ func HttpStaticBuildInUrlPostIfStatsTest(s *NoTopoSuite) {
 }
 
 func HttpStaticMacTimeTest(s *NoTopoSuite) {
-       currentDate := time.Now().In(time.FixedZone("GMT", 0)).Format(http.TimeFormat)
+       currentDate := time.Now().In(time.FixedZone("GMT", 0)).Format(http.TimeFormat)[:17]
        vpp := s.GetContainerByName("vpp").VppInstance
        serverAddress := s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString()
        s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug"))
@@ -848,6 +857,8 @@ func HttpStaticMacTimeTest(s *NoTopoSuite) {
        s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).Ip4AddressString())
        s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).HwAddress.String())
        s.AssertContains(resp.Header.Get("Content-Type"), "json")
+       s.AssertEqual(len(resp.Header.Get("Date")), 29)
+       // only checking date
        s.AssertContains(resp.Header.Get("Date"), currentDate)
 }
 
index 7a069da..2e63e33 100644 (file)
@@ -17,6 +17,8 @@
 #include <vppinfra/bihash_template.c>
 #include <vppinfra/unix.h>
 #include <vlib/vlib.h>
+#include <sys/stat.h>
+#include <vppinfra/time_range.h>
 
 static void
 hss_cache_lock (hss_cache_t *hc)
@@ -153,7 +155,7 @@ lru_update (hss_cache_t *hc, hss_cache_entry_t *ep, f64 now)
 
 static void
 hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data,
-                       u64 *data_len)
+                       u64 *data_len, u8 **last_modified)
 {
   hss_cache_entry_t *ce;
 
@@ -162,6 +164,7 @@ hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data,
   ce->inuse++;
   *data = ce->data;
   *data_len = vec_len (ce->data);
+  *last_modified = ce->last_modified;
 
   /* Update the cache entry, mark it in-use */
   lru_update (hc, ce, vlib_time_now (vlib_get_main ()));
@@ -209,16 +212,15 @@ hss_cache_lookup (hss_cache_t *hc, u8 *path)
 
 u32
 hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
-                            u64 *data_len)
+                            u64 *data_len, u8 **last_modified)
 {
   u32 ce_index;
-
   /* Make sure nobody removes the entry while we look it up */
   hss_cache_lock (hc);
 
   ce_index = hss_cache_lookup (hc, path);
   if (ce_index != ~0)
-    hss_cache_attach_entry (hc, ce_index, data, data_len);
+    hss_cache_attach_entry (hc, ce_index, data, data_len, last_modified);
 
   hss_cache_unlock (hc);
 
@@ -260,6 +262,7 @@ hss_cache_do_evictions (hss_cache_t *hc)
       hc->cache_evictions++;
       vec_free (ce->filename);
       vec_free (ce->data);
+      vec_free (ce->last_modified);
 
       if (hc->debug_level > 1)
        clib_warning ("pool put index %d", ce - hc->cache_pool);
@@ -271,13 +274,15 @@ hss_cache_do_evictions (hss_cache_t *hc)
 }
 
 u32
-hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len)
+hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len,
+                         u8 **last_modified)
 {
   BVT (clib_bihash_kv) kv;
   hss_cache_entry_t *ce;
   clib_error_t *error;
   u8 *file_data;
   u32 ce_index;
+  struct stat dm;
 
   hss_cache_lock (hc);
 
@@ -298,11 +303,17 @@ hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len)
   pool_get_zero (hc->cache_pool, ce);
   ce->filename = vec_dup (path);
   ce->data = file_data;
+  if (stat ((char *) path, &dm) == 0)
+    {
+      ce->last_modified =
+       format (0, "%U GMT", format_clib_timebase_time, (f64) dm.st_mtime);
+    }
 
   /* Attach cache entry without additional lock */
   ce->inuse++;
   *data = file_data;
   *data_len = vec_len (file_data);
+  *last_modified = ce->last_modified;
   lru_add (hc, ce, vlib_time_now (vlib_get_main ()));
 
   hc->cache_size += vec_len (ce->data);
@@ -364,6 +375,7 @@ hss_cache_clear (hss_cache_t *hc)
       hc->cache_evictions++;
       vec_free (ce->filename);
       vec_free (ce->data);
+      vec_free (ce->last_modified);
       if (hc->debug_level > 1)
        clib_warning ("pool put index %d", ce - hc->cache_pool);
       pool_put (hc->cache_pool, ce);
index a89ed5e..21f71a9 100644 (file)
@@ -22,6 +22,9 @@ typedef struct hss_cache_entry_
 {
   /** Name of the file */
   u8 *filename;
+  /** Last modified date, format:
+   *  <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT  */
+  u8 *last_modified;
   /** Contents of the file, as a u8 * vector */
   u8 *data;
   /** Last time the cache entry was used */
@@ -58,9 +61,9 @@ typedef struct hss_cache_
 } hss_cache_t;
 
 u32 hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
-                                u64 *data_len);
+                                u64 *data_len, u8 **last_modified);
 u32 hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data,
-                             u64 *data_len);
+                             u64 *data_len, u8 **last_modified);
 void hss_cache_detach_entry (hss_cache_t *hc, u32 ce_index);
 u32 hss_cache_clear (hss_cache_t *hc);
 void hss_cache_init (hss_cache_t *hc, uword cache_size, u8 debug_level);
index 40de6cb..674ce8a 100644 (file)
@@ -411,6 +411,7 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
   u8 *path, *sanitized_path;
   u32 ce_index;
   http_content_type_t type;
+  u8 *last_modified;
 
   /* Feature not enabled */
   if (!hsm->www_root)
@@ -435,8 +436,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
 
   hs->data_offset = 0;
 
-  ce_index =
-    hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+  ce_index = hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data,
+                                         &hs->data_len, &last_modified);
   if (ce_index == ~0)
     {
       if (!file_path_is_valid (path))
@@ -455,8 +456,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
          sc = try_index_file (hsm, hs, path);
          goto done;
        }
-      ce_index =
-       hss_cache_add_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+      ce_index = hss_cache_add_and_attach (&hsm->cache, path, &hs->data,
+                                          &hs->data_len, &last_modified);
       if (ce_index == ~0)
        {
          sc = HTTP_STATUS_INTERNAL_ERROR;
@@ -478,6 +479,9 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
   http_add_header (
     &hs->resp_headers, http_header_name_token (HTTP_HEADER_CACHE_CONTROL),
     (const char *) hsm->max_age_formatted, vec_len (hsm->max_age_formatted));
+  http_add_header (&hs->resp_headers,
+                  http_header_name_token (HTTP_HEADER_LAST_MODIFIED),
+                  (const char *) last_modified, vec_len (last_modified));
 
 done:
   vec_free (sanitized_path);