mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-14 14:56:27 +03:00
fileserver: Don't assume len(str) == len(ToLower(str)) (fix #3623)
We can't use a positional index on an original string that we got from its lower-cased equivalent. Implement our own IndexFold() function b/c the std lib does not have one.
This commit is contained in:
parent
6f73a358f4
commit
3860b235d0
2 changed files with 56 additions and 27 deletions
|
@ -117,11 +117,13 @@ func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
return d.ArgErr()
|
||||
}
|
||||
m.TryPolicy = d.Val()
|
||||
case "split":
|
||||
case "split_path":
|
||||
m.SplitPath = d.RemainingArgs()
|
||||
if len(m.SplitPath) == 0 {
|
||||
return d.ArgErr()
|
||||
}
|
||||
default:
|
||||
return d.Errf("unrecognized subdirective: %s", d.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,9 +281,8 @@ func strictFileExists(file string) bool {
|
|||
// in the split value. Returns the path as-is if the
|
||||
// path cannot be split.
|
||||
func (m MatchFile) firstSplit(path string) string {
|
||||
lowerPath := strings.ToLower(path)
|
||||
for _, split := range m.SplitPath {
|
||||
if idx := strings.Index(lowerPath, strings.ToLower(split)); idx > -1 {
|
||||
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:], "/") {
|
||||
|
@ -293,6 +294,19 @@ func (m MatchFile) firstSplit(path string) string {
|
|||
return path
|
||||
}
|
||||
|
||||
// There is no strings.IndexFold() function like there is strings.EqualFold(),
|
||||
// but we can use strings.EqualFold() to build our own case-insensitive
|
||||
// substring search (as of Go 1.14).
|
||||
func indexFold(haystack, needle string) int {
|
||||
nlen := len(needle)
|
||||
for i := 0; i+nlen < len(haystack); i++ {
|
||||
if strings.EqualFold(haystack[i:i+nlen], needle) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
const (
|
||||
tryPolicyFirstExist = "first_exist"
|
||||
tryPolicyLargestSize = "largest_size"
|
||||
|
|
|
@ -22,69 +22,84 @@ import (
|
|||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func TestPhpFileMatcher(t *testing.T) {
|
||||
|
||||
func TestPHPFileMatcher(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
path string
|
||||
path string
|
||||
expectedPath string
|
||||
matched bool
|
||||
matched bool
|
||||
}{
|
||||
{
|
||||
path: "/index.php",
|
||||
path: "/index.php",
|
||||
expectedPath: "/index.php",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/index.php/somewhere",
|
||||
path: "/index.php/somewhere",
|
||||
expectedPath: "/index.php",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/remote.php",
|
||||
path: "/remote.php",
|
||||
expectedPath: "/remote.php",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/remote.php/somewhere",
|
||||
path: "/remote.php/somewhere",
|
||||
expectedPath: "/remote.php",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/missingfile.php",
|
||||
path: "/missingfile.php",
|
||||
matched: false,
|
||||
},
|
||||
{
|
||||
path: "/notphp.php.txt",
|
||||
path: "/notphp.php.txt",
|
||||
expectedPath: "/notphp.php.txt",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/notphp.php.txt/",
|
||||
path: "/notphp.php.txt/",
|
||||
expectedPath: "/notphp.php.txt",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/notphp.php.txt.suffixed",
|
||||
path: "/notphp.php.txt.suffixed",
|
||||
matched: false,
|
||||
},
|
||||
{
|
||||
path: "/foo.php.php/index.php",
|
||||
path: "/foo.php.php/index.php",
|
||||
expectedPath: "/foo.php.php/index.php",
|
||||
matched: true,
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
path: "/foo.php.PHP/index.php",
|
||||
expectedPath: "/foo.php.PHP/index.php",
|
||||
matched: true,
|
||||
},
|
||||
{
|
||||
// See https://github.com/caddyserver/caddy/issues/3623
|
||||
path: "/%E2%C3",
|
||||
expectedPath: "/%E2%C3",
|
||||
matched: false,
|
||||
},
|
||||
} {
|
||||
m := &MatchFile{
|
||||
Root: "./testdata",
|
||||
TryFiles: []string{"{http.request.uri.path}"},
|
||||
TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php"},
|
||||
SplitPath: []string{".php"},
|
||||
}
|
||||
|
||||
req := &http.Request{URL: &url.URL{Path: tc.path}}
|
||||
u, err := url.Parse(tc.path)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: parsing path: %v", i, err)
|
||||
}
|
||||
|
||||
req := &http.Request{URL: u}
|
||||
repl := caddyhttp.NewTestReplacer(req)
|
||||
|
||||
result := m.Match(req)
|
||||
if result != tc.matched {
|
||||
t.Fatalf("Test %d: match bool result: %v, expected: %v", i, result, tc.matched)
|
||||
t.Fatalf("Test %d: expected match=%t, got %t", i, tc.matched, result)
|
||||
}
|
||||
|
||||
rel, ok := repl.Get("http.matchers.file.relative")
|
||||
|
@ -99,4 +114,4 @@ func TestPhpFileMatcher(t *testing.T) {
|
|||
t.Fatalf("Test %d: actual path: %v, expected: %v", i, rel, tc.expectedPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue