// Copyright (C) 2022 Marius Schellenberger package srv import ( "errors" "io" stdfs "io/fs" "net/http" "path" "strings" "cachefs/pkg/fs" "github.com/go-logr/logr" ) type FileServer struct { log logr.Logger fs *fs.FS h http.Handler } func NewFileServer(fs *fs.FS, log logr.Logger) http.Handler { return &FileServer{log, fs, http.FileServer(fs)} } func skipLog(path string) bool { return strings.HasSuffix(path, "favicon.ico") } func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { s := &statusInterceptor{w: w} w = s defer func() { if skipLog(r.RequestURI) { return } fs.log.Info("access", "uri", r.RequestURI, "method", r.Method, "status", s.Status(), ) }() upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath r.URL.Path = upath } p := path.Clean(upath) d, err := fs.fs.Stat(p) if err != nil { msg, code := toHTTPError(err) http.Error(w, msg, code) return } if r.FormValue("v") == "t" { err = video.Execute(w, r.URL.Path) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } if !d.IsDir() { if r.FormValue("p") == "t" { fs.fs.Preload(p) idx := strings.LastIndex(p, "/") if idx > 0 { p = p[:idx] } else { p = "/" } http.Redirect(w, r, p, http.StatusFound) return } if r.FormValue("n") == "t" { fs.fs.NoCache.ServeHTTP(w, r) return } fs.h.ServeHTTP(w, r) return } i := &responseInterceptor{w: w} fs.h.ServeHTTP(i, r) paths, err := i.GetPaths() if err == io.EOF { w.WriteHeader(i.Status()) return } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(i.Status()) err = index.Execute(w, paths) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } func toHTTPError(err error) (string, int) { if errors.Is(err, stdfs.ErrNotExist) { return "404 page not found", http.StatusNotFound } if errors.Is(err, stdfs.ErrPermission) { return "403 Forbidden", http.StatusForbidden } // Default: return "500 Internal Server Error", http.StatusInternalServerError }