From a4581868cd315d8a34f8f535ec3a26154588e21b Mon Sep 17 00:00:00 2001
From: a
Date: Thu, 21 Sep 2023 13:04:41 -0500
Subject: [PATCH 1/4] try_files
---
modules/caddyfs/filesystem.go | 2 ++
modules/caddyhttp/fileserver/matcher.go | 28 ++++++++++++++-------
modules/caddyhttp/fileserver/staticfiles.go | 4 ++-
3 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/modules/caddyfs/filesystem.go b/modules/caddyfs/filesystem.go
index bdfeae405f1c..51993be19356 100644
--- a/modules/caddyfs/filesystem.go
+++ b/modules/caddyfs/filesystem.go
@@ -9,6 +9,7 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "go.uber.org/zap"
)
func init() {
@@ -66,6 +67,7 @@ func (xs *Filesystems) Provision(ctx caddy.Context) error {
f.fileSystem = mod.(fs.FS)
}
// register that module
+ ctx.Logger().Debug("registering fs", zap.String("fs", f.Key))
ctx.Filesystems().Register(f.Key, f.fileSystem)
// remember to unregister the module when we are done
xs.defers = append(xs.defers, func() {
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index 688e7707f127..c11946fbf05f 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -35,6 +35,7 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+ "github.com/caddyserver/caddy/v2/internal/filesystems"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
@@ -64,7 +65,6 @@ type MatchFile struct {
// The file system implementation to use. By default, the
// local disk file system will be used.
FileSystem string `json:"fs,omitempty"`
- fileSystem fs.FS
// The root directory, used for creating absolute
// file paths, and required when working with
@@ -107,6 +107,8 @@ type MatchFile struct {
// component in order to be used as a split delimiter.
SplitPath []string `json:"split_path,omitempty"`
+ fsmap *filesystems.FilesystemMap
+
logger *zap.Logger
}
@@ -269,12 +271,16 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
func (m *MatchFile) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
- m.fileSystem, _ = ctx.Filesystems().Get(m.FileSystem)
+ m.fsmap = ctx.Filesystems()
if m.Root == "" {
m.Root = "{http.vars.root}"
}
+ if m.FileSystem == "" {
+ m.FileSystem = "{http.vars.fs}"
+ }
+
// if list of files to try was omitted entirely, assume URL path
// (use placeholder instead of r.URL.Path; see issue #4146)
if m.TryFiles == nil {
@@ -315,6 +321,10 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
root := filepath.Clean(repl.ReplaceAll(m.Root, "."))
+ fsName := repl.ReplaceAll(m.FileSystem, "")
+
+ fileSystem, _ := m.fsmap.Get(fsName)
+
type matchCandidate struct {
fullpath, relative, splitRemainder string
}
@@ -363,7 +373,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
if runtime.GOOS == "windows" {
globResults = []string{fullPattern} // precious Windows
} else {
- globResults, err = fs.Glob(m.fileSystem, fullPattern)
+ globResults, err = fs.Glob(fileSystem, fullPattern)
if err != nil {
m.logger.Error("expanding glob", zap.Error(err))
}
@@ -405,7 +415,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
}
candidates := makeCandidates(pattern)
for _, c := range candidates {
- if info, exists := m.strictFileExists(c.fullpath); exists {
+ if info, exists := m.strictFileExists(fileSystem, c.fullpath); exists {
setPlaceholders(c, info)
return true
}
@@ -419,7 +429,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern)
for _, c := range candidates {
- info, err := fs.Stat(m.fileSystem, c.fullpath)
+ info, err := fs.Stat(fileSystem, c.fullpath)
if err == nil && info.Size() > largestSize {
largestSize = info.Size()
largest = c
@@ -440,7 +450,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern)
for _, c := range candidates {
- info, err := fs.Stat(m.fileSystem, c.fullpath)
+ info, err := fs.Stat(fileSystem, c.fullpath)
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
smallestSize = info.Size()
smallest = c
@@ -460,7 +470,7 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
for _, pattern := range m.TryFiles {
candidates := makeCandidates(pattern)
for _, c := range candidates {
- info, err := fs.Stat(m.fileSystem, c.fullpath)
+ info, err := fs.Stat(fileSystem, c.fullpath)
if err == nil &&
(recentInfo == nil || info.ModTime().After(recentInfo.ModTime())) {
recent = c
@@ -498,8 +508,8 @@ func parseErrorCode(input string) error {
// the file must also be a directory; if it does
// NOT end in a forward slash, the file must NOT
// be a directory.
-func (m MatchFile) strictFileExists(file string) (os.FileInfo, bool) {
- info, err := fs.Stat(m.fileSystem, file)
+func (m MatchFile) strictFileExists(fileSystem fs.FS, file string) (os.FileInfo, bool) {
+ info, err := fs.Stat(fileSystem, file)
if err != nil {
// in reality, this can be any error
// such as permission or even obscure
diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go
index 47631d78eef3..de4ba0971eb8 100644
--- a/modules/caddyhttp/fileserver/staticfiles.go
+++ b/modules/caddyhttp/fileserver/staticfiles.go
@@ -252,14 +252,16 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
filesToHide := fsrv.transformHidePaths(repl)
root := repl.ReplaceAll(fsrv.Root, ".")
+ fsName := repl.ReplaceAll(fsrv.FileSystem, "")
- fileSystem, _ := fsrv.fsmap.Get(repl.ReplaceAll(fsrv.FileSystem, ""))
+ fileSystem, _ := fsrv.fsmap.Get(fsName)
// remove any trailing `/` as it breaks fs.ValidPath() in the stdlib
filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/")
fsrv.logger.Debug("sanitized path join",
zap.String("site_root", root),
+ zap.String("fs", fsName),
zap.String("request_path", r.URL.Path),
zap.String("result", filename))
From fc80a5d8e428b0cead1c99988ee678f33439f680 Mon Sep 17 00:00:00 2001
From: a
Date: Thu, 21 Sep 2023 13:24:36 -0500
Subject: [PATCH 2/4] remove unused
---
internal/filesystems/map.go | 48 ++++++++++++++++++-------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/internal/filesystems/map.go b/internal/filesystems/map.go
index 7de5a94d627a..e9fe751c3096 100644
--- a/internal/filesystems/map.go
+++ b/internal/filesystems/map.go
@@ -2,6 +2,7 @@ package filesystems
import (
"io/fs"
+ "sync"
)
const (
@@ -9,7 +10,7 @@ const (
)
var (
- DefaultFilesystem = OsFS{}
+ DefaultFilesystem = &wrapperFs{key: DefaultFilesystemKey, FS: OsFS{}}
)
type emptyFs struct {
@@ -19,6 +20,7 @@ func (emptyFs) Open(name string) (fs.File, error) {
return nil, fs.ErrNotExist
}
+// wrapperFs exists so can easily add to wrapperFs down the line
type wrapperFs struct {
key string
fs.FS
@@ -28,58 +30,56 @@ type wrapperFs struct {
// the empty key will be overwritten to be the default key
// it includes a default filesystem, based off the os fs
type FilesystemMap struct {
- m map[string]*wrapperFs
+ m sync.Map
}
-func (f *FilesystemMap) get(k string) *wrapperFs {
- if f.m == nil {
- f.m = map[string]*wrapperFs{
- DefaultFilesystemKey: {key: DefaultFilesystemKey, FS: DefaultFilesystem},
- }
- }
+// note that the first invocation of key cannot be called in a racy context.
+func (f *FilesystemMap) key(k string) string {
if k == "" {
k = DefaultFilesystemKey
}
- _, ok := f.m[k]
- if !ok {
- f.m[k] = &wrapperFs{
- key: k,
- FS: emptyFs{},
- }
- }
- return f.m[k]
+ return k
}
// Register will add the filesystem with key to later be retrieved
// A call with a nil fs will call unregister, ensuring that a call to Default() will never be nil
func (f *FilesystemMap) Register(k string, v fs.FS) {
+ k = f.key(k)
if v == nil {
f.Unregister(k)
return
}
- c := f.get(k)
- c.FS = v
+ f.m.Store(k, &wrapperFs{key: k, FS: v})
}
// Unregister will remove the filesystem with key from the filesystem map
// if the key is the default key, it will set the default to the osFS instead of deleting it
// modules should call this on cleanup to be safe
func (f *FilesystemMap) Unregister(k string) {
- c := f.get(k)
+ k = f.key(k)
if k == DefaultFilesystemKey {
- c.FS = DefaultFilesystem
+ f.m.Store(k, DefaultFilesystem)
} else {
- delete(f.m, k)
+ f.m.Delete(k)
}
}
// Get will get a filesystem with a given key
func (f *FilesystemMap) Get(k string) (v fs.FS, ok bool) {
- c := f.get(k)
- return c, c.FS != nil
+ k = f.key(k)
+ c, ok := f.m.Load(k)
+ if !ok {
+ if k == DefaultFilesystemKey {
+ f.m.Store(k, DefaultFilesystem)
+ return DefaultFilesystem, true
+ }
+ return nil, ok
+ }
+ return c.(fs.FS), false
}
// Default will get the default filesystem in the filesystem map
func (f *FilesystemMap) Default() fs.FS {
- return f.get(DefaultFilesystemKey)
+ val, _ := f.Get(DefaultFilesystemKey)
+ return val
}
From ec62f5f60efbf512643d0363c94e890b93609776 Mon Sep 17 00:00:00 2001
From: a
Date: Thu, 21 Sep 2023 13:27:34 -0500
Subject: [PATCH 3/4] error on no fs
---
modules/caddyhttp/fileserver/matcher.go | 7 +++++--
modules/caddyhttp/fileserver/staticfiles.go | 5 ++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index c11946fbf05f..163c9941e609 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -323,8 +323,11 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
fsName := repl.ReplaceAll(m.FileSystem, "")
- fileSystem, _ := m.fsmap.Get(fsName)
-
+ fileSystem, ok := m.fsmap.Get(fsName)
+ if !ok {
+ m.logger.Error("use of unregistered filesystem", zap.String("fs", "fsName"))
+ return false
+ }
type matchCandidate struct {
fullpath, relative, splitRemainder string
}
diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go
index de4ba0971eb8..3051db82b8b6 100644
--- a/modules/caddyhttp/fileserver/staticfiles.go
+++ b/modules/caddyhttp/fileserver/staticfiles.go
@@ -254,7 +254,10 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
root := repl.ReplaceAll(fsrv.Root, ".")
fsName := repl.ReplaceAll(fsrv.FileSystem, "")
- fileSystem, _ := fsrv.fsmap.Get(fsName)
+ fileSystem, ok := fsrv.fsmap.Get(fsName)
+ if !ok {
+ return caddyhttp.Error(http.StatusNotFound, fmt.Errorf("filesystem not found"))
+ }
// remove any trailing `/` as it breaks fs.ValidPath() in the stdlib
filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/")
From 50158703ae6c4a4df167a444f9989811c20dad28 Mon Sep 17 00:00:00 2001
From: a
Date: Thu, 21 Sep 2023 13:27:40 -0500
Subject: [PATCH 4/4] a
---
internal/filesystems/map.go | 7 -------
1 file changed, 7 deletions(-)
diff --git a/internal/filesystems/map.go b/internal/filesystems/map.go
index e9fe751c3096..286f4e2fcc21 100644
--- a/internal/filesystems/map.go
+++ b/internal/filesystems/map.go
@@ -13,13 +13,6 @@ var (
DefaultFilesystem = &wrapperFs{key: DefaultFilesystemKey, FS: OsFS{}}
)
-type emptyFs struct {
-}
-
-func (emptyFs) Open(name string) (fs.File, error) {
- return nil, fs.ErrNotExist
-}
-
// wrapperFs exists so can easily add to wrapperFs down the line
type wrapperFs struct {
key string