package main // Static file serving: the Go port of lib/http.js serveStatic. Assets are // embedded from embed_public/ (populated by build.sh from ../public); // when NG_PUBLIC_DIR is set, files are read from that directory instead. import ( "embed" "io/fs" "net/http" "os" "path" "path/filepath" "strconv" "strings" ) //go:embed embed_public var embeddedPublic embed.FS const embedRoot = "embed_public" var staticMIME = map[string]string{ ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".svg": "image/svg+xml", ".json": "application/json; charset=utf-8", } // ServeStatic serves pathname from the public root; returns false when no // regular file matches (mirroring serveStatic's false), and an error for // unexpected I/O failures (mirroring its throw). func ServeStatic(w http.ResponseWriter, pathname string) (bool, error) { if dir := os.Getenv("NG_PUBLIC_DIR"); dir != "" { return serveStaticDisk(w, dir, pathname) } relative := "index.html" if pathname != "/" { relative = strings.TrimLeft(pathname, "/") } // Reject any path that would escape the root (JS path.resolve check). if escapesRoot(relative) { return false, nil } name := embedRoot + "/" + relative info, err := fs.Stat(embeddedPublic, name) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } if !info.Mode().IsRegular() { return false, nil } body, err := fs.ReadFile(embeddedPublic, name) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } writeStatic(w, relative, body) return true, nil } // escapesRoot reports whether a slash-separated relative path climbs above // its root via ".." segments. func escapesRoot(relative string) bool { depth := 0 for _, seg := range strings.Split(relative, "/") { switch seg { case "..": if depth == 0 { return true } depth-- case "", ".": default: depth++ } } return false } func serveStaticDisk(w http.ResponseWriter, publicDir, pathname string) (bool, error) { relative := "index.html" if pathname != "/" { relative = strings.TrimLeft(pathname, "/") } root := filepath.Clean(publicDir) target := filepath.Join(root, filepath.FromSlash(relative)) if !strings.HasPrefix(target, root+string(filepath.Separator)) && target != filepath.Join(root, "index.html") { return false, nil } info, err := os.Stat(target) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } if !info.Mode().IsRegular() { return false, nil } body, err := os.ReadFile(target) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } writeStatic(w, relative, body) return true, nil } func writeStatic(w http.ResponseWriter, name string, body []byte) { contentType := staticMIME[path.Ext(name)] if contentType == "" { contentType = "application/octet-stream" } h := w.Header() h.Set("Content-Type", contentType) h.Set("Content-Length", strconv.Itoa(len(body))) h.Set("Cache-Control", "no-store") h.Set("X-Content-Type-Options", "nosniff") w.WriteHeader(200) _, _ = w.Write(body) }