]> git.earlybird.gay Git - today/commitdiff
explicit content-type for static files, better app init
authorearly <me@earlybird.gay>
Wed, 4 Sep 2024 04:13:36 +0000 (22:13 -0600)
committerearly <me@earlybird.gay>
Wed, 4 Sep 2024 04:13:36 +0000 (22:13 -0600)
app/app.go

index 07a640b1fabdf9e3728a04169a21605237480d4c..7016c9ef8eebc5cff42f7b09f460ab3cb2518a90 100644 (file)
@@ -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)
 }