running atomic.Bool
wg sync.WaitGroup
shutdown, stop chan struct{}
+
+ ready atomic.Bool
+ initLock sync.Mutex
}
func New() *App {
}()
}
+// 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
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)
})
}
}
-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
return err
}
- app.running.Store(true)
err = app.Server.Serve(listener)
app.Logger.Info("application stopped", "reason", err)
}
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)
}