package handler import ( "errors" "fmt" "io/fs" "mime" "net/http" "os" "path/filepath" "strings" "git.pyer.club/kingecg/gologger" ) type FileHandler struct { WPath string Root string Default string http.FileSystem } func (f FileHandler) String() string { return fmt.Sprintf("FileHandler:{WPath:%s,Root:%s,Default:%s}", f.WPath, f.Root, f.Default) } func (f FileHandler) Open(name string) (http.File, error) { l := gologger.GetLogger("filehandler") l.Debug("access:", name) if strings.HasPrefix(name, "../") { return nil, errors.New("not permitted") } relatedPath := strings.TrimPrefix(name, f.WPath) rPath := filepath.Join(f.Root, strings.TrimPrefix(relatedPath, "/")) l.Debug("access:", rPath) // if rPath == f.Root { // if f.Default == "" { // return nil, errors.New("not permit list dir") // } // rPath = filepath.Join(rPath, f.Default) // } fInfo, _, err := FileExists(rPath) if err != nil { l.Error("access file error:", rPath, err) return nil, err } if fInfo.IsDir() && rPath != strings.TrimSuffix(f.Root, "/") { return nil, errors.New("not permit list dir") } if fInfo.Mode() == fs.ModeSymlink { return nil, errors.New("not permit follow symbol link") } fp, err := os.Open(rPath) if err != nil { l.Error("open file fail", err) return nil, err } return fp, nil } func FileExists(name string) (os.FileInfo, bool, error) { info, err := os.Stat(name) if err != nil { return nil, os.IsExist(err), err } return info, true, nil } func NewFileHandler(f FileHandler) http.Handler { return http.FileServer(f) } func init() { mime.AddExtensionType(".js", "application/javascript") mime.AddExtensionType(".css", "text/css") mime.AddExtensionType(".html", "text/html") mime.AddExtensionType(".htm", "text/html") mime.AddExtensionType(".svg", "image/svg+xml") mime.AddExtensionType(".png", "image/png") mime.AddExtensionType(".mjs", "application/javascript") }