123 lines
2.4 KiB
Go
123 lines
2.4 KiB
Go
// 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
|
|
if !skipLog(r.RequestURI) {
|
|
defer func() {
|
|
if s.Status() == http.StatusPartialContent {
|
|
return
|
|
}
|
|
fs.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)
|
|
d, _, err := fs.fs.StatWithOffline(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)
|
|
rdir := "/"
|
|
if i := strings.LastIndex(p, "/"); i > 0 {
|
|
rdir = p[:i]
|
|
}
|
|
http.Redirect(w, r, rdir+"#"+anchor(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}
|
|
// TODO implement directory listing cache
|
|
offline := true
|
|
if offline {
|
|
r.Header.Del("If-Modified-Since")
|
|
r.Header.Del("Cache-Control")
|
|
}
|
|
fs.h.ServeHTTP(i, r)
|
|
if offline {
|
|
w.Header().Del("Last-Modified")
|
|
}
|
|
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
|
|
}
|
|
return "500 Internal Server Error", http.StatusInternalServerError
|
|
}
|