// Package web serves the browser front end, the manual and sample programs. package web import ( "embed" "encoding/json" "io/fs" "net/http" "sort" "strings" "wh/docs" "wh/examples" ) //go:embed static var static embed.FS // Handler serves the site. Mount it as the catch-all route; more specific API // routes registered on the same mux take precedence. func Handler() http.Handler { sub, _ := fs.Sub(static, "static") files := http.FileServerFS(sub) mux := http.NewServeMux() mux.Handle("GET /", files) mux.HandleFunc("GET /manual.txt", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Write(docs.Manual) }) mux.HandleFunc("GET /examples/index.json", func(w http.ResponseWriter, r *http.Request) { entries, _ := fs.ReadDir(examples.FS, ".") names := []string{} for _, e := range entries { if n, ok := strings.CutSuffix(e.Name(), ".s"); ok { names = append(names, n) } } sort.Strings(names) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(names) }) mux.Handle("GET /examples/", http.StripPrefix("/examples/", http.FileServerFS(examples.FS))) return secure(mux) } // secure adds headers appropriate to a page that loads only its own assets. func secure(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { hd := w.Header() hd.Set("Content-Security-Policy", "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'") hd.Set("X-Content-Type-Options", "nosniff") hd.Set("Referrer-Policy", "no-referrer") h.ServeHTTP(w, r) }) }