From 0fc97211abd46098f5953fc6b152aa891060fca5 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 15 Nov 2019 12:47:06 -0700 Subject: [PATCH] http: Make path matcher case-insensitive Adds tests for both the path matcher and host matcher for case insensitivity. If case sensitivity is required for the path, a regexp matcher can be used instead. This is the v2 equivalent fix of PR #2882. --- modules/caddyhttp/matchers.go | 19 +++++++++++++----- modules/caddyhttp/matchers_test.go | 32 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 23b13dd1..bddb2140 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -33,10 +33,10 @@ import ( ) type ( - // MatchHost matches requests by the Host value. + // MatchHost matches requests by the Host value (case-insensitive). MatchHost []string - // MatchPath matches requests by the URI's path. + // MatchPath matches requests by the URI's path (case-insensitive). MatchPath []string // MatchPathRE matches requests by a regular expression on the URI's path. @@ -154,20 +154,29 @@ func (MatchPath) CaddyModule() caddy.ModuleInfo { } } +// Provision lower-cases the paths in m to ensure case-insensitive matching. +func (m MatchPath) Provision(_ caddy.Context) error { + for i := range m { + m[i] = strings.ToLower(m[i]) + } + return nil +} + // Match returns true if r matches m. func (m MatchPath) Match(r *http.Request) bool { + lowerPath := strings.ToLower(r.URL.Path) for _, matchPath := range m { // as a special case, if the first character is a // wildcard, treat it as a quick suffix match if strings.HasPrefix(matchPath, "*") { - return strings.HasSuffix(r.URL.Path, matchPath[1:]) + return strings.HasSuffix(lowerPath, matchPath[1:]) } // can ignore error here because we can't handle it anyway - matches, _ := filepath.Match(matchPath, r.URL.Path) + matches, _ := filepath.Match(matchPath, lowerPath) if matches { return true } - if strings.HasPrefix(r.URL.Path, matchPath) { + if strings.HasPrefix(lowerPath, matchPath) { return true } } diff --git a/modules/caddyhttp/matchers_test.go b/modules/caddyhttp/matchers_test.go index 81cb1d09..45694258 100644 --- a/modules/caddyhttp/matchers_test.go +++ b/modules/caddyhttp/matchers_test.go @@ -47,6 +47,16 @@ func TestHostMatcher(t *testing.T) { input: "example.com", expect: true, }, + { + match: MatchHost{"EXAMPLE.COM"}, + input: "example.com", + expect: true, + }, + { + match: MatchHost{"example.com"}, + input: "EXAMPLE.COM", + expect: true, + }, { match: MatchHost{"example.com"}, input: "foo.example.com", @@ -72,6 +82,11 @@ func TestHostMatcher(t *testing.T) { input: "example.com", expect: false, }, + { + match: MatchHost{"*.example.com"}, + input: "SUB.EXAMPLE.COM", + expect: true, + }, { match: MatchHost{"*.example.com"}, input: "foo.example.com", @@ -174,7 +189,12 @@ func TestPathMatcher(t *testing.T) { }, { match: MatchPath{"*.ext"}, - input: "foo.ext", + input: "/foo.ext", + expect: true, + }, + { + match: MatchPath{"*.php"}, + input: "/index.PHP", expect: true, }, { @@ -192,6 +212,16 @@ func TestPathMatcher(t *testing.T) { input: "/foo/bar/bam", expect: false, }, + { + match: MatchPath{"/foo"}, + input: "/FOO", + expect: true, + }, + { + match: MatchPath{"/foo/bar.txt"}, + input: "/foo/BAR.txt", + expect: true, + }, } { req := &http.Request{URL: &url.URL{Path: tc.input}} actual := tc.match.Match(req)