From ca2edeb965f8cfbfdb7ec495b330fa2923f38691 Mon Sep 17 00:00:00 2001 From: early Date: Tue, 3 Sep 2024 22:13:36 -0600 Subject: [PATCH] explicit content-type for static files, better app init --- app/app.go | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index 07a640b..7016c9e 100644 --- a/app/app.go +++ b/app/app.go @@ -36,6 +36,9 @@ type App struct { running atomic.Bool wg sync.WaitGroup shutdown, stop chan struct{} + + ready atomic.Bool + initLock sync.Mutex } func New() *App { @@ -78,6 +81,89 @@ func (app *App) Subprocess(process func(done <-chan struct{}) error) { }() } +// List of static content types for app.Static. +// http *should* handle this, but sometimes it doesn't. Use this, then let the +// stdlib try to sort it out otherwise. +// +// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types +var staticContentTypes = map[string]string{ + ".aac": "audio/aac", + ".abw": "application/x-abiword", + ".apng": "image/apng", + ".arc": "application/x-freearc", + ".avif": "image/avif", + ".avi": "video/x-msvideo", + ".azw": "application/vnd.amazon.ebook", + ".bin": "application/octet-stream", + ".bmp": "image/bmp", + ".bz": "application/x-bzip", + ".bz2": "application/x-bzip2", + ".cda": "application/x-cdf", + ".csh": "application/x-csh", + ".css": "text/css", + ".csv": "text/csv", + ".doc": "application/msword", + ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".eot": "application/vnd.ms-fontobject", + ".epub": "application/epub+zip", + ".gz": "application/gzip", + ".gif": "image/gif", + ".htm": "text/html", + ".html": "text/html", + ".ico": "image/vnd.microsoft.icon", + ".ics": "text/calendar", + ".jar": "application/java-archive", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".js": "text/javascript", + ".json": "application/json", + ".jsonld": "application/ld+json", + ".mid": "audio/midi", + ".midi": "audio/midi", + ".mjs": "text/javascript", + ".mp3": "audio/mpeg", + ".mp4": "video/mp4", + ".mpeg": "video/mpeg", + ".mpkg": "application/vnd.apple.installer+xml", + ".odp": "application/vnd.oasis.opendocument.presentation", + ".ods": "application/vnd.oasis.opendocument.spreadsheet", + ".odt": "application/vnd.oasis.opendocument.text", + ".oga": "audio/ogg", + ".ogv": "video/ogg", + ".ogx": "application/ogg", + ".opus": "audio/ogg", + ".otf": "font/otf", + ".png": "image/png", + ".pdf": "application/pdf", + ".php": "application/x-httpd-php", + ".ppt": "application/vnd.ms-powerpoint", + ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ".rar": "application/vnd.rar", + ".rtf": "application/rtf", + ".sh": "application/x-sh", + ".svg": "image/svg+xml", + ".tar": "application/x-tar", + ".tif": "image/tiff", + ".tiff": "image/tiff", + ".ts": "video/mp2t", + ".ttf": "font/ttf", + ".txt": "text/plain", + ".vsd": "application/vnd.visio", + ".wav": "audio/wav", + ".weba": "audio/webm", + ".webm": "video/webm", + ".webp": "image/webp", + ".woff": "font/woff", + ".woff2": "font/woff2", + ".xhtml": "application/xhtml+xml", + ".xls": "application/vnd.ms-excel", + ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ".xml": "application/xml", + ".xul": "application/vnd.mozilla.xul+xml", + ".zip": "application/zip", + ".7z": "application/x-7z-compressed", +} + // Static serves the files in rootDir at rootPath. // // - .html files have their extensions stripped, and are served as their @@ -138,6 +224,9 @@ func (app *App) Static(rootPath, rootDir string) error { fmt.Fprintln(w, "failed to open file") return } + if contentType, ok := staticContentTypes[fileExt]; ok { + w.Header().Set("Content-Type", contentType) + } w.WriteHeader(http.StatusOK) io.Copy(w, f) }) @@ -193,9 +282,26 @@ registerStaticHandlers: } } -func (app *App) ListenAndServe() error { +func (app *App) initOnce() { + // Only let one through + // can't use CAS because we need non-init processes to wait + if app.ready.Load() { + return + } + app.initLock.Lock() + if app.ready.Load() { + return + } app.setDefaults() app.registerStatic() + + app.ready.Store(true) + app.running.Store(true) + app.initLock.Unlock() +} + +func (app *App) ListenAndServe() error { + app.initOnce() app.Logger.Info("application starting", "host", app.Addr) var listener net.Listener @@ -216,7 +322,6 @@ func (app *App) ListenAndServe() error { return err } - app.running.Store(true) err = app.Server.Serve(listener) app.Logger.Info("application stopped", "reason", err) @@ -224,6 +329,7 @@ func (app *App) ListenAndServe() error { } func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { + app.initOnce() app.Logger.Debug("serving request", "host", r.Host, "path", r.URL.Path) app.ServeMux.ServeHTTP(w, r) } -- 2.39.5