Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
5f6a0a4c
Commit
5f6a0a4c
authored
Jan 06, 2016
by
Matt Holt
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #479 from abiosoft/rewrite-patch
rewrite: not_has, not_match, multiple to, and status codes
parents
cae98808
0a04fa40
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
178 additions
and
29 deletions
+178
-29
caddy/setup/rewrite.go
caddy/setup/rewrite.go
+17
-7
caddy/setup/rewrite_test.go
caddy/setup/rewrite_test.go
+29
-2
middleware/rewrite/condition.go
middleware/rewrite/condition.go
+19
-0
middleware/rewrite/condition_test.go
middleware/rewrite/condition_test.go
+16
-0
middleware/rewrite/rewrite.go
middleware/rewrite/rewrite.go
+48
-16
middleware/rewrite/rewrite_test.go
middleware/rewrite/rewrite_test.go
+46
-1
middleware/rewrite/to.go
middleware/rewrite/to.go
+3
-3
No files found.
caddy/setup/rewrite.go
View file @
5f6a0a4c
...
...
@@ -2,6 +2,7 @@ package setup
import
(
"net/http"
"strconv"
"strings"
"github.com/mholt/caddy/middleware"
...
...
@@ -33,6 +34,7 @@ func rewriteParse(c *Controller) ([]rewrite.Rule, error) {
var
err
error
var
base
=
"/"
var
pattern
,
to
string
var
status
int
var
ext
[]
string
args
:=
c
.
RemainingArgs
()
...
...
@@ -40,9 +42,6 @@ func rewriteParse(c *Controller) ([]rewrite.Rule, error) {
var
ifs
[]
rewrite
.
If
switch
len
(
args
)
{
case
2
:
rule
=
rewrite
.
NewSimpleRule
(
args
[
0
],
args
[
1
])
simpleRules
=
append
(
simpleRules
,
rule
)
case
1
:
base
=
args
[
0
]
fallthrough
...
...
@@ -76,20 +75,31 @@ func rewriteParse(c *Controller) ([]rewrite.Rule, error) {
return
nil
,
err
}
ifs
=
append
(
ifs
,
ifCond
)
case
"status"
:
if
!
c
.
NextArg
()
{
return
nil
,
c
.
ArgErr
()
}
status
,
_
=
strconv
.
Atoi
(
c
.
Val
())
if
status
<
400
||
status
>
499
{
return
nil
,
c
.
Err
(
"status must be 4xx"
)
}
default
:
return
nil
,
c
.
ArgErr
()
}
}
// ensure to is specified
if
to
==
""
{
// ensure to
or status
is specified
if
to
==
""
&&
status
==
0
{
return
nil
,
c
.
ArgErr
()
}
if
rule
,
err
=
rewrite
.
NewComplexRule
(
base
,
pattern
,
to
,
ext
,
ifs
);
err
!=
nil
{
if
rule
,
err
=
rewrite
.
NewComplexRule
(
base
,
pattern
,
to
,
status
,
ext
,
ifs
);
err
!=
nil
{
return
nil
,
err
}
regexpRules
=
append
(
regexpRules
,
rule
)
// the only unhandled case is 2 and above
default
:
return
nil
,
c
.
ArgErr
()
rule
=
rewrite
.
NewSimpleRule
(
args
[
0
],
strings
.
Join
(
args
[
1
:
],
" "
))
simpleRules
=
append
(
simpleRules
,
rule
)
}
}
...
...
caddy/setup/rewrite_test.go
View file @
5f6a0a4c
...
...
@@ -50,8 +50,8 @@ func TestRewriteParse(t *testing.T) {
}},
{
`rewrite a`
,
true
,
[]
rewrite
.
Rule
{}},
{
`rewrite`
,
true
,
[]
rewrite
.
Rule
{}},
{
`rewrite a b c`
,
tru
e
,
[]
rewrite
.
Rule
{
rewrite
.
SimpleRule
{
From
:
"a"
,
To
:
"b"
},
{
`rewrite a b c`
,
fals
e
,
[]
rewrite
.
Rule
{
rewrite
.
SimpleRule
{
From
:
"a"
,
To
:
"b
c
"
},
}},
}
...
...
@@ -137,6 +137,33 @@ func TestRewriteParse(t *testing.T) {
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
To
:
"/to"
,
Ifs
:
[]
rewrite
.
If
{
rewrite
.
If
{
A
:
"{path}"
,
Operator
:
"is"
,
B
:
"a"
}}},
}},
{
`rewrite {
status 400
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
Regexp
:
regexp
.
MustCompile
(
".*"
),
Status
:
400
},
}},
{
`rewrite {
to /to
status 400
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
To
:
"/to"
,
Regexp
:
regexp
.
MustCompile
(
".*"
),
Status
:
400
},
}},
{
`rewrite {
status 399
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{},
}},
{
`rewrite {
status 0
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{},
}},
{
`rewrite {
to /to
status 0
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{},
}},
}
for
i
,
test
:=
range
regexpTests
{
...
...
middleware/rewrite/condition.go
View file @
5f6a0a4c
...
...
@@ -14,9 +14,11 @@ const (
Is
=
"is"
Not
=
"not"
Has
=
"has"
NotHas
=
"not_has"
StartsWith
=
"starts_with"
EndsWith
=
"ends_with"
Match
=
"match"
NotMatch
=
"not_match"
)
func
operatorError
(
operator
string
)
error
{
...
...
@@ -34,9 +36,11 @@ var conditions = map[string]condition{
Is
:
isFunc
,
Not
:
notFunc
,
Has
:
hasFunc
,
NotHas
:
notHasFunc
,
StartsWith
:
startsWithFunc
,
EndsWith
:
endsWithFunc
,
Match
:
matchFunc
,
NotMatch
:
notMatchFunc
,
}
// isFunc is condition for Is operator.
...
...
@@ -57,6 +61,12 @@ func hasFunc(a, b string) bool {
return
strings
.
Contains
(
a
,
b
)
}
// notHasFunc is condition for NotHas operator.
// It checks if b is not a substring of a.
func
notHasFunc
(
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
{
...
...
@@ -71,11 +81,20 @@ func endsWithFunc(a, b string) bool {
// 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
}
// notMatchFunc is condition for NotMatch operator.
// It does regexp matching of a against pattern in b
// and returns if they do not match.
func
notMatchFunc
(
a
,
b
string
)
bool
{
matched
,
_
:=
regexp
.
MatchString
(
b
,
a
)
return
!
matched
}
// If is statement for a rewrite condition.
type
If
struct
{
A
string
...
...
middleware/rewrite/condition_test.go
View file @
5f6a0a4c
...
...
@@ -20,6 +20,11 @@ func TestConditions(t *testing.T) {
{
"ba has b"
,
true
},
{
"bab has b"
,
true
},
{
"bab has bb"
,
false
},
{
"a not_has a"
,
false
},
{
"a not_has b"
,
true
},
{
"ba not_has b"
,
false
},
{
"bab not_has b"
,
false
},
{
"bab not_has bb"
,
true
},
{
"bab starts_with bb"
,
false
},
{
"bab starts_with ba"
,
true
},
{
"bab starts_with bab"
,
true
},
...
...
@@ -37,6 +42,17 @@ func TestConditions(t *testing.T) {
{
"b0a match b[a-z]"
,
false
},
{
"b0a match b[a-z]+"
,
false
},
{
"b0a match b[a-z0-9]+"
,
true
},
{
"a not_match *"
,
true
},
{
"a not_match a"
,
false
},
{
"a not_match .*"
,
false
},
{
"a not_match a.*"
,
false
},
{
"a not_match b.*"
,
true
},
{
"ba not_match b.*"
,
false
},
{
"ba not_match b[a-z]"
,
false
},
{
"b0 not_match b[a-z]"
,
true
},
{
"b0a not_match b[a-z]"
,
true
},
{
"b0a not_match b[a-z]+"
,
true
},
{
"b0a not_match b[a-z0-9]+"
,
false
},
}
for
i
,
test
:=
range
tests
{
...
...
middleware/rewrite/rewrite.go
View file @
5f6a0a4c
...
...
@@ -13,6 +13,19 @@ import (
"github.com/mholt/caddy/middleware"
)
// RewriteResult is the result of a rewrite
type
RewriteResult
int
const
(
// RewriteIgnored is returned when rewrite is not done on request.
RewriteIgnored
RewriteResult
=
iota
// RewriteDone is returned when rewrite is done on request.
RewriteDone
// RewriteStatus is returned when rewrite is not needed and status code should be set
// for the request.
RewriteStatus
)
// Rewrite is middleware to rewrite request locations internally before being handled.
type
Rewrite
struct
{
Next
middleware
.
Handler
...
...
@@ -22,9 +35,18 @@ type Rewrite struct {
// ServeHTTP implements the middleware.Handler interface.
func
(
rw
Rewrite
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
outer
:
for
_
,
rule
:=
range
rw
.
Rules
{
if
ok
:=
rule
.
Rewrite
(
rw
.
FileSys
,
r
);
ok
{
switch
result
:=
rule
.
Rewrite
(
rw
.
FileSys
,
r
);
result
{
case
RewriteDone
:
break
outer
case
RewriteIgnored
:
break
case
RewriteStatus
:
// only valid for complex rules.
if
cRule
,
ok
:=
rule
.
(
*
ComplexRule
);
ok
&&
cRule
.
Status
!=
0
{
return
cRule
.
Status
,
nil
}
}
}
return
rw
.
Next
.
ServeHTTP
(
w
,
r
)
...
...
@@ -33,7 +55,7 @@ func (rw Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
// Rule describes an internal location rewrite rule.
type
Rule
interface
{
// Rewrite rewrites the internal location of the current request.
Rewrite
(
http
.
FileSystem
,
*
http
.
Request
)
bool
Rewrite
(
http
.
FileSystem
,
*
http
.
Request
)
RewriteResult
}
// SimpleRule is a simple rewrite rule.
...
...
@@ -47,7 +69,7 @@ func NewSimpleRule(from, to string) SimpleRule {
}
// Rewrite rewrites the internal location of the current request.
func
(
s
SimpleRule
)
Rewrite
(
fs
http
.
FileSystem
,
r
*
http
.
Request
)
bool
{
func
(
s
SimpleRule
)
Rewrite
(
fs
http
.
FileSystem
,
r
*
http
.
Request
)
RewriteResult
{
if
s
.
From
==
r
.
URL
.
Path
{
// take note of this rewrite for internal use by fastcgi
// all we need is the URI, not full URL
...
...
@@ -56,7 +78,7 @@ func (s SimpleRule) Rewrite(fs http.FileSystem, r *http.Request) bool {
// attempt rewrite
return
To
(
fs
,
r
,
s
.
To
,
newReplacer
(
r
))
}
return
false
return
RewriteIgnored
}
// ComplexRule is a rewrite rule based on a regular expression
...
...
@@ -67,6 +89,10 @@ type ComplexRule struct {
// Path to rewrite to
To
string
// If set, neither performs rewrite nor proceeds
// with request. Only returns code.
Status
int
// Extensions to filter by
Exts
[]
string
...
...
@@ -78,7 +104,7 @@ type ComplexRule struct {
// NewRegexpRule creates a new RegexpRule. It returns an error if regexp
// pattern (pattern) or extensions (ext) are invalid.
func
NewComplexRule
(
base
,
pattern
,
to
string
,
ext
[]
string
,
ifs
[]
If
)
(
*
ComplexRule
,
error
)
{
func
NewComplexRule
(
base
,
pattern
,
to
string
,
status
int
,
ext
[]
string
,
ifs
[]
If
)
(
*
ComplexRule
,
error
)
{
// validate regexp if present
var
r
*
regexp
.
Regexp
if
pattern
!=
""
{
...
...
@@ -102,6 +128,7 @@ func NewComplexRule(base, pattern, to string, ext []string, ifs []If) (*ComplexR
return
&
ComplexRule
{
Base
:
base
,
To
:
to
,
Status
:
status
,
Exts
:
ext
,
Ifs
:
ifs
,
Regexp
:
r
,
...
...
@@ -109,33 +136,33 @@ func NewComplexRule(base, pattern, to string, ext []string, ifs []If) (*ComplexR
}
// Rewrite rewrites the internal location of the current request.
func
(
r
*
ComplexRule
)
Rewrite
(
fs
http
.
FileSystem
,
req
*
http
.
Request
)
bool
{
func
(
r
*
ComplexRule
)
Rewrite
(
fs
http
.
FileSystem
,
req
*
http
.
Request
)
(
re
RewriteResult
)
{
rPath
:=
req
.
URL
.
Path
replacer
:=
newReplacer
(
req
)
// validate base
if
!
middleware
.
Path
(
rPath
)
.
Matches
(
r
.
Base
)
{
return
false
return
}
// validate extensions
if
!
r
.
matchExt
(
rPath
)
{
return
false
}
// include trailing slash in regexp if present
start
:=
len
(
r
.
Base
)
if
strings
.
HasSuffix
(
r
.
Base
,
"/"
)
{
start
--
return
}
// validate regexp if present
if
r
.
Regexp
!=
nil
{
// include trailing slash in regexp if present
start
:=
len
(
r
.
Base
)
if
strings
.
HasSuffix
(
r
.
Base
,
"/"
)
{
start
--
}
matches
:=
r
.
FindStringSubmatch
(
rPath
[
start
:
])
switch
len
(
matches
)
{
case
0
:
// no match
return
false
return
default
:
// set regexp match variables {1}, {2} ...
for
i
:=
1
;
i
<
len
(
matches
);
i
++
{
...
...
@@ -147,10 +174,15 @@ func (r *ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) bool {
// validate rewrite conditions
for
_
,
i
:=
range
r
.
Ifs
{
if
!
i
.
True
(
req
)
{
return
false
return
}
}
// if status is present, stop rewrite and return it.
if
r
.
Status
!=
0
{
return
RewriteStatus
}
// attempt rewrite
return
To
(
fs
,
req
,
r
.
To
,
replacer
)
}
...
...
middleware/rewrite/rewrite_test.go
View file @
5f6a0a4c
...
...
@@ -41,7 +41,7 @@ func TestRewrite(t *testing.T) {
if
s
:=
strings
.
Split
(
regexpRule
[
3
],
"|"
);
len
(
s
)
>
1
{
ext
=
s
[
:
len
(
s
)
-
1
]
}
rule
,
err
:=
NewComplexRule
(
regexpRule
[
0
],
regexpRule
[
1
],
regexpRule
[
2
],
ext
,
nil
)
rule
,
err
:=
NewComplexRule
(
regexpRule
[
0
],
regexpRule
[
1
],
regexpRule
[
2
],
0
,
ext
,
nil
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
...
...
@@ -106,6 +106,51 @@ func TestRewrite(t *testing.T) {
i
,
test
.
expectedTo
,
rec
.
Body
.
String
())
}
}
statusTests
:=
[]
struct
{
status
int
base
string
to
string
regexp
string
statusExpected
bool
}{
{
400
,
"/status"
,
""
,
""
,
true
},
{
400
,
"/ignore"
,
""
,
""
,
false
},
{
400
,
"/"
,
""
,
"^/ignore"
,
false
},
{
400
,
"/"
,
""
,
"(.*)"
,
true
},
{
400
,
"/status"
,
""
,
""
,
true
},
}
for
i
,
s
:=
range
statusTests
{
urlPath
:=
fmt
.
Sprintf
(
"/status%d"
,
i
)
rule
,
err
:=
NewComplexRule
(
s
.
base
,
s
.
regexp
,
s
.
to
,
s
.
status
,
nil
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"Test %d: No error expected for rule but found %v"
,
i
,
err
)
}
rw
.
Rules
=
[]
Rule
{
rule
}
req
,
err
:=
http
.
NewRequest
(
"GET"
,
urlPath
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"Test %d: Could not create HTTP request: %v"
,
i
,
err
)
}
rec
:=
httptest
.
NewRecorder
()
code
,
err
:=
rw
.
ServeHTTP
(
rec
,
req
)
if
err
!=
nil
{
t
.
Fatalf
(
"Test %d: No error expected for handler but found %v"
,
i
,
err
)
}
if
s
.
statusExpected
{
if
rec
.
Body
.
String
()
!=
""
{
t
.
Errorf
(
"Test %d: Expected empty body but found %s"
,
i
,
rec
.
Body
.
String
())
}
if
code
!=
s
.
status
{
t
.
Errorf
(
"Test %d: Expected status code %d found %d"
,
i
,
s
.
status
,
code
)
}
}
else
{
if
code
!=
0
{
t
.
Errorf
(
"Test %d: Expected no status code found %d"
,
i
,
code
)
}
}
}
}
func
urlPrinter
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
...
...
middleware/rewrite/to.go
View file @
5f6a0a4c
...
...
@@ -13,7 +13,7 @@ import (
// To attempts rewrite. It attempts to rewrite to first valid path
// or the last path if none of the paths are valid.
// Returns true if rewrite is successful and false otherwise.
func
To
(
fs
http
.
FileSystem
,
r
*
http
.
Request
,
to
string
,
replacer
middleware
.
Replacer
)
bool
{
func
To
(
fs
http
.
FileSystem
,
r
*
http
.
Request
,
to
string
,
replacer
middleware
.
Replacer
)
RewriteResult
{
tos
:=
strings
.
Fields
(
to
)
// try each rewrite paths
...
...
@@ -38,7 +38,7 @@ func To(fs http.FileSystem, r *http.Request, to string, replacer middleware.Repl
// Let the user know we got here. Rewrite is expected but
// the resulting url is invalid.
log
.
Printf
(
"[ERROR] rewrite: resulting path '%v' is invalid. error: %v"
,
t
,
err
)
return
false
return
RewriteIgnored
}
// take note of this rewrite for internal use by fastcgi
...
...
@@ -56,7 +56,7 @@ func To(fs http.FileSystem, r *http.Request, to string, replacer middleware.Repl
r
.
URL
.
Fragment
=
u
.
Fragment
}
return
tru
e
return
RewriteDon
e
}
// isValidFile checks if file exists on the filesystem.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment