mirror of
https://github.com/caddyserver/caddy.git
synced 2025-05-24 02:02:26 -04:00
Merge pull request #1796 from mholt/bugfix_rewrite_1794
Fix for #1794: Fixes issues with IfMatcher and regular expressions.
This commit is contained in:
commit
20fbc7303c
@ -53,59 +53,8 @@ const (
|
|||||||
matchOp = "match"
|
matchOp = "match"
|
||||||
)
|
)
|
||||||
|
|
||||||
func operatorError(operator string) error {
|
|
||||||
return fmt.Errorf("Invalid operator %v", operator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ifCondition is a 'if' condition.
|
// ifCondition is a 'if' condition.
|
||||||
type ifCondition func(string, string) bool
|
type ifFunc func(a, b string) bool
|
||||||
|
|
||||||
var ifConditions = map[string]ifCondition{
|
|
||||||
isOp: isFunc,
|
|
||||||
notOp: notFunc,
|
|
||||||
hasOp: hasFunc,
|
|
||||||
startsWithOp: startsWithFunc,
|
|
||||||
endsWithOp: endsWithFunc,
|
|
||||||
matchOp: matchFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
// isFunc is condition for Is operator.
|
|
||||||
// It checks for equality.
|
|
||||||
func isFunc(a, b string) bool {
|
|
||||||
return a == b
|
|
||||||
}
|
|
||||||
|
|
||||||
// notFunc is condition for Not operator.
|
|
||||||
// It checks for inequality.
|
|
||||||
func notFunc(a, b string) bool {
|
|
||||||
return !isFunc(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasFunc is condition for Has operator.
|
|
||||||
// It checks if b is a substring of a.
|
|
||||||
func hasFunc(a, b string) bool {
|
|
||||||
return strings.Contains(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// startsWithFunc is condition for StartsWith operator.
|
|
||||||
// It checks if b is a prefix of a.
|
|
||||||
func startsWithFunc(a, b string) bool {
|
|
||||||
return strings.HasPrefix(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endsWithFunc is condition for EndsWith operator.
|
|
||||||
// It checks if b is a suffix of a.
|
|
||||||
func endsWithFunc(a, b string) bool {
|
|
||||||
return strings.HasSuffix(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchFunc is condition for Match operator.
|
|
||||||
// It does regexp matching of a against pattern in b
|
|
||||||
// and returns if they match.
|
|
||||||
func matchFunc(a, b string) bool {
|
|
||||||
matched, _ := regexp.MatchString(b, a)
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
|
|
||||||
// ifCond is statement for a IfMatcher condition.
|
// ifCond is statement for a IfMatcher condition.
|
||||||
type ifCond struct {
|
type ifCond struct {
|
||||||
@ -113,40 +62,79 @@ type ifCond struct {
|
|||||||
op string
|
op string
|
||||||
b string
|
b string
|
||||||
neg bool
|
neg bool
|
||||||
|
rex *regexp.Regexp
|
||||||
|
f ifFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIfCond creates a new If condition.
|
// newIfCond creates a new If condition.
|
||||||
func newIfCond(a, operator, b string) (ifCond, error) {
|
func newIfCond(a, op, b string) (ifCond, error) {
|
||||||
neg := false
|
i := ifCond{a: a, op: op, b: b}
|
||||||
if strings.HasPrefix(operator, "not_") {
|
if strings.HasPrefix(op, "not_") {
|
||||||
neg = true
|
i.neg = true
|
||||||
operator = operator[4:]
|
i.op = op[4:]
|
||||||
}
|
}
|
||||||
if _, ok := ifConditions[operator]; !ok {
|
|
||||||
return ifCond{}, operatorError(operator)
|
switch i.op {
|
||||||
|
case isOp:
|
||||||
|
// It checks for equality.
|
||||||
|
i.f = i.isFunc
|
||||||
|
case notOp:
|
||||||
|
// It checks for inequality.
|
||||||
|
i.f = i.notFunc
|
||||||
|
case hasOp:
|
||||||
|
// It checks if b is a substring of a.
|
||||||
|
i.f = strings.Contains
|
||||||
|
case startsWithOp:
|
||||||
|
// It checks if b is a prefix of a.
|
||||||
|
i.f = strings.HasPrefix
|
||||||
|
case endsWithOp:
|
||||||
|
// It checks if b is a suffix of a.
|
||||||
|
i.f = strings.HasSuffix
|
||||||
|
case matchOp:
|
||||||
|
// It does regexp matching of a against pattern in b and returns if they match.
|
||||||
|
var err error
|
||||||
|
if i.rex, err = regexp.Compile(i.b); err != nil {
|
||||||
|
return ifCond{}, fmt.Errorf("Invalid regular expression: '%s', %v", i.b, err)
|
||||||
|
}
|
||||||
|
i.f = i.matchFunc
|
||||||
|
default:
|
||||||
|
return ifCond{}, fmt.Errorf("Invalid operator %v", i.op)
|
||||||
}
|
}
|
||||||
return ifCond{
|
|
||||||
a: a,
|
return i, nil
|
||||||
op: operator,
|
}
|
||||||
b: b,
|
|
||||||
neg: neg,
|
// isFunc is condition for Is operator.
|
||||||
}, nil
|
func (i ifCond) isFunc(a, b string) bool {
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
// notFunc is condition for Not operator.
|
||||||
|
func (i ifCond) notFunc(a, b string) bool {
|
||||||
|
return a != b
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchFunc is condition for Match operator.
|
||||||
|
func (i ifCond) matchFunc(a, b string) bool {
|
||||||
|
return i.rex.MatchString(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// True returns true if the condition is true and false otherwise.
|
// True returns true if the condition is true and false otherwise.
|
||||||
// If r is not nil, it replaces placeholders before comparison.
|
// If r is not nil, it replaces placeholders before comparison.
|
||||||
func (i ifCond) True(r *http.Request) bool {
|
func (i ifCond) True(r *http.Request) bool {
|
||||||
if c, ok := ifConditions[i.op]; ok {
|
if i.f != nil {
|
||||||
a, b := i.a, i.b
|
a, b := i.a, i.b
|
||||||
if r != nil {
|
if r != nil {
|
||||||
replacer := NewReplacer(r, nil, "")
|
replacer := NewReplacer(r, nil, "")
|
||||||
a = replacer.Replace(i.a)
|
a = replacer.Replace(i.a)
|
||||||
b = replacer.Replace(i.b)
|
if i.op != matchOp {
|
||||||
|
b = replacer.Replace(i.b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if i.neg {
|
if i.neg {
|
||||||
return !c(a, b)
|
return !i.f(a, b)
|
||||||
}
|
}
|
||||||
return c(a, b)
|
return i.f(a, b)
|
||||||
}
|
}
|
||||||
return i.neg // false if not negated, true otherwise
|
return i.neg // false if not negated, true otherwise
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package httpserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -14,66 +14,72 @@ func TestConditions(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
condition string
|
condition string
|
||||||
isTrue bool
|
isTrue bool
|
||||||
|
shouldErr bool
|
||||||
}{
|
}{
|
||||||
{"a is b", false},
|
{"a is b", false, false},
|
||||||
{"a is a", true},
|
{"a is a", true, false},
|
||||||
{"a not b", true},
|
{"a not b", true, false},
|
||||||
{"a not a", false},
|
{"a not a", false, false},
|
||||||
{"a has a", true},
|
{"a has a", true, false},
|
||||||
{"a has b", false},
|
{"a has b", false, false},
|
||||||
{"ba has b", true},
|
{"ba has b", true, false},
|
||||||
{"bab has b", true},
|
{"bab has b", true, false},
|
||||||
{"bab has bb", false},
|
{"bab has bb", false, false},
|
||||||
{"a not_has a", false},
|
{"a not_has a", false, false},
|
||||||
{"a not_has b", true},
|
{"a not_has b", true, false},
|
||||||
{"ba not_has b", false},
|
{"ba not_has b", false, false},
|
||||||
{"bab not_has b", false},
|
{"bab not_has b", false, false},
|
||||||
{"bab not_has bb", true},
|
{"bab not_has bb", true, false},
|
||||||
{"bab starts_with bb", false},
|
{"bab starts_with bb", false, false},
|
||||||
{"bab starts_with ba", true},
|
{"bab starts_with ba", true, false},
|
||||||
{"bab starts_with bab", true},
|
{"bab starts_with bab", true, false},
|
||||||
{"bab not_starts_with bb", true},
|
{"bab not_starts_with bb", true, false},
|
||||||
{"bab not_starts_with ba", false},
|
{"bab not_starts_with ba", false, false},
|
||||||
{"bab not_starts_with bab", false},
|
{"bab not_starts_with bab", false, false},
|
||||||
{"bab ends_with bb", false},
|
{"bab ends_with bb", false, false},
|
||||||
{"bab ends_with bab", true},
|
{"bab ends_with bab", true, false},
|
||||||
{"bab ends_with ab", true},
|
{"bab ends_with ab", true, false},
|
||||||
{"bab not_ends_with bb", true},
|
{"bab not_ends_with bb", true, false},
|
||||||
{"bab not_ends_with ab", false},
|
{"bab not_ends_with ab", false, false},
|
||||||
{"bab not_ends_with bab", false},
|
{"bab not_ends_with bab", false, false},
|
||||||
{"a match *", false},
|
{"a match *", false, true},
|
||||||
{"a match a", true},
|
{"a match a", true, false},
|
||||||
{"a match .*", true},
|
{"a match .*", true, false},
|
||||||
{"a match a.*", true},
|
{"a match a.*", true, false},
|
||||||
{"a match b.*", false},
|
{"a match b.*", false, false},
|
||||||
{"ba match b.*", true},
|
{"ba match b.*", true, false},
|
||||||
{"ba match b[a-z]", true},
|
{"ba match b[a-z]", true, false},
|
||||||
{"b0 match b[a-z]", false},
|
{"b0 match b[a-z]", false, false},
|
||||||
{"b0a match b[a-z]", false},
|
{"b0a match b[a-z]", false, false},
|
||||||
{"b0a match b[a-z]+", false},
|
{"b0a match b[a-z]+", false, false},
|
||||||
{"b0a match b[a-z0-9]+", true},
|
{"b0a match b[a-z0-9]+", true, false},
|
||||||
{"a not_match *", true},
|
{"bac match b[a-z]{2}", true, false},
|
||||||
{"a not_match a", false},
|
{"a not_match *", false, true},
|
||||||
{"a not_match .*", false},
|
{"a not_match a", false, false},
|
||||||
{"a not_match a.*", false},
|
{"a not_match .*", false, false},
|
||||||
{"a not_match b.*", true},
|
{"a not_match a.*", false, false},
|
||||||
{"ba not_match b.*", false},
|
{"a not_match b.*", true, false},
|
||||||
{"ba not_match b[a-z]", false},
|
{"ba not_match b.*", false, false},
|
||||||
{"b0 not_match b[a-z]", true},
|
{"ba not_match b[a-z]", false, false},
|
||||||
{"b0a not_match b[a-z]", true},
|
{"b0 not_match b[a-z]", true, false},
|
||||||
{"b0a not_match b[a-z]+", true},
|
{"b0a not_match b[a-z]", true, false},
|
||||||
{"b0a not_match b[a-z0-9]+", false},
|
{"b0a not_match b[a-z]+", true, false},
|
||||||
|
{"b0a not_match b[a-z0-9]+", false, false},
|
||||||
|
{"bac not_match b[a-z]{2}", false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
str := strings.Fields(test.condition)
|
str := strings.Fields(test.condition)
|
||||||
ifCond, err := newIfCond(str[0], str[1], str[2])
|
ifCond, err := newIfCond(str[0], str[1], str[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
if !test.shouldErr {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
isTrue := ifCond.True(nil)
|
isTrue := ifCond.True(nil)
|
||||||
if isTrue != test.isTrue {
|
if isTrue != test.isTrue {
|
||||||
t.Errorf("Test %d: expected %v found %v", i, test.isTrue, isTrue)
|
t.Errorf("Test %d: '%s' expected %v found %v", i, test.condition, test.isTrue, isTrue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +198,7 @@ func TestIfMatcher(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSetupIfMatcher(t *testing.T) {
|
func TestSetupIfMatcher(t *testing.T) {
|
||||||
|
rex_b, _ := regexp.Compile("b")
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
shouldErr bool
|
shouldErr bool
|
||||||
@ -201,7 +208,7 @@ func TestSetupIfMatcher(t *testing.T) {
|
|||||||
if a match b
|
if a match b
|
||||||
}`, false, IfMatcher{
|
}`, false, IfMatcher{
|
||||||
ifs: []ifCond{
|
ifs: []ifCond{
|
||||||
{a: "a", op: "match", b: "b", neg: false},
|
{a: "a", op: "match", b: "b", neg: false, rex: rex_b},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
{`test {
|
{`test {
|
||||||
@ -209,7 +216,7 @@ func TestSetupIfMatcher(t *testing.T) {
|
|||||||
if_op or
|
if_op or
|
||||||
}`, false, IfMatcher{
|
}`, false, IfMatcher{
|
||||||
ifs: []ifCond{
|
ifs: []ifCond{
|
||||||
{a: "a", op: "match", b: "b", neg: false},
|
{a: "a", op: "match", b: "b", neg: false, rex: rex_b},
|
||||||
},
|
},
|
||||||
isOr: true,
|
isOr: true,
|
||||||
}},
|
}},
|
||||||
@ -255,6 +262,7 @@ func TestSetupIfMatcher(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
c := caddy.NewTestController("http", test.input)
|
c := caddy.NewTestController("http", test.input)
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
||||||
matcher, err := SetupIfMatcher(c)
|
matcher, err := SetupIfMatcher(c)
|
||||||
if err == nil && test.shouldErr {
|
if err == nil && test.shouldErr {
|
||||||
t.Errorf("Test %d didn't error, but it should have", i)
|
t.Errorf("Test %d didn't error, but it should have", i)
|
||||||
@ -263,15 +271,60 @@ func TestSetupIfMatcher(t *testing.T) {
|
|||||||
} else if err != nil && test.shouldErr {
|
} else if err != nil && test.shouldErr {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := matcher.(IfMatcher); !ok {
|
|
||||||
|
test_if, ok := matcher.(IfMatcher)
|
||||||
|
if !ok {
|
||||||
t.Error("RequestMatcher should be of type IfMatcher")
|
t.Error("RequestMatcher should be of type IfMatcher")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, but got: %v", err)
|
t.Errorf("Expected no error, but got: %v", err)
|
||||||
}
|
}
|
||||||
if fmt.Sprint(matcher) != fmt.Sprint(test.expected) {
|
|
||||||
t.Errorf("Test %v: Expected %v, found %v", i,
|
if len(test_if.ifs) != len(test.expected.ifs) {
|
||||||
fmt.Sprint(test.expected), fmt.Sprint(matcher))
|
t.Errorf("Test %d: Expected %d ifConditions, found %v", i,
|
||||||
|
len(test.expected.ifs), len(test_if.ifs))
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, if_c := range test_if.ifs {
|
||||||
|
expected_c := test.expected.ifs[j]
|
||||||
|
|
||||||
|
if if_c.a != expected_c.a {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected A=%s, got %s",
|
||||||
|
i, j, if_c.a, expected_c.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
if if_c.op != expected_c.op {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected Op=%s, got %s",
|
||||||
|
i, j, if_c.op, expected_c.op)
|
||||||
|
}
|
||||||
|
|
||||||
|
if if_c.b != expected_c.b {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected B=%s, got %s",
|
||||||
|
i, j, if_c.b, expected_c.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if if_c.neg != expected_c.neg {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected Neg=%v, got %v",
|
||||||
|
i, j, if_c.neg, expected_c.neg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected_c.rex != nil && if_c.rex == nil {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected Rex=%v, got <nil>",
|
||||||
|
i, j, expected_c.rex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected_c.rex == nil && if_c.rex != nil {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected Rex=<nil>, got %v",
|
||||||
|
i, j, if_c.rex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected_c.rex != nil && if_c.rex != nil {
|
||||||
|
if if_c.rex.String() != expected_c.rex.String() {
|
||||||
|
t.Errorf("Test %d, ifCond %d: Expected Rex=%v, got %v",
|
||||||
|
i, j, if_c.rex, expected_c.rex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,19 +84,19 @@ type ComplexRule struct {
|
|||||||
// Request matcher
|
// Request matcher
|
||||||
httpserver.RequestMatcher
|
httpserver.RequestMatcher
|
||||||
|
|
||||||
*regexp.Regexp
|
Regexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewComplexRule creates a new RegexpRule. It returns an error if regexp
|
// NewComplexRule creates a new RegexpRule. It returns an error if regexp
|
||||||
// pattern (pattern) or extensions (ext) are invalid.
|
// pattern (pattern) or extensions (ext) are invalid.
|
||||||
func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.RequestMatcher) (*ComplexRule, error) {
|
func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.RequestMatcher) (ComplexRule, error) {
|
||||||
// validate regexp if present
|
// validate regexp if present
|
||||||
var r *regexp.Regexp
|
var r *regexp.Regexp
|
||||||
if pattern != "" {
|
if pattern != "" {
|
||||||
var err error
|
var err error
|
||||||
r, err = regexp.Compile(pattern)
|
r, err = regexp.Compile(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ComplexRule{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.R
|
|||||||
if len(v) < 2 || (len(v) < 3 && v[0] == '!') {
|
if len(v) < 2 || (len(v) < 3 && v[0] == '!') {
|
||||||
// check if no extension is specified
|
// check if no extension is specified
|
||||||
if v != "/" && v != "!/" {
|
if v != "/" && v != "!/" {
|
||||||
return nil, fmt.Errorf("invalid extension %v", v)
|
return ComplexRule{}, fmt.Errorf("invalid extension %v", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.R
|
|||||||
httpserver.PathMatcher(base),
|
httpserver.PathMatcher(base),
|
||||||
)
|
)
|
||||||
|
|
||||||
return &ComplexRule{
|
return ComplexRule{
|
||||||
Base: base,
|
Base: base,
|
||||||
To: to,
|
To: to,
|
||||||
Exts: ext,
|
Exts: ext,
|
||||||
@ -128,13 +128,13 @@ func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BasePath satisfies httpserver.Config
|
// BasePath satisfies httpserver.Config
|
||||||
func (r *ComplexRule) BasePath() string { return r.Base }
|
func (r ComplexRule) BasePath() string { return r.Base }
|
||||||
|
|
||||||
// Match satisfies httpserver.Config.
|
// Match satisfies httpserver.Config.
|
||||||
//
|
//
|
||||||
// Though ComplexRule embeds a RequestMatcher, additional
|
// Though ComplexRule embeds a RequestMatcher, additional
|
||||||
// checks are needed which requires a custom implementation.
|
// checks are needed which requires a custom implementation.
|
||||||
func (r *ComplexRule) Match(req *http.Request) bool {
|
func (r ComplexRule) Match(req *http.Request) bool {
|
||||||
// validate RequestMatcher
|
// validate RequestMatcher
|
||||||
// includes if and path
|
// includes if and path
|
||||||
if !r.RequestMatcher.Match(req) {
|
if !r.RequestMatcher.Match(req) {
|
||||||
@ -155,7 +155,7 @@ func (r *ComplexRule) Match(req *http.Request) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite rewrites the internal location of the current request.
|
// Rewrite rewrites the internal location of the current request.
|
||||||
func (r *ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result) {
|
func (r ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result) {
|
||||||
replacer := newReplacer(req)
|
replacer := newReplacer(req)
|
||||||
|
|
||||||
// validate regexp if present
|
// validate regexp if present
|
||||||
@ -189,7 +189,7 @@ func (r *ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result)
|
|||||||
|
|
||||||
// matchExt matches rPath against registered file extensions.
|
// matchExt matches rPath against registered file extensions.
|
||||||
// Returns true if a match is found and false otherwise.
|
// Returns true if a match is found and false otherwise.
|
||||||
func (r *ComplexRule) matchExt(rPath string) bool {
|
func (r ComplexRule) matchExt(rPath string) bool {
|
||||||
f := filepath.Base(rPath)
|
f := filepath.Base(rPath)
|
||||||
ext := path.Ext(f)
|
ext := path.Ext(f)
|
||||||
if ext == "" {
|
if ext == "" {
|
||||||
@ -216,14 +216,14 @@ func (r *ComplexRule) matchExt(rPath string) bool {
|
|||||||
return !mustUse
|
return !mustUse
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ComplexRule) regexpMatches(rPath string) []string {
|
func (r ComplexRule) regexpMatches(rPath string) []string {
|
||||||
if r.Regexp != nil {
|
if r.Regexp != nil {
|
||||||
// include trailing slash in regexp if present
|
// include trailing slash in regexp if present
|
||||||
start := len(r.Base)
|
start := len(r.Base)
|
||||||
if strings.HasSuffix(r.Base, "/") {
|
if strings.HasSuffix(r.Base, "/") {
|
||||||
start--
|
start--
|
||||||
}
|
}
|
||||||
return r.FindStringSubmatch(rPath[start:])
|
return r.Regexp.FindStringSubmatch(rPath[start:])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package rewrite
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
@ -97,14 +98,14 @@ func TestRewriteParse(t *testing.T) {
|
|||||||
r .*
|
r .*
|
||||||
to /to /index.php?
|
to /to /index.php?
|
||||||
}`, false, []Rule{
|
}`, false, []Rule{
|
||||||
&ComplexRule{Base: "/", To: "/to /index.php?", Regexp: regexp.MustCompile(".*")},
|
ComplexRule{Base: "/", To: "/to /index.php?", Regexp: regexp.MustCompile(".*")},
|
||||||
}},
|
}},
|
||||||
{`rewrite {
|
{`rewrite {
|
||||||
regexp .*
|
regexp .*
|
||||||
to /to
|
to /to
|
||||||
ext / html txt
|
ext / html txt
|
||||||
}`, false, []Rule{
|
}`, false, []Rule{
|
||||||
&ComplexRule{Base: "/", To: "/to", Exts: []string{"/", "html", "txt"}, Regexp: regexp.MustCompile(".*")},
|
ComplexRule{Base: "/", To: "/to", Exts: []string{"/", "html", "txt"}, Regexp: regexp.MustCompile(".*")},
|
||||||
}},
|
}},
|
||||||
{`rewrite /path {
|
{`rewrite /path {
|
||||||
r rr
|
r rr
|
||||||
@ -115,27 +116,27 @@ func TestRewriteParse(t *testing.T) {
|
|||||||
to /to /to2
|
to /to /to2
|
||||||
}
|
}
|
||||||
`, false, []Rule{
|
`, false, []Rule{
|
||||||
&ComplexRule{Base: "/path", To: "/dest", Regexp: regexp.MustCompile("rr")},
|
ComplexRule{Base: "/path", To: "/dest", Regexp: regexp.MustCompile("rr")},
|
||||||
&ComplexRule{Base: "/", To: "/to /to2", Regexp: regexp.MustCompile("[a-z]+")},
|
ComplexRule{Base: "/", To: "/to /to2", Regexp: regexp.MustCompile("[a-z]+")},
|
||||||
}},
|
}},
|
||||||
{`rewrite {
|
{`rewrite {
|
||||||
r .*
|
r .*
|
||||||
}`, true, []Rule{
|
}`, true, []Rule{
|
||||||
&ComplexRule{},
|
ComplexRule{},
|
||||||
}},
|
}},
|
||||||
{`rewrite {
|
{`rewrite {
|
||||||
|
|
||||||
}`, true, []Rule{
|
}`, true, []Rule{
|
||||||
&ComplexRule{},
|
ComplexRule{},
|
||||||
}},
|
}},
|
||||||
{`rewrite /`, true, []Rule{
|
{`rewrite /`, true, []Rule{
|
||||||
&ComplexRule{},
|
ComplexRule{},
|
||||||
}},
|
}},
|
||||||
{`rewrite {
|
{`rewrite {
|
||||||
if {path} match /
|
if {path} match /
|
||||||
to /to
|
to /to
|
||||||
}`, false, []Rule{
|
}`, false, []Rule{
|
||||||
&ComplexRule{Base: "/", To: "/to"},
|
ComplexRule{Base: "/", To: "/to"},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,8 +157,8 @@ func TestRewriteParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for j, e := range test.expected {
|
for j, e := range test.expected {
|
||||||
actualRule := actual[j].(*ComplexRule)
|
actualRule := actual[j].(ComplexRule)
|
||||||
expectedRule := e.(*ComplexRule)
|
expectedRule := e.(ComplexRule)
|
||||||
|
|
||||||
if actualRule.Base != expectedRule.Base {
|
if actualRule.Base != expectedRule.Base {
|
||||||
t.Errorf("Test %d, rule %d: Expected Base=%s, got %s",
|
t.Errorf("Test %d, rule %d: Expected Base=%s, got %s",
|
||||||
@ -175,13 +176,15 @@ func TestRewriteParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if actualRule.Regexp != nil {
|
if actualRule.Regexp != nil {
|
||||||
if actualRule.String() != expectedRule.String() {
|
if actualRule.Regexp.String() != expectedRule.Regexp.String() {
|
||||||
t.Errorf("Test %d, rule %d: Expected Pattern=%s, got %s",
|
t.Errorf("Test %d, rule %d: Expected Pattern=%s, got %s",
|
||||||
i, j, expectedRule.String(), actualRule.String())
|
i, j, actualRule.Regexp.String(), expectedRule.Regexp.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rules_fmt := fmt.Sprintf("%v", actual); strings.HasPrefix(rules_fmt, "%!") {
|
||||||
|
t.Errorf("Test %d: Failed to string encode: %#v", i, rules_fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user