Skip to content

Commit

Permalink
fastcgi: Set PATH_INFO to file matcher remainder as fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
francislavoie committed Nov 25, 2020
1 parent 53aa60a commit 1b8ab84
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 20 deletions.
46 changes: 28 additions & 18 deletions modules/caddyhttp/fileserver/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func init() {
// of the matched file.
// - `{http.matchers.file.type}` Set to "directory" if
// the matched file is a directory, "file" otherwise.
// - `{http.matchers.file.remainder}` Set to the remainder
// of the path if the path was split by `split_path`.
type MatchFile struct {
// The root directory, used for creating absolute
// file paths, and required when working with
Expand Down Expand Up @@ -155,11 +157,12 @@ func (m MatchFile) Validate() error {
}

// Match returns true if r matches m. Returns true
// if a file was matched. If so, three placeholders
// if a file was matched. If so, four placeholders
// will be available:
// - http.matchers.file.relative
// - http.matchers.file.absolute
// - http.matchers.file.type
// - http.matchers.file.remainder
func (m MatchFile) Match(r *http.Request) bool {
return m.selectFile(r)
}
Expand All @@ -179,19 +182,20 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
}

// common preparation of the file into parts
prepareFilePath := func(file string) (string, string) {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
prepareFilePath := func(file string) (string, string, string) {
suffix, remainder := m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
if strings.HasSuffix(file, "/") {
suffix += "/"
}
fullpath := sanitizedPathJoin(root, suffix)
return suffix, fullpath
return suffix, fullpath, remainder
}

// sets up the placeholders for the matched file
setPlaceholders := func(info os.FileInfo, rel string, abs string) {
setPlaceholders := func(info os.FileInfo, rel string, abs string, remainder string) {
repl.Set("http.matchers.file.relative", rel)
repl.Set("http.matchers.file.absolute", abs)
repl.Set("http.matchers.file.remainder", remainder)

fileType := "file"
if info.IsDir() {
Expand All @@ -203,9 +207,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
switch m.TryPolicy {
case "", tryPolicyFirstExist:
for _, f := range m.TryFiles {
suffix, fullpath := prepareFilePath(f)
suffix, fullpath, remainder := prepareFilePath(f)
if info, exists := strictFileExists(fullpath); exists {
setPlaceholders(info, suffix, fullpath)
setPlaceholders(info, suffix, fullpath, remainder)
return true
}
}
Expand All @@ -214,52 +218,58 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
var largestSize int64
var largestFilename string
var largestSuffix string
var remainder string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix, fullpath := prepareFilePath(f)
suffix, fullpath, splitRemainder := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && info.Size() > largestSize {
largestSize = info.Size()
largestFilename = fullpath
largestSuffix = suffix
remainder = splitRemainder
}
}
setPlaceholders(info, largestSuffix, largestFilename)
setPlaceholders(info, largestSuffix, largestFilename, remainder)
return true

case tryPolicySmallestSize:
var smallestSize int64
var smallestFilename string
var smallestSuffix string
var remainder string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix, fullpath := prepareFilePath(f)
suffix, fullpath, splitRemainder := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
smallestSize = info.Size()
smallestFilename = fullpath
smallestSuffix = suffix
remainder = splitRemainder
}
}
setPlaceholders(info, smallestSuffix, smallestFilename)
setPlaceholders(info, smallestSuffix, smallestFilename, remainder)
return true

case tryPolicyMostRecentlyMod:
var recentDate time.Time
var recentFilename string
var recentSuffix string
var remainder string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix, fullpath := prepareFilePath(f)
suffix, fullpath, splitRemainder := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil &&
(recentDate.IsZero() || info.ModTime().After(recentDate)) {
recentDate = info.ModTime()
recentFilename = fullpath
recentSuffix = suffix
remainder = splitRemainder
}
}
setPlaceholders(info, recentSuffix, recentFilename)
setPlaceholders(info, recentSuffix, recentFilename, remainder)
return true
}

Expand Down Expand Up @@ -298,20 +308,20 @@ func strictFileExists(file string) (os.FileInfo, bool) {
// firstSplit returns the first result where the path
// can be split in two by a value in m.SplitPath. The
// result is the first piece of the path that ends with
// in the split value. Returns the path as-is if the
// path cannot be split.
func (m MatchFile) firstSplit(path string) string {
// in the split value, and the remainder. Returns the
// path as-is if the path cannot be split.
func (m MatchFile) firstSplit(path string) (string, string) {
for _, split := range m.SplitPath {
if idx := indexFold(path, split); idx > -1 {
pos := idx + len(split)
// skip the split if it's not the final part of the filename
if pos != len(path) && !strings.HasPrefix(path[pos:], "/") {
continue
}
return path[:pos]
return path[:pos], path[pos:]
}
}
return path
return path, ""
}

// There is no strings.IndexFold() function like there is strings.EqualFold(),
Expand Down
8 changes: 6 additions & 2 deletions modules/caddyhttp/fileserver/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,13 @@ func TestPHPFileMatcher(t *testing.T) {

func TestFirstSplit(t *testing.T) {
m := MatchFile{SplitPath: []string{".php"}}
actual := m.firstSplit("index.PHP/somewhere")
actual, remainder := m.firstSplit("index.PHP/somewhere")
expected := "index.PHP"
expectedRemainder := "/somewhere"
if actual != expected {
t.Errorf("Expected %s but got %s", expected, actual)
t.Errorf("Expected split %s but got %s", expected, actual)
}
if remainder != expectedRemainder {
t.Errorf("Expected remainder %s but got %s", expectedRemainder, remainder)
}
}
9 changes: 9 additions & 0 deletions modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
}
scriptName := fpath

// Try to grab the path remainder from a file matcher if we didn't
// get a split result here.
// See https://github.com/caddyserver/caddy/issues/3718
if pathInfo == "" {
if remainder, ok := repl.Get("http.matchers.file.remainder"); ok {
pathInfo = remainder.(string)
}
}

// Strip PATH_INFO from SCRIPT_NAME
scriptName = strings.TrimSuffix(scriptName, pathInfo)

Expand Down

0 comments on commit 1b8ab84

Please sign in to comment.