From e125f8b05bead3c9420f1ffdea25d9836b67ec6e Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Thu, 10 May 2018 06:53:39 +0100 Subject: [PATCH] gui: Enable proper asset caching (#4931) --- cmd/strelaypoolsrv/main.go | 19 +++++++++++++++++++ cmd/syncthing/gui_statics.go | 21 +++++++++++++++++++++ script/genassets.go | 9 +++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/cmd/strelaypoolsrv/main.go b/cmd/strelaypoolsrv/main.go index 3a0d57dc4..9784c0217 100644 --- a/cmd/strelaypoolsrv/main.go +++ b/cmd/strelaypoolsrv/main.go @@ -251,6 +251,8 @@ func handleMetrics(w http.ResponseWriter, r *http.Request) { } func handleAssets(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "no-cache, must-revalidate") + assets := auto.Assets() path := r.URL.Path[1:] if path == "" { @@ -263,11 +265,28 @@ func handleAssets(w http.ResponseWriter, r *http.Request) { return } + etag := fmt.Sprintf("%d", auto.Generated) + modified := time.Unix(auto.Generated, 0).UTC() + + w.Header().Set("Last-Modified", modified.Format(http.TimeFormat)) + w.Header().Set("Etag", etag) + mtype := mimeTypeForFile(path) if len(mtype) != 0 { w.Header().Set("Content-Type", mtype) } + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modified.Add(time.Second).After(t) { + w.WriteHeader(http.StatusNotModified) + return + } + + if match := r.Header.Get("If-None-Match"); match != "" { + if strings.Contains(match, etag) { + w.WriteHeader(http.StatusNotModified) + return + } + } if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { w.Header().Set("Content-Encoding", "gzip") } else { diff --git a/cmd/syncthing/gui_statics.go b/cmd/syncthing/gui_statics.go index b54cd9934..b4023630b 100644 --- a/cmd/syncthing/gui_statics.go +++ b/cmd/syncthing/gui_statics.go @@ -16,6 +16,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/syncthing/syncthing/lib/auto" "github.com/syncthing/syncthing/lib/config" @@ -71,6 +72,8 @@ func (s *staticsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (s *staticsServer) serveAsset(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "no-cache, must-revalidate") + file := r.URL.Path if file[0] == '/' { @@ -122,6 +125,24 @@ func (s *staticsServer) serveAsset(w http.ResponseWriter, r *http.Request) { } } + etag := fmt.Sprintf("%d", auto.Generated) + modified := time.Unix(auto.Generated, 0).UTC() + + w.Header().Set("Last-Modified", modified.Format(http.TimeFormat)) + w.Header().Set("Etag", etag) + + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modified.Add(time.Second).After(t) { + w.WriteHeader(http.StatusNotModified) + return + } + + if match := r.Header.Get("If-None-Match"); match != "" { + if strings.Contains(match, etag) { + w.WriteHeader(http.StatusNotModified) + return + } + } + mtype := s.mimeTypeForFile(file) if len(mtype) != 0 { w.Header().Set("Content-Type", mtype) diff --git a/script/genassets.go b/script/genassets.go index dcda34e8d..63d04fa34 100644 --- a/script/genassets.go +++ b/script/genassets.go @@ -19,10 +19,13 @@ import ( "path/filepath" "strings" "text/template" + "time" ) var tpl = template.Must(template.New("assets").Parse(`package auto +const Generated int64 = {{.Generated}} + func Assets() map[string][]byte { var assets = make(map[string][]byte, {{.Assets | len}}) {{range $asset := .Assets}} @@ -75,7 +78,8 @@ func walkerFor(basePath string) filepath.WalkFunc { } type templateVars struct { - Assets []asset + Assets []asset + Generated int64 } func main() { @@ -84,7 +88,8 @@ func main() { filepath.Walk(flag.Arg(0), walkerFor(flag.Arg(0))) var buf bytes.Buffer tpl.Execute(&buf, templateVars{ - Assets: assets, + Assets: assets, + Generated: time.Now().Unix(), }) bs, err := format.Source(buf.Bytes()) if err != nil {