mirror of
https://github.com/caddyserver/caddy.git
synced 2025-03-24 04:14:47 +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()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
m.TryPolicy = d.Val()
|
m.TryPolicy = d.Val()
|
||||||
case "split":
|
case "split_path":
|
||||||
m.SplitPath = d.RemainingArgs()
|
m.SplitPath = d.RemainingArgs()
|
||||||
if len(m.SplitPath) == 0 {
|
if len(m.SplitPath) == 0 {
|
||||||
return d.ArgErr()
|
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
|
// in the split value. Returns the path as-is if the
|
||||||
// path cannot be split.
|
// path cannot be split.
|
||||||
func (m MatchFile) firstSplit(path string) string {
|
func (m MatchFile) firstSplit(path string) string {
|
||||||
lowerPath := strings.ToLower(path)
|
|
||||||
for _, split := range m.SplitPath {
|
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)
|
pos := idx + len(split)
|
||||||
// skip the split if it's not the final part of the filename
|
// skip the split if it's not the final part of the filename
|
||||||
if pos != len(path) && !strings.HasPrefix(path[pos:], "/") {
|
if pos != len(path) && !strings.HasPrefix(path[pos:], "/") {
|
||||||
|
@ -293,6 +294,19 @@ func (m MatchFile) firstSplit(path string) string {
|
||||||
return path
|
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 (
|
const (
|
||||||
tryPolicyFirstExist = "first_exist"
|
tryPolicyFirstExist = "first_exist"
|
||||||
tryPolicyLargestSize = "largest_size"
|
tryPolicyLargestSize = "largest_size"
|
||||||
|
|
|
@ -22,69 +22,84 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPhpFileMatcher(t *testing.T) {
|
func TestPHPFileMatcher(t *testing.T) {
|
||||||
|
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
path string
|
path string
|
||||||
expectedPath string
|
expectedPath string
|
||||||
matched bool
|
matched bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
path: "/index.php",
|
path: "/index.php",
|
||||||
expectedPath: "/index.php",
|
expectedPath: "/index.php",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/index.php/somewhere",
|
path: "/index.php/somewhere",
|
||||||
expectedPath: "/index.php",
|
expectedPath: "/index.php",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/remote.php",
|
path: "/remote.php",
|
||||||
expectedPath: "/remote.php",
|
expectedPath: "/remote.php",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/remote.php/somewhere",
|
path: "/remote.php/somewhere",
|
||||||
expectedPath: "/remote.php",
|
expectedPath: "/remote.php",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/missingfile.php",
|
path: "/missingfile.php",
|
||||||
matched: false,
|
matched: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/notphp.php.txt",
|
path: "/notphp.php.txt",
|
||||||
expectedPath: "/notphp.php.txt",
|
expectedPath: "/notphp.php.txt",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/notphp.php.txt/",
|
path: "/notphp.php.txt/",
|
||||||
expectedPath: "/notphp.php.txt",
|
expectedPath: "/notphp.php.txt",
|
||||||
matched: true,
|
matched: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/notphp.php.txt.suffixed",
|
path: "/notphp.php.txt.suffixed",
|
||||||
matched: false,
|
matched: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/foo.php.php/index.php",
|
path: "/foo.php.php/index.php",
|
||||||
expectedPath: "/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{
|
m := &MatchFile{
|
||||||
Root: "./testdata",
|
Root: "./testdata",
|
||||||
TryFiles: []string{"{http.request.uri.path}"},
|
TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php"},
|
||||||
SplitPath: []string{".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)
|
repl := caddyhttp.NewTestReplacer(req)
|
||||||
|
|
||||||
result := m.Match(req)
|
result := m.Match(req)
|
||||||
if result != tc.matched {
|
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")
|
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)
|
t.Fatalf("Test %d: actual path: %v, expected: %v", i, rel, tc.expectedPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue