Merge pull request #5018 from hairyhenderson/allow-fs.FS-for-virtual-filesystems

Drop requirement for filesystems to implement fs.StatFS
This commit is contained in:
Dave Henderson 2022-09-05 20:10:48 -04:00 committed by GitHub
commit 8f6a88e2b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 21 additions and 20 deletions

View file

@ -210,7 +210,7 @@ func (fsrv *FileServer) isSymlinkTargetDir(f fs.FileInfo, root, urlPath string)
return false return false
} }
target := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, f.Name())) target := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, f.Name()))
targetInfo, err := fsrv.fileSystem.Stat(target) targetInfo, err := fs.Stat(fsrv.fileSystem, target)
if err != nil { if err != nil {
return false return false
} }

View file

@ -65,7 +65,7 @@ func (fsrv *FileServer) directoryListing(entries []fs.DirEntry, canGoUp bool, ro
fileIsSymlink := isSymlink(info) fileIsSymlink := isSymlink(info)
if fileIsSymlink { if fileIsSymlink {
path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name())) path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name()))
fileInfo, err := fsrv.fileSystem.Stat(path) fileInfo, err := fs.Stat(fsrv.fileSystem, path)
if err == nil { if err == nil {
size = fileInfo.Size() size = fileInfo.Size()
} }

View file

@ -77,11 +77,11 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
statFS, ok := unm.(fs.StatFS) fsys, ok := unm.(fs.FS)
if !ok { if !ok {
return nil, h.Errf("module %s (%T) is not a supported file system implementation (requires fs.StatFS)", modID, unm) return nil, h.Errf("module %s (%T) is not a supported file system implementation (requires fs.FS)", modID, unm)
} }
fsrv.FileSystemRaw = caddyconfig.JSONModuleObject(statFS, "backend", name, nil) fsrv.FileSystemRaw = caddyconfig.JSONModuleObject(fsys, "backend", name, nil)
case "hide": case "hide":
fsrv.Hide = h.RemainingArgs() fsrv.Hide = h.RemainingArgs()

View file

@ -64,7 +64,7 @@ type MatchFile struct {
// The file system implementation to use. By default, the // The file system implementation to use. By default, the
// local disk file system will be used. // local disk file system will be used.
FileSystemRaw json.RawMessage `json:"file_system,omitempty" caddy:"namespace=caddy.fs inline_key=backend"` FileSystemRaw json.RawMessage `json:"file_system,omitempty" caddy:"namespace=caddy.fs inline_key=backend"`
fileSystem fs.StatFS fileSystem fs.FS
// The root directory, used for creating absolute // The root directory, used for creating absolute
// file paths, and required when working with // file paths, and required when working with
@ -264,7 +264,7 @@ func (m *MatchFile) Provision(ctx caddy.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("loading file system module: %v", err) return fmt.Errorf("loading file system module: %v", err)
} }
m.fileSystem = mod.(fs.StatFS) m.fileSystem = mod.(fs.FS)
} }
if m.fileSystem == nil { if m.fileSystem == nil {
m.fileSystem = osFS{} m.fileSystem = osFS{}
@ -418,7 +418,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles { for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern) candidates := makeCandidates(pattern)
for _, c := range candidates { for _, c := range candidates {
info, err := m.fileSystem.Stat(c.fullpath) info, err := fs.Stat(m.fileSystem, c.fullpath)
if err == nil && info.Size() > largestSize { if err == nil && info.Size() > largestSize {
largestSize = info.Size() largestSize = info.Size()
largest = c largest = c
@ -439,7 +439,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles { for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern) candidates := makeCandidates(pattern)
for _, c := range candidates { for _, c := range candidates {
info, err := m.fileSystem.Stat(c.fullpath) info, err := fs.Stat(m.fileSystem, c.fullpath)
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) { if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
smallestSize = info.Size() smallestSize = info.Size()
smallest = c smallest = c
@ -459,7 +459,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles { for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern) candidates := makeCandidates(pattern)
for _, c := range candidates { for _, c := range candidates {
info, err := m.fileSystem.Stat(c.fullpath) info, err := fs.Stat(m.fileSystem, c.fullpath)
if err == nil && if err == nil &&
(recentInfo == nil || info.ModTime().After(recentInfo.ModTime())) { (recentInfo == nil || info.ModTime().After(recentInfo.ModTime())) {
recent = c recent = c
@ -498,7 +498,7 @@ func parseErrorCode(input string) error {
// NOT end in a forward slash, the file must NOT // NOT end in a forward slash, the file must NOT
// be a directory. // be a directory.
func (m MatchFile) strictFileExists(file string) (os.FileInfo, bool) { func (m MatchFile) strictFileExists(file string) (os.FileInfo, bool) {
info, err := m.fileSystem.Stat(file) info, err := fs.Stat(m.fileSystem, file)
if err != nil { if err != nil {
// in reality, this can be any error // in reality, this can be any error
// such as permission or even obscure // such as permission or even obscure

View file

@ -83,14 +83,14 @@ type FileServer struct {
// disk file system. // disk file system.
// //
// File system modules used here must adhere to the following requirements: // File system modules used here must adhere to the following requirements:
// - Implement fs.StatFS interface. // - Implement fs.FS interface.
// - Support seeking on opened files; i.e.returned fs.File values must // - Support seeking on opened files; i.e.returned fs.File values must
// implement the io.Seeker interface. This is required for determining // implement the io.Seeker interface. This is required for determining
// Content-Length and satisfying Range requests. // Content-Length and satisfying Range requests.
// - fs.File values that represent directories must implement the // - fs.File values that represent directories must implement the
// fs.ReadDirFile interface so that directory listings can be procured. // fs.ReadDirFile interface so that directory listings can be procured.
FileSystemRaw json.RawMessage `json:"file_system,omitempty" caddy:"namespace=caddy.fs inline_key=backend"` FileSystemRaw json.RawMessage `json:"file_system,omitempty" caddy:"namespace=caddy.fs inline_key=backend"`
fileSystem fs.StatFS fileSystem fs.FS
// The path to the root of the site. Default is `{http.vars.root}` if set, // The path to the root of the site. Default is `{http.vars.root}` if set,
// or current working directory otherwise. This should be a trusted value. // or current working directory otherwise. This should be a trusted value.
@ -175,7 +175,7 @@ func (fsrv *FileServer) Provision(ctx caddy.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("loading file system module: %v", err) return fmt.Errorf("loading file system module: %v", err)
} }
fsrv.fileSystem = mod.(fs.StatFS) fsrv.fileSystem = mod.(fs.FS)
} }
if fsrv.fileSystem == nil { if fsrv.fileSystem == nil {
fsrv.fileSystem = osFS{} fsrv.fileSystem = osFS{}
@ -244,7 +244,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
zap.String("result", filename)) zap.String("result", filename))
// get information about the file // get information about the file
info, err := fsrv.fileSystem.Stat(filename) info, err := fs.Stat(fsrv.fileSystem, filename)
if err != nil { if err != nil {
err = fsrv.mapDirOpenError(err, filename) err = fsrv.mapDirOpenError(err, filename)
if errors.Is(err, fs.ErrNotExist) { if errors.Is(err, fs.ErrNotExist) {
@ -270,7 +270,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
continue continue
} }
indexInfo, err := fsrv.fileSystem.Stat(indexPath) indexInfo, err := fs.Stat(fsrv.fileSystem, indexPath)
if err != nil { if err != nil {
continue continue
} }
@ -350,7 +350,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
continue continue
} }
compressedFilename := filename + precompress.Suffix() compressedFilename := filename + precompress.Suffix()
compressedInfo, err := fsrv.fileSystem.Stat(compressedFilename) compressedInfo, err := fs.Stat(fsrv.fileSystem, compressedFilename)
if err != nil || compressedInfo.IsDir() { if err != nil || compressedInfo.IsDir() {
fsrv.logger.Debug("precompressed file not accessible", zap.String("filename", compressedFilename), zap.Error(err)) fsrv.logger.Debug("precompressed file not accessible", zap.String("filename", compressedFilename), zap.Error(err))
continue continue
@ -490,7 +490,7 @@ func (fsrv *FileServer) mapDirOpenError(originalErr error, name string) error {
if parts[i] == "" { if parts[i] == "" {
continue continue
} }
fi, err := fsrv.fileSystem.Stat(strings.Join(parts[:i+1], separator)) fi, err := fs.Stat(fsrv.fileSystem, strings.Join(parts[:i+1], separator))
if err != nil { if err != nil {
return originalErr return originalErr
} }
@ -613,13 +613,13 @@ func (wr statusOverrideResponseWriter) WriteHeader(int) {
wr.ResponseWriter.WriteHeader(wr.code) wr.ResponseWriter.WriteHeader(wr.code)
} }
// osFS is a simple fs.StatFS implementation that uses the local // osFS is a simple fs.FS implementation that uses the local
// file system. (We do not use os.DirFS because we do our own // file system. (We do not use os.DirFS because we do our own
// rooting or path prefixing without being constrained to a single // rooting or path prefixing without being constrained to a single
// root folder. The standard os.DirFS implementation is problematic // root folder. The standard os.DirFS implementation is problematic
// since roots can be dynamic in our application.) // since roots can be dynamic in our application.)
// //
// osFS also implements fs.GlobFS, fs.ReadDirFS, and fs.ReadFileFS. // osFS also implements fs.StatFS, fs.GlobFS, fs.ReadDirFS, and fs.ReadFileFS.
type osFS struct{} type osFS struct{}
func (osFS) Open(name string) (fs.File, error) { return os.Open(name) } func (osFS) Open(name string) (fs.File, error) { return os.Open(name) }
@ -640,6 +640,7 @@ var (
_ caddy.Provisioner = (*FileServer)(nil) _ caddy.Provisioner = (*FileServer)(nil)
_ caddyhttp.MiddlewareHandler = (*FileServer)(nil) _ caddyhttp.MiddlewareHandler = (*FileServer)(nil)
_ fs.StatFS = (*osFS)(nil)
_ fs.GlobFS = (*osFS)(nil) _ fs.GlobFS = (*osFS)(nil)
_ fs.ReadDirFS = (*osFS)(nil) _ fs.ReadDirFS = (*osFS)(nil)
_ fs.ReadFileFS = (*osFS)(nil) _ fs.ReadFileFS = (*osFS)(nil)