From 14f9662f9cc0f93e88d5efbbaf10de79070bea93 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 6 Sep 2019 12:36:45 -0600 Subject: Various fixes/tweaks to HTTP placeholder variables and file matching - Rename http.var.* -> http.vars.* to be more consistent - Prefixing a path matcher with * now invokes simple suffix matching - Handlers and matchers that need a root path default to {http.vars.root} - Clean replacer output on the file matcher's file selection suffix --- modules/caddyhttp/fileserver/caddyfile.go | 8 +------- modules/caddyhttp/fileserver/matcher.go | 16 +++++++++++----- modules/caddyhttp/fileserver/staticfiles.go | 4 ++++ 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'modules/caddyhttp/fileserver') diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go index 7afcc9e..4622af2 100644 --- a/modules/caddyhttp/fileserver/caddyfile.go +++ b/modules/caddyhttp/fileserver/caddyfile.go @@ -17,9 +17,9 @@ package fileserver import ( "encoding/json" - "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" + "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" ) func init() { @@ -71,11 +71,6 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) } } - // if no root was configured explicitly, use site root - if fsrv.Root == "" { - fsrv.Root = "{http.var.root}" - } - // hide the Caddyfile (and any imported Caddyfiles) if configFiles := h.Caddyfiles(); len(configFiles) > 0 { for _, file := range configFiles { @@ -104,7 +99,6 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) matcherSet := map[string]json.RawMessage{ "file": h.JSON(MatchFile{ - Root: "{http.var.root}", TryFiles: try, }, nil), } diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index b091250..99e217e 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -18,6 +18,7 @@ import ( "fmt" "net/http" "os" + "path" "time" "github.com/caddyserver/caddy/v2" @@ -87,8 +88,13 @@ func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } } } + return nil +} + +// Provision sets up m's defaults. +func (m *MatchFile) Provision(_ caddy.Context) error { if m.Root == "" { - m.Root = "{http.var.root}" + m.Root = "{http.vars.root}" } return nil } @@ -141,7 +147,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { switch m.TryPolicy { case "", tryPolicyFirstExist: for _, f := range m.TryFiles { - suffix := repl.ReplaceAll(f, "") + suffix := path.Clean(repl.ReplaceAll(f, "")) fullpath := sanitizedPathJoin(root, suffix) if fileExists(fullpath) { return suffix, fullpath, true @@ -153,7 +159,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { var largestFilename string var largestSuffix string for _, f := range m.TryFiles { - suffix := repl.ReplaceAll(f, "") + suffix := path.Clean(repl.ReplaceAll(f, "")) fullpath := sanitizedPathJoin(root, suffix) info, err := os.Stat(fullpath) if err == nil && info.Size() > largestSize { @@ -169,7 +175,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { var smallestFilename string var smallestSuffix string for _, f := range m.TryFiles { - suffix := repl.ReplaceAll(f, "") + suffix := path.Clean(repl.ReplaceAll(f, "")) fullpath := sanitizedPathJoin(root, suffix) info, err := os.Stat(fullpath) if err == nil && (smallestSize == 0 || info.Size() < smallestSize) { @@ -185,7 +191,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { var recentFilename string var recentSuffix string for _, f := range m.TryFiles { - suffix := repl.ReplaceAll(f, "") + suffix := path.Clean(repl.ReplaceAll(f, "")) fullpath := sanitizedPathJoin(root, suffix) info, err := os.Stat(fullpath) if err == nil && diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index cdac453..cfb79f8 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -57,6 +57,10 @@ func (FileServer) CaddyModule() caddy.ModuleInfo { // Provision sets up the static files responder. func (fsrv *FileServer) Provision(ctx caddy.Context) error { + if fsrv.Root == "" { + fsrv.Root = "{http.vars.root}" + } + if fsrv.IndexNames == nil { fsrv.IndexNames = defaultIndexNames } -- cgit v1.2.3 From 4bd949652564eff8ccfc50f105f0d35f0af26402 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 6 Sep 2019 12:57:12 -0600 Subject: Fix Schrodinger's file existence check in file matcher See: https://stackoverflow.com/a/12518877/1048862 For example, trying to check the existence of "/www/index.php/index.php" fails but not with an os.IsNotExist()-type error. So we have to assume that a file that cannot be successfully stat'ed at all does not exist. --- modules/caddyhttp/fileserver/matcher.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'modules/caddyhttp/fileserver') diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index 99e217e..88ce1d0 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -207,10 +207,22 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { return } -// fileExists returns true if file exists. +// fileExists returns true if file exists, +// false if it doesn't, or false if there +// was any other error. func fileExists(file string) bool { _, err := os.Stat(file) - return !os.IsNotExist(err) + if err == nil { + return true + } else if os.IsNotExist(err) { + return false + } else { + // we don't know if it exists, + // so assume it doesn't, since + // there must have been some + // other error anyway + return false + } } const ( -- cgit v1.2.3 From 97ace2a39e058f435d4e6adbee874eaabb42e45d Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 6 Sep 2019 13:32:02 -0600 Subject: File matcher enforces trailing-slash convention to match dirs/files --- modules/caddyhttp/fileserver/matcher.go | 42 +++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'modules/caddyhttp/fileserver') diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index 88ce1d0..fde086e 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -19,6 +19,7 @@ import ( "net/http" "os" "path" + "strings" "time" "github.com/caddyserver/caddy/v2" @@ -149,7 +150,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { for _, f := range m.TryFiles { suffix := path.Clean(repl.ReplaceAll(f, "")) fullpath := sanitizedPathJoin(root, suffix) - if fileExists(fullpath) { + if strictFileExists(fullpath) { return suffix, fullpath, true } } @@ -207,22 +208,33 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { return } -// fileExists returns true if file exists, -// false if it doesn't, or false if there -// was any other error. -func fileExists(file string) bool { - _, err := os.Stat(file) - if err == nil { - return true - } else if os.IsNotExist(err) { - return false - } else { - // we don't know if it exists, - // so assume it doesn't, since - // there must have been some - // other error anyway +// strictFileExists returns true if file exists +// and matches the convention of the given file +// path. If the path ends in a forward slash, +// the file must also be a directory; if it does +// NOT end in a forward slash, the file must NOT +// be a directory. +func strictFileExists(file string) bool { + stat, err := os.Stat(file) + if err != nil { + // in reality, this can be any error + // such as permission or even obscure + // ones like "is not a directory" (when + // trying to stat a file within a file); + // in those cases we can't be sure if + // the file exists, so we just treat any + // error as if it does not exist; see + // https://stackoverflow.com/a/12518877/1048862 return false } + if strings.HasSuffix(file, "/") { + // by convention, file paths ending + // in a slash must be a directory + return stat.IsDir() + } + // by convention, file paths NOT ending + // in a slash must NOT be a directory + return !stat.IsDir() } const ( -- cgit v1.2.3