// Copyright (C) 2022 Marius Schellenberger package srv import ( "io" "net/http" "path" "strings" "cachefs/pkg/fs" "github.com/go-logr/logr" ) type CacheFS struct { fs *fs.FS } func (cfs *CacheFS) Open(name string) (http.File, error) { return cfs.fs.OpenDst(name) } type CacheServer struct { log logr.Logger fs *fs.FS h http.Handler } func NewCacheServer(fs *fs.FS, log logr.Logger) http.Handler { return &CacheServer{log, fs, http.FileServer(&CacheFS{fs})} } func (cs *CacheServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { s := &statusInterceptor{w: w} w = s if !skipLog(r.RequestURI) { defer func() { if s.Status() == http.StatusPartialContent { return } cs.log.Info("access", "client", r.RemoteAddr, "method", r.Method, "status", s.Status(), "uri", r.RequestURI, ) }() } upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath r.URL.Path = upath } p := path.Clean(upath) option := r.FormValue("o") d, err := cs.fs.StatDst(p) if err != nil { msg, code := toHTTPError(err) http.Error(w, msg, code) return } h := w.Header() if option == "v" { h.Set(csp, videoCSP) err = video.Execute(w, data{Paths: dirContents{Base: r.URL.Path}}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } if !d.IsDir() { cs.h.ServeHTTP(w, r) return } i := &responseInterceptor{w: w} r.Header.Del("If-Modified-Since") r.Header.Del("Cache-Control") cs.h.ServeHTTP(i, r) h.Del("Last-Modified") paths, err := i.GetPaths(p, cs.fs, true) if err == io.EOF { w.WriteHeader(i.Status()) return } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } h.Set(csp, indexCSP) w.WriteHeader(i.Status()) err = cache.Execute(w, data{Paths: paths}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }