complete overhaul
Some checks failed
build-and-push / docker (push) Failing after 10m36s

This commit is contained in:
2025-09-18 00:23:21 +02:00
parent 6afcfaa5b3
commit a2743dd7fb
11 changed files with 1201 additions and 271 deletions

View File

@@ -1,12 +1,14 @@
package main
import (
"compress/gzip"
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"sync/atomic"
"time"
@@ -89,7 +91,8 @@ func main() {
port = "8080"
}
http.Handle("/static/", withLogging(http.StripPrefix("/static/", http.FileServer(http.Dir("./static")))))
// Static files under /static with long-lived caching and gzip for text assets
http.Handle("/static/", withLogging(withCacheGzip(http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))), true)))
http.Handle("/healthz", withLogging(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
@@ -105,10 +108,35 @@ func main() {
proxyToNominatimGET(w, r, nominatimBase+"/reverse", nominatimUA)
})))
http.Handle("/", withLogging(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Serve index
if r.URL.Path == "/" {
http.ServeFile(w, r, "./static/index.html")
// Short cache for HTML entrypoint
setShortCache(w)
maybeGzipAndServeFile(w, r, "./static/index.html")
return
}
// Serve other assets from ./static with long cache for versioned files
// Decide cache and compression based on extension
ext := strings.ToLower(filepath.Ext(r.URL.Path))
if isTextLikeExt(ext) {
setLongCache(w)
w.Header().Add("Vary", "Accept-Encoding")
// Use gzip when supported
if acceptsGzip(r) {
gz := gzip.NewWriter(w)
defer gz.Close()
w.Header().Set("Content-Encoding", "gzip")
// Avoid Content-Length when gzipping
w.Header().Del("Content-Length")
gzw := &gzipResponseWriter{ResponseWriter: w, gw: gz}
http.ServeFile(gzw, r, "./static/"+r.URL.Path[1:])
return
}
http.ServeFile(w, r, "./static/"+r.URL.Path[1:])
return
}
// Non-text assets: just set long cache
setLongCache(w)
http.ServeFile(w, r, "./static/"+r.URL.Path[1:])
})))
logf(levelInfo, "Listening on :%s", port)
@@ -246,4 +274,78 @@ func clientIP(r *http.Request) string {
return xff
}
return r.RemoteAddr
}
// ---- Performance helpers: caching and gzip compression ----
func setShortCache(w http.ResponseWriter) {
// Short cache e.g., for HTML entrypoint
w.Header().Set("Cache-Control", "public, max-age=300")
}
func setLongCache(w http.ResponseWriter) {
// Long-lived cache for versioned static assets
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
func isTextLikeExt(ext string) bool {
switch ext {
case ".html", ".css", ".js", ".mjs", ".json", ".svg", ".xml", ".txt", ".map", ".webmanifest":
return true
default:
return false
}
}
func acceptsGzip(r *http.Request) bool {
ae := r.Header.Get("Accept-Encoding")
return strings.Contains(ae, "gzip")
}
type gzipResponseWriter struct {
http.ResponseWriter
gw *gzip.Writer
}
func (g *gzipResponseWriter) Write(b []byte) (int, error) {
return g.gw.Write(b)
}
// withCacheGzip wraps a handler to set cache headers and gzip text-like responses
func withCacheGzip(next http.Handler, longCache bool) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if longCache {
setLongCache(w)
} else {
setShortCache(w)
}
ext := strings.ToLower(filepath.Ext(r.URL.Path))
if isTextLikeExt(ext) && acceptsGzip(r) {
w.Header().Add("Vary", "Accept-Encoding")
w.Header().Set("Content-Encoding", "gzip")
w.Header().Del("Content-Length")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := &gzipResponseWriter{ResponseWriter: w, gw: gz}
next.ServeHTTP(gzw, r)
return
}
next.ServeHTTP(w, r)
})
}
func maybeGzipAndServeFile(w http.ResponseWriter, r *http.Request, path string) {
// For text-like files, gzip when supported
ext := strings.ToLower(filepath.Ext(path))
if isTextLikeExt(ext) && acceptsGzip(r) {
w.Header().Add("Vary", "Accept-Encoding")
w.Header().Set("Content-Encoding", "gzip")
w.Header().Del("Content-Length")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := &gzipResponseWriter{ResponseWriter: w, gw: gz}
http.ServeFile(gzw, r, path)
return
}
http.ServeFile(w, r, path)
}