Matthew Holt
6 years ago
5 changed files with 519 additions and 66 deletions
@ -0,0 +1,346 @@ |
|||
package caddyhttp |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"net/http" |
|||
"net/http/httptest" |
|||
"net/url" |
|||
"testing" |
|||
) |
|||
|
|||
func TestHostMatcher(t *testing.T) { |
|||
for i, tc := range []struct { |
|||
match matchHost |
|||
input string |
|||
expect bool |
|||
}{ |
|||
{ |
|||
match: matchHost{}, |
|||
input: "example.com", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHost{"example.com"}, |
|||
input: "example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"example.com"}, |
|||
input: "foo.example.com", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHost{"example.com"}, |
|||
input: "EXAMPLE.COM", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"foo.example.com"}, |
|||
input: "foo.example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"foo.example.com"}, |
|||
input: "bar.example.com", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.com"}, |
|||
input: "example.com", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.com"}, |
|||
input: "foo.example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.com"}, |
|||
input: "foo.bar.example.com", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.com", "example.net"}, |
|||
input: "example.net", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"example.net", "*.example.com"}, |
|||
input: "foo.example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.net", "*.*.example.com"}, |
|||
input: "foo.bar.example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.net", "sub.*.example.com"}, |
|||
input: "sub.foo.example.com", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHost{"*.example.net", "sub.*.example.com"}, |
|||
input: "sub.foo.example.net", |
|||
expect: false, |
|||
}, |
|||
} { |
|||
req := &http.Request{Host: tc.input} |
|||
actual := tc.match.Match(req) |
|||
if actual != tc.expect { |
|||
t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input) |
|||
continue |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestPathMatcher(t *testing.T) { |
|||
for i, tc := range []struct { |
|||
match matchPath |
|||
input string |
|||
expect bool |
|||
}{ |
|||
{ |
|||
match: matchPath{}, |
|||
input: "/", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchPath{"/"}, |
|||
input: "/", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPath{"/foo/bar"}, |
|||
input: "/", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchPath{"/foo/bar"}, |
|||
input: "/foo/bar", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPath{"/foo/bar/"}, |
|||
input: "/foo/bar", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchPath{"/foo/bar/", "/other"}, |
|||
input: "/other/", |
|||
expect: true, |
|||
}, |
|||
} { |
|||
req := &http.Request{URL: &url.URL{Path: tc.input}} |
|||
actual := tc.match.Match(req) |
|||
if actual != tc.expect { |
|||
t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input) |
|||
continue |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestPathREMatcher(t *testing.T) { |
|||
for i, tc := range []struct { |
|||
match matchPathRE |
|||
input string |
|||
expect bool |
|||
expectRepl map[string]string |
|||
}{ |
|||
{ |
|||
match: matchPathRE{}, |
|||
input: "/", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "/"}}, |
|||
input: "/", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "/foo"}}, |
|||
input: "/foo", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "/foo"}}, |
|||
input: "/foo/", |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "/bar"}}, |
|||
input: "/foo/", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "^/bar"}}, |
|||
input: "/foo/bar", |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "^/foo/(.*)/baz$", Name: "name"}}, |
|||
input: "/foo/bar/baz", |
|||
expect: true, |
|||
expectRepl: map[string]string{"name.1": "bar"}, |
|||
}, |
|||
{ |
|||
match: matchPathRE{matchRegexp{Pattern: "^/foo/(?P<myparam>.*)/baz$", Name: "name"}}, |
|||
input: "/foo/bar/baz", |
|||
expect: true, |
|||
expectRepl: map[string]string{"name.myparam": "bar"}, |
|||
}, |
|||
} { |
|||
// compile the regexp and validate its name
|
|||
err := tc.match.Provision() |
|||
if err != nil { |
|||
t.Errorf("Test %d %v: Provisioning: %v", i, tc.match, err) |
|||
continue |
|||
} |
|||
err = tc.match.Validate() |
|||
if err != nil { |
|||
t.Errorf("Test %d %v: Validating: %v", i, tc.match, err) |
|||
continue |
|||
} |
|||
|
|||
// set up the fake request and its Replacer
|
|||
req := &http.Request{URL: &url.URL{Path: tc.input}} |
|||
repl := &Replacer{req: req, resp: httptest.NewRecorder(), custom: make(map[string]string)} |
|||
ctx := context.WithValue(req.Context(), ReplacerCtxKey, repl) |
|||
req = req.WithContext(ctx) |
|||
|
|||
actual := tc.match.Match(req) |
|||
if actual != tc.expect { |
|||
t.Errorf("Test %d [%v]: Expected %t, got %t for input '%s'", |
|||
i, tc.match.Pattern, tc.expect, actual, tc.input) |
|||
continue |
|||
} |
|||
|
|||
for key, expectVal := range tc.expectRepl { |
|||
placeholder := fmt.Sprintf("{http.matchers.path_regexp.%s}", key) |
|||
actualVal := repl.Replace(placeholder, "<empty>") |
|||
if actualVal != expectVal { |
|||
t.Errorf("Test %d [%v]: Expected placeholder {http.matchers.path_regexp.%s} to be '%s' but got '%s'", |
|||
i, tc.match.Pattern, key, expectVal, actualVal) |
|||
continue |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestHeaderMatcher(t *testing.T) { |
|||
for i, tc := range []struct { |
|||
match matchHeader |
|||
input http.Header // make sure these are canonical cased (std lib will do that in a real request)
|
|||
expect bool |
|||
}{ |
|||
{ |
|||
match: matchHeader{"Field": []string{"foo"}}, |
|||
input: http.Header{"Field": []string{"foo"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeader{"Field": []string{"foo", "bar"}}, |
|||
input: http.Header{"Field": []string{"bar"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeader{"Field": []string{"foo", "bar"}}, |
|||
input: http.Header{"Alakazam": []string{"kapow"}}, |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHeader{"Field": []string{"foo", "bar"}}, |
|||
input: http.Header{"Field": []string{"kapow"}}, |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHeader{"Field": []string{"foo", "bar"}}, |
|||
input: http.Header{"Field": []string{"kapow", "foo"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeader{"Field1": []string{"foo"}, "Field2": []string{"bar"}}, |
|||
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"bar"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}}, |
|||
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"bar"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}}, |
|||
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"kapow"}}, |
|||
expect: false, |
|||
}, |
|||
} { |
|||
req := &http.Request{Header: tc.input} |
|||
actual := tc.match.Match(req) |
|||
if actual != tc.expect { |
|||
t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input) |
|||
continue |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestHeaderREMatcher(t *testing.T) { |
|||
for i, tc := range []struct { |
|||
match matchHeaderRE |
|||
input http.Header // make sure these are canonical cased (std lib will do that in a real request)
|
|||
expect bool |
|||
expectRepl map[string]string |
|||
}{ |
|||
{ |
|||
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "foo"}}, |
|||
input: http.Header{"Field": []string{"foo"}}, |
|||
expect: true, |
|||
}, |
|||
{ |
|||
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "$foo^"}}, |
|||
input: http.Header{"Field": []string{"foobar"}}, |
|||
expect: false, |
|||
}, |
|||
{ |
|||
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "^foo(.*)$", Name: "name"}}, |
|||
input: http.Header{"Field": []string{"foobar"}}, |
|||
expect: true, |
|||
expectRepl: map[string]string{"name.1": "bar"}, |
|||
}, |
|||
} { |
|||
// compile the regexp and validate its name
|
|||
err := tc.match.Provision() |
|||
if err != nil { |
|||
t.Errorf("Test %d %v: Provisioning: %v", i, tc.match, err) |
|||
continue |
|||
} |
|||
err = tc.match.Validate() |
|||
if err != nil { |
|||
t.Errorf("Test %d %v: Validating: %v", i, tc.match, err) |
|||
continue |
|||
} |
|||
|
|||
// set up the fake request and its Replacer
|
|||
req := &http.Request{Header: tc.input, URL: new(url.URL)} |
|||
repl := &Replacer{req: req, resp: httptest.NewRecorder(), custom: make(map[string]string)} |
|||
ctx := context.WithValue(req.Context(), ReplacerCtxKey, repl) |
|||
req = req.WithContext(ctx) |
|||
|
|||
actual := tc.match.Match(req) |
|||
if actual != tc.expect { |
|||
t.Errorf("Test %d [%v]: Expected %t, got %t for input '%s'", |
|||
i, tc.match, tc.expect, actual, tc.input) |
|||
continue |
|||
} |
|||
|
|||
for key, expectVal := range tc.expectRepl { |
|||
placeholder := fmt.Sprintf("{http.matchers.header_regexp.%s}", key) |
|||
actualVal := repl.Replace(placeholder, "<empty>") |
|||
if actualVal != expectVal { |
|||
t.Errorf("Test %d [%v]: Expected placeholder {http.matchers.header_regexp.%s} to be '%s' but got '%s'", |
|||
i, tc.match, key, expectVal, actualVal) |
|||
continue |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue