From bfb4f4b5ae3fc8cfdea76b40cdcb0e592e7442f1 Mon Sep 17 00:00:00 2001 From: Dave Ross Date: Sat, 23 May 2020 22:12:33 -0400 Subject: [PATCH] Etags (#23) * Calculate font file md5 on load * etag header * Support for a single If-None_Match header * Support multiple If-None-Match headers --- fonts/main.go | 22 +++++++++++++++++++++- main.go | 12 ++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/fonts/main.go b/fonts/main.go index d1f7022..384048c 100644 --- a/fonts/main.go +++ b/fonts/main.go @@ -2,10 +2,12 @@ package fonts import ( "crypto/sha256" + "crypto/md5" "encoding/hex" "errors" "log" "os" + "io" "path" "regexp" "strings" @@ -21,6 +23,7 @@ type FontFileData struct { FileName string Extension string CSSFormat string + MD5 string } // FontVariant describes a variant (ex: "Black", "Italic") of a font @@ -101,12 +104,13 @@ func LoadFont(filePath string, config adrianConfig.Config) { Variants: make(map[string]FontVariant), } } - + fontFileData := FontFileData{ Name: fontName, CSSFormat: fontCSSFormat(fontFormat), Extension: fontFormat, Path: filePath, + MD5: calcFileMD5(filePath), } fontVariant, ok := fontData.Variants[fontName] @@ -162,6 +166,22 @@ func calcUniqueID(fontVariant FontVariant, config adrianConfig.Config) string { return fontVariant.Name } +// calcFileMD5 calculates the MD5 hash of a file +func calcFileMD5(filepath string) string { + file, err := os.Open(filepath) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + hash := md5.New() + if _, err := io.Copy(hash, file); err != nil { + log.Fatal(err) + } + + return hex.EncodeToString(hash.Sum(nil)) +} + // fontCSSFormat determines the appropriate CSS font format given a FontData struct with Type set func fontCSSFormat(fileType string) string { switch fileType { diff --git a/main.go b/main.go index 3414445..b0fdca9 100644 --- a/main.go +++ b/main.go @@ -123,6 +123,17 @@ func outputFont(c echo.Context, mimeType string) error { log.Fatal("Invalid font format" + adrianFonts.GetCanonicalExtension(filename)) } + for i := range c.Request().Header["If-None-Match"] { + individualHashes := strings.Split(c.Request().Header["If-None-Match"][i], (", ")) + for j := range individualHashes { + if individualHashes[j] == fontFileData.MD5 { + status := make(map[string]string) + status["message"] = "Not Modified" + return c.JSON(http.StatusNotModified, status) + } + } + } + fontBinary, err := ioutil.ReadFile(fontFileData.Path) // just pass the file name if err != nil { log.Fatal("Can't read font file " + fontFileData.FileName) @@ -130,6 +141,7 @@ func outputFont(c echo.Context, mimeType string) error { c.Response().Header().Set("Content-Transfer-Encoding", "binary") c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + c.Response().Header().Set("ETag", fontFileData.MD5) return c.Blob(http.StatusOK, mimeType, fontBinary) }