summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/fileserver/matcher.go
diff options
context:
space:
mode:
authorFrancis Lavoie <lavofr@gmail.com>2020-09-16 20:09:28 -0400
committerGitHub <noreply@github.com>2020-09-16 18:09:28 -0600
commitb95b87381a282e1fe57295d145b71645d7801f07 (patch)
tree853f7368ac81f6ede2de30503201cf943841b34e /modules/caddyhttp/fileserver/matcher.go
parentb01bb275b395643542ceca4fbc82bedea8e43937 (diff)
fileserver: Fix try_files for directories; windows fix (#3684)
* fileserver: Fix try_files for directories, windows fix * fileserver: Add new file type placeholder, refactoring, tests * fileserver: Review cleanup * fileserver: Flip the return args order
Diffstat (limited to 'modules/caddyhttp/fileserver/matcher.go')
-rw-r--r--modules/caddyhttp/fileserver/matcher.go85
1 files changed, 53 insertions, 32 deletions
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index 1844421..475a9ee 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -19,6 +19,7 @@ import (
"net/http"
"os"
"path"
+ "path/filepath"
"strings"
"time"
@@ -34,7 +35,7 @@ func init() {
// MatchFile is an HTTP request matcher that can match
// requests based upon file existence.
//
-// Upon matching, two new placeholders will be made
+// Upon matching, three new placeholders will be made
// available:
//
// - `{http.matchers.file.relative}` The root-relative
@@ -42,6 +43,8 @@ func init() {
// requests.
// - `{http.matchers.file.absolute}` The absolute path
// of the matched file.
+// - `{http.matchers.file.type}` Set to "directory" if
+// the matched file is a directory, "file" otherwise.
type MatchFile struct {
// The root directory, used for creating absolute
// file paths, and required when working with
@@ -153,25 +156,18 @@ func (m MatchFile) Validate() error {
}
// Match returns true if r matches m. Returns true
-// if a file was matched. If so, two placeholders
+// if a file was matched. If so, three placeholders
// will be available:
// - http.matchers.file.relative
// - http.matchers.file.absolute
+// - http.matchers.file.type
func (m MatchFile) Match(r *http.Request) bool {
- repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
- rel, abs, matched := m.selectFile(r)
- if matched {
- repl.Set("http.matchers.file.relative", rel)
- repl.Set("http.matchers.file.absolute", abs)
- }
- return matched
+ return m.selectFile(r)
}
// selectFile chooses a file according to m.TryPolicy by appending
// the paths in m.TryFiles to m.Root, with placeholder replacements.
-// It returns the root-relative path to the matched file, the full
-// or absolute path, and whether a match was made.
-func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
+func (m MatchFile) selectFile(r *http.Request) (matched bool) {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
root := repl.ReplaceAll(m.Root, ".")
@@ -183,13 +179,35 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
m.TryFiles = []string{r.URL.Path}
}
+ // common preparation of the file into parts
+ prepareFilePath := func(file string) (string, string) {
+ suffix := m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
+ if strings.HasSuffix(file, "/") {
+ suffix += "/"
+ }
+ fullpath := sanitizedPathJoin(root, suffix)
+ return suffix, fullpath
+ }
+
+ // sets up the placeholders for the matched file
+ setPlaceholders := func(info os.FileInfo, rel string, abs string) {
+ repl.Set("http.matchers.file.relative", rel)
+ repl.Set("http.matchers.file.absolute", abs)
+
+ fileType := "file"
+ if info.IsDir() {
+ fileType = "directory"
+ }
+ repl.Set("http.matchers.file.type", fileType)
+ }
+
switch m.TryPolicy {
case "", tryPolicyFirstExist:
for _, f := range m.TryFiles {
- suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
- fullpath := sanitizedPathJoin(root, suffix)
- if strictFileExists(fullpath) {
- return suffix, fullpath, true
+ suffix, fullpath := prepareFilePath(f)
+ if info, exists := strictFileExists(fullpath); exists {
+ setPlaceholders(info, suffix, fullpath)
+ return true
}
}
@@ -197,9 +215,9 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
var largestSize int64
var largestFilename string
var largestSuffix string
+ var info os.FileInfo
for _, f := range m.TryFiles {
- suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
- fullpath := sanitizedPathJoin(root, suffix)
+ suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && info.Size() > largestSize {
largestSize = info.Size()
@@ -207,15 +225,16 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
largestSuffix = suffix
}
}
- return largestSuffix, largestFilename, true
+ setPlaceholders(info, largestSuffix, largestFilename)
+ return true
case tryPolicySmallestSize:
var smallestSize int64
var smallestFilename string
var smallestSuffix string
+ var info os.FileInfo
for _, f := range m.TryFiles {
- suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
- fullpath := sanitizedPathJoin(root, suffix)
+ suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
smallestSize = info.Size()
@@ -223,15 +242,16 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
smallestSuffix = suffix
}
}
- return smallestSuffix, smallestFilename, true
+ setPlaceholders(info, smallestSuffix, smallestFilename)
+ return true
case tryPolicyMostRecentlyMod:
var recentDate time.Time
var recentFilename string
var recentSuffix string
+ var info os.FileInfo
for _, f := range m.TryFiles {
- suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
- fullpath := sanitizedPathJoin(root, suffix)
+ suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil &&
(recentDate.IsZero() || info.ModTime().After(recentDate)) {
@@ -240,7 +260,8 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
recentSuffix = suffix
}
}
- return recentSuffix, recentFilename, true
+ setPlaceholders(info, recentSuffix, recentFilename)
+ return true
}
return
@@ -252,7 +273,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
// 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 {
+func strictFileExists(file string) (os.FileInfo, bool) {
stat, err := os.Stat(file)
if err != nil {
// in reality, this can be any error
@@ -263,16 +284,16 @@ func strictFileExists(file string) bool {
// the file exists, so we just treat any
// error as if it does not exist; see
// https://stackoverflow.com/a/12518877/1048862
- return false
+ return nil, false
}
- if strings.HasSuffix(file, "/") {
+ if strings.HasSuffix(file, string(filepath.Separator)) {
// by convention, file paths ending
- // in a slash must be a directory
- return stat.IsDir()
+ // in a path separator must be a directory
+ return stat, stat.IsDir()
}
// by convention, file paths NOT ending
- // in a slash must NOT be a directory
- return !stat.IsDir()
+ // in a path separator must NOT be a directory
+ return stat, !stat.IsDir()
}
// firstSplit returns the first result where the path