diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2026-09-08 17:13:08 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2026-09-08 17:40:36 -0700 |
| commit | f0ff9117fe15d5d1f4e5ade7cf6250a2b58684bd (patch) | |
| tree | 76a530c5131b73b2db70f11de0e6fc006eacaa6c /roseh.moe.go | |
| parent | Use a different ETag for Content-Encoding gzip (diff) | |
| download | roseh.moe-f0ff9117fe15d5d1f4e5ade7cf6250a2b58684bd.tar.zst | |
Give static HTML pages an ETag
Diffstat (limited to 'roseh.moe.go')
| -rw-r--r-- | roseh.moe.go | 90 |
1 files changed, 48 insertions, 42 deletions
diff --git a/roseh.moe.go b/roseh.moe.go index 8654282..23e96ce 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -147,6 +147,14 @@ func loadConfig() (*serviceConfiguration, error) { return config, nil } +func makeETag(content io.Reader) (string, error) { + h := sha256.New() + if _, err := io.Copy(h, content); err != nil { + return "", err + } + return `"` + base64.RawURLEncoding.EncodeToString(h.Sum(nil)[:8]) + `"`, nil +} + var ( //go:embed templates/outline.html.template outlineString string @@ -165,6 +173,32 @@ func outlinedTemplate(s string) (*template.Template, error) { return t.Lookup("outline"), nil } +type staticHandler struct { + content, eTag string +} + +func (h *staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Header().Set("ETag", h.eTag) + http.ServeContent(w, r, "", time.Time{}, strings.NewReader(h.content)) +} + +func mustStaticTemplate(tmpl *template.Template) *staticHandler { + buf := new(strings.Builder) + if err := tmpl.Execute(buf, nil); err != nil { + panic(err) + } + content := buf.String() + eTag, err := makeETag(strings.NewReader(content)) + if err != nil { + panic(err) + } + return &staticHandler{ + content: buf.String(), + eTag: eTag, + } +} + var ( //go:embed templates/404.html.template notFoundString string @@ -183,52 +217,28 @@ func notFound(w http.ResponseWriter, r *http.Request) { var ( //go:embed templates/index.html.template - indexString string - indexTemplate = template.Must(outlinedTemplate(indexString)) + indexString string + indexHandler = mustStaticTemplate(template.Must(outlinedTemplate(indexString))) ) -func index(w http.ResponseWriter, _ *http.Request) { - if err := indexTemplate.Execute(w, nil); err != nil { - log.Printf("Warning: index: %s", err) - } -} - var ( //go:embed templates/games/index.html.template - gamesIndexString string - gamesIndexTemplate = template.Must(outlinedTemplate(gamesIndexString)) + gamesIndexString string + gamesIndexHandler = mustStaticTemplate(template.Must(outlinedTemplate(gamesIndexString))) ) -func gamesIndex(w http.ResponseWriter, _ *http.Request) { - if err := gamesIndexTemplate.Execute(w, nil); err != nil { - log.Printf("Waning: gamesIndex: %s", err) - } -} - var ( //go:embed templates/games/pong.html.template - pongString string - pongTemplate = template.Must(template.New("pong").Parse(pongString)) + pongString string + pongHandler = mustStaticTemplate(template.Must(template.New("pong").Parse(pongString))) ) -func pong(w http.ResponseWriter, _ *http.Request) { - if err := pongTemplate.Execute(w, nil); err != nil { - log.Printf("Warning: pong: %s", err) - } -} - var ( //go:embed templates/games/rms.html.template - stallmanShooterString string - stallmanShooterTemplate = template.Must(outlinedTemplate(stallmanShooterString)) + stallmanShooterString string + stallmanShooterHandler = mustStaticTemplate(template.Must(outlinedTemplate(stallmanShooterString))) ) -func stallmanShooter(w http.ResponseWriter, _ *http.Request) { - if err := stallmanShooterTemplate.Execute(w, nil); err != nil { - log.Printf("Warning: stallmanShooter: %s", err) - } -} - //go:embed static var staticFiles embed.FS @@ -245,11 +255,9 @@ func buildStaticFileHashes() map[string]string { return err } defer f.Close() - h := sha256.New() - if _, err := io.Copy(h, f); err != nil { + if hashMap[path], err = makeETag(f); err != nil { return err } - hashMap[path] = `"` + base64.RawURLEncoding.EncodeToString(h.Sum(nil)[:8]) + `"` return nil }); err != nil { panic(fmt.Sprintf("Build static file hash map: %s", err)) @@ -288,9 +296,7 @@ func qrHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("failed to encode QR code: %s", err), http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "image/png") - w.Header().Set("Content-Length", strconv.Itoa(len(qr))) - w.Write(qr) + http.ServeContent(w, r, "qr.png", time.Time{}, bytes.NewReader(qr)) } var ( @@ -1059,10 +1065,10 @@ func main() { }() mux := http.NewServeMux() - mux.HandleFunc("GET /{$}", index) - mux.HandleFunc("GET /games/{$}", gamesIndex) - mux.HandleFunc("GET /games/pong", pong) - mux.HandleFunc("GET /games/rms", stallmanShooter) + mux.Handle("GET /{$}", indexHandler) + mux.Handle("GET /games/{$}", gamesIndexHandler) + mux.Handle("GET /games/pong", pongHandler) + mux.Handle("GET /games/rms", stallmanShooterHandler) mux.HandleFunc("GET /qr.png", qrHandler) mux.HandleFunc("GET /wormhole", wormhole) mux.HandleFunc("POST /wormhole", wormholeUpload) |
