-
Notifications
You must be signed in to change notification settings - Fork 360
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI Etag support for embedded content (#7132)
* UI etag embedded content Scan embedded content on startup and serve etag header to FileServer. FileServer will use the Etag and provide no content change if needed. This will allow cache all static resources, which allow better performance after the first load, in the web ui. * apply code review changes
- Loading branch information
Showing
2 changed files
with
81 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package api | ||
|
||
import ( | ||
"crypto/md5" //nolint:gosec | ||
"encoding/hex" | ||
"io" | ||
"io/fs" | ||
"net/http" | ||
"path" | ||
"strings" | ||
) | ||
|
||
// EtagMiddleware returns a new Etag middleware handler. | ||
// It designs to work on embedded FS, where the content doesn't change. | ||
// It calculates the Etag for each file on startup and serves it on each request. | ||
func EtagMiddleware(root fs.FS, next http.Handler) http.Handler { | ||
etags, err := scanFSEtags(root) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return &etagHandler{ | ||
root: root, | ||
next: next, | ||
etags: etags, | ||
} | ||
} | ||
|
||
type etagHandler struct { | ||
root fs.FS | ||
next http.Handler | ||
etags map[string]string | ||
} | ||
|
||
func (e *etagHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
upath := r.URL.Path | ||
if !strings.HasPrefix(upath, "/") { | ||
upath = "/" + upath | ||
} | ||
upath = path.Clean(upath) | ||
if strings.HasSuffix(upath, "/") { | ||
upath += "index.html" | ||
} | ||
etag, ok := e.etags[upath] | ||
if ok { | ||
w.Header().Set("Etag", "\""+etag+"\"") | ||
} | ||
e.next.ServeHTTP(w, r) | ||
} | ||
|
||
func scanFSEtags(fSys fs.FS) (map[string]string, error) { | ||
etags := make(map[string]string) | ||
err := fs.WalkDir(fSys, ".", func(fpath string, d fs.DirEntry, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if d.IsDir() { | ||
return nil | ||
} | ||
|
||
f, err := fSys.Open(fpath) | ||
if err != nil { | ||
return err | ||
} | ||
defer func() { _ = f.Close() }() | ||
|
||
h := md5.New() //nolint:gosec | ||
if _, err := io.Copy(h, f); err != nil { | ||
return err | ||
} | ||
hashValue := hex.EncodeToString(h.Sum(nil)) | ||
etags["/"+fpath] = hashValue | ||
return nil | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return etags, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters