mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-14 06:46:27 +03:00
fastcgi: Set PATH_INFO to file matcher remainder as fallback (#3739)
* fastcgi: Set PATH_INFO to file matcher remainder as fallback * fastcgi: Avoid changing scriptName when not necessary * Stylistic tweaks Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
5643dc3fb9
commit
6e9ac248dd
3 changed files with 52 additions and 29 deletions
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) (suffix, fullpath, remainder string) {
|
||||
suffix, remainder = m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
|
||||
if strings.HasSuffix(file, "/") {
|
||||
suffix += "/"
|
||||
}
|
||||
fullpath := sanitizedPathJoin(root, suffix)
|
||||
return suffix, fullpath
|
||||
fullpath = sanitizedPathJoin(root, suffix)
|
||||
return
|
||||
}
|
||||
|
||||
// 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() {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -297,10 +307,11 @@ 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 {
|
||||
// return values are the first piece of the path that
|
||||
// ends with the split substring and the remainder.
|
||||
// If the path cannot be split, the path is returned
|
||||
// as-is (with no remainder).
|
||||
func (m MatchFile) firstSplit(path string) (splitPart, remainder string) {
|
||||
for _, split := range m.SplitPath {
|
||||
if idx := indexFold(path, split); idx > -1 {
|
||||
pos := idx + len(split)
|
||||
|
@ -308,10 +319,10 @@ func (m MatchFile) firstSplit(path string) string {
|
|||
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(),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,19 +195,27 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
|
|||
}
|
||||
|
||||
fpath := r.URL.Path
|
||||
scriptName := fpath
|
||||
|
||||
docURI := fpath
|
||||
// split "actual path" from "path info" if configured
|
||||
var docURI, pathInfo string
|
||||
var pathInfo string
|
||||
if splitPos := t.splitPos(fpath); splitPos > -1 {
|
||||
docURI = fpath[:splitPos]
|
||||
pathInfo = fpath[splitPos:]
|
||||
} else {
|
||||
docURI = fpath
|
||||
}
|
||||
scriptName := fpath
|
||||
|
||||
// Strip PATH_INFO from SCRIPT_NAME
|
||||
scriptName = strings.TrimSuffix(scriptName, pathInfo)
|
||||
// Strip PATH_INFO from SCRIPT_NAME
|
||||
scriptName = strings.TrimSuffix(scriptName, pathInfo)
|
||||
}
|
||||
|
||||
// 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.GetString("http.matchers.file.remainder"); ok {
|
||||
pathInfo = remainder
|
||||
}
|
||||
}
|
||||
|
||||
// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
|
||||
scriptFilename := filepath.Join(root, scriptName)
|
||||
|
|
Loading…
Reference in a new issue