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
bb23f68a
Commit
bb23f68a
authored
Dec 30, 2015
by
Abiola Ibrahim
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #463 from abiosoft/rewrite-improvements
Rewrite improvements
parents
6a27968f
92bd9144
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
434 additions
and
77 deletions
+434
-77
caddy/setup/rewrite.go
caddy/setup/rewrite.go
+26
-6
caddy/setup/rewrite_test.go
caddy/setup/rewrite_test.go
+28
-20
middleware/rewrite/condition.go
middleware/rewrite/condition.go
+111
-0
middleware/rewrite/condition_test.go
middleware/rewrite/condition_test.go
+90
-0
middleware/rewrite/rewrite.go
middleware/rewrite/rewrite.go
+43
-46
middleware/rewrite/rewrite_test.go
middleware/rewrite/rewrite_test.go
+5
-5
middleware/rewrite/testdata/testdir/empty
middleware/rewrite/testdata/testdir/empty
+0
-0
middleware/rewrite/testdata/testfile
middleware/rewrite/testdata/testfile
+1
-0
middleware/rewrite/to.go
middleware/rewrite/to.go
+86
-0
middleware/rewrite/to_test.go
middleware/rewrite/to_test.go
+44
-0
No files found.
caddy/setup/rewrite.go
View file @
bb23f68a
package
setup
import
(
"net/http"
"strings"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/rewrite"
)
...
...
@@ -13,7 +16,11 @@ func Rewrite(c *Controller) (middleware.Middleware, error) {
}
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
return
rewrite
.
Rewrite
{
Next
:
next
,
Rules
:
rewrites
}
return
rewrite
.
Rewrite
{
Next
:
next
,
FileSys
:
http
.
Dir
(
c
.
Root
),
Rules
:
rewrites
,
}
},
nil
}
...
...
@@ -30,6 +37,8 @@ func rewriteParse(c *Controller) ([]rewrite.Rule, error) {
args
:=
c
.
RemainingArgs
()
var
ifs
[]
rewrite
.
If
switch
len
(
args
)
{
case
2
:
rule
=
rewrite
.
NewSimpleRule
(
args
[
0
],
args
[
1
])
...
...
@@ -46,25 +55,36 @@ func rewriteParse(c *Controller) ([]rewrite.Rule, error) {
}
pattern
=
c
.
Val
()
case
"to"
:
if
!
c
.
NextArg
()
{
args1
:=
c
.
RemainingArgs
()
if
len
(
args1
)
==
0
{
return
nil
,
c
.
ArgErr
()
}
to
=
c
.
Val
(
)
to
=
strings
.
Join
(
args1
,
" "
)
case
"ext"
:
args1
:=
c
.
RemainingArgs
()
if
len
(
args1
)
==
0
{
return
nil
,
c
.
ArgErr
()
}
ext
=
args1
case
"if"
:
args1
:=
c
.
RemainingArgs
()
if
len
(
args1
)
!=
3
{
return
nil
,
c
.
ArgErr
()
}
ifCond
,
err
:=
rewrite
.
NewIf
(
args1
[
0
],
args1
[
1
],
args1
[
2
])
if
err
!=
nil
{
return
nil
,
err
}
ifs
=
append
(
ifs
,
ifCond
)
default
:
return
nil
,
c
.
ArgErr
()
}
}
// ensure
pattern and to are
specified
if
pattern
==
""
||
to
==
""
{
// ensure
to is
specified
if
to
==
""
{
return
nil
,
c
.
ArgErr
()
}
if
rule
,
err
=
rewrite
.
New
RegexpRule
(
base
,
pattern
,
to
,
ext
);
err
!=
nil
{
if
rule
,
err
=
rewrite
.
New
ComplexRule
(
base
,
pattern
,
to
,
ext
,
ifs
);
err
!=
nil
{
return
nil
,
err
}
regexpRules
=
append
(
regexpRules
,
rule
)
...
...
caddy/setup/rewrite_test.go
View file @
bb23f68a
package
setup
import
(
"testing"
"fmt"
"regexp"
"testing"
"github.com/mholt/caddy/middleware/rewrite"
)
...
...
@@ -96,16 +95,16 @@ func TestRewriteParse(t *testing.T) {
}{
{
`rewrite {
r .*
to /to
to /to
/index.php?
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
RegexpRule
{
Base
:
"/"
,
To
:
"/to
"
,
Regexp
:
regexp
.
MustCompile
(
".*"
)},
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
To
:
"/to /index.php?
"
,
Regexp
:
regexp
.
MustCompile
(
".*"
)},
}},
{
`rewrite {
regexp .*
to /to
ext / html txt
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
Regexp
Rule
{
Base
:
"/"
,
To
:
"/to"
,
Exts
:
[]
string
{
"/"
,
"html"
,
"txt"
},
Regexp
:
regexp
.
MustCompile
(
".*"
)},
&
rewrite
.
Complex
Rule
{
Base
:
"/"
,
To
:
"/to"
,
Exts
:
[]
string
{
"/"
,
"html"
,
"txt"
},
Regexp
:
regexp
.
MustCompile
(
".*"
)},
}},
{
`rewrite /path {
r rr
...
...
@@ -113,29 +112,30 @@ func TestRewriteParse(t *testing.T) {
}
rewrite / {
regexp [a-z]+
to /to
to /to
/to2
}
`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
RegexpRule
{
Base
:
"/path"
,
To
:
"/dest"
,
Regexp
:
regexp
.
MustCompile
(
"rr"
)},
&
rewrite
.
RegexpRule
{
Base
:
"/"
,
To
:
"/to"
,
Regexp
:
regexp
.
MustCompile
(
"[a-z]+"
)},
}},
{
`rewrite {
to /to
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
RegexpRule
{},
&
rewrite
.
ComplexRule
{
Base
:
"/path"
,
To
:
"/dest"
,
Regexp
:
regexp
.
MustCompile
(
"rr"
)},
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
To
:
"/to /to2"
,
Regexp
:
regexp
.
MustCompile
(
"[a-z]+"
)},
}},
{
`rewrite {
r .*
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
Regexp
Rule
{},
&
rewrite
.
Complex
Rule
{},
}},
{
`rewrite {
}`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
Regexp
Rule
{},
&
rewrite
.
Complex
Rule
{},
}},
{
`rewrite /`
,
true
,
[]
rewrite
.
Rule
{
&
rewrite
.
RegexpRule
{},
&
rewrite
.
ComplexRule
{},
}},
{
`rewrite {
to /to
if {path} is a
}`
,
false
,
[]
rewrite
.
Rule
{
&
rewrite
.
ComplexRule
{
Base
:
"/"
,
To
:
"/to"
,
Ifs
:
[]
rewrite
.
If
{
rewrite
.
If
{
A
:
"{path}"
,
Operator
:
"is"
,
B
:
"a"
}}},
}},
}
...
...
@@ -157,8 +157,8 @@ func TestRewriteParse(t *testing.T) {
}
for
j
,
e
:=
range
test
.
expected
{
actualRule
:=
actual
[
j
]
.
(
*
rewrite
.
Regexp
Rule
)
expectedRule
:=
e
.
(
*
rewrite
.
Regexp
Rule
)
actualRule
:=
actual
[
j
]
.
(
*
rewrite
.
Complex
Rule
)
expectedRule
:=
e
.
(
*
rewrite
.
Complex
Rule
)
if
actualRule
.
Base
!=
expectedRule
.
Base
{
t
.
Errorf
(
"Test %d, rule %d: Expected Base=%s, got %s"
,
...
...
@@ -175,11 +175,19 @@ func TestRewriteParse(t *testing.T) {
i
,
j
,
expectedRule
.
To
,
actualRule
.
To
)
}
if
actualRule
.
Regexp
!=
nil
{
if
actualRule
.
String
()
!=
expectedRule
.
String
()
{
t
.
Errorf
(
"Test %d, rule %d: Expected Pattern=%s, got %s"
,
i
,
j
,
expectedRule
.
String
(),
actualRule
.
String
())
}
}
if
fmt
.
Sprint
(
actualRule
.
Ifs
)
!=
fmt
.
Sprint
(
expectedRule
.
Ifs
)
{
t
.
Errorf
(
"Test %d, rule %d: Expected Pattern=%s, got %s"
,
i
,
j
,
fmt
.
Sprint
(
expectedRule
.
Ifs
),
fmt
.
Sprint
(
actualRule
.
Ifs
))
}
}
}
}
middleware/rewrite/condition.go
0 → 100644
View file @
bb23f68a
package
rewrite
import
(
"fmt"
"net/http"
"regexp"
"strings"
"github.com/mholt/caddy/middleware"
)
const
(
// Operators
Is
=
"is"
Not
=
"not"
Has
=
"has"
StartsWith
=
"starts_with"
EndsWith
=
"ends_with"
Match
=
"match"
)
func
operatorError
(
operator
string
)
error
{
return
fmt
.
Errorf
(
"Invalid operator %v"
,
operator
)
}
func
newReplacer
(
r
*
http
.
Request
)
middleware
.
Replacer
{
return
middleware
.
NewReplacer
(
r
,
nil
,
""
)
}
// condition is a rewrite condition.
type
condition
func
(
string
,
string
)
bool
var
conditions
=
map
[
string
]
condition
{
Is
:
isFunc
,
Not
:
notFunc
,
Has
:
hasFunc
,
StartsWith
:
startsWithFunc
,
EndsWith
:
endsWithFunc
,
Match
:
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
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
func
matchFunc
(
a
,
b
string
)
bool
{
matched
,
_
:=
regexp
.
MatchString
(
b
,
a
)
return
matched
}
// If is statement for a rewrite condition.
type
If
struct
{
A
string
Operator
string
B
string
}
// True returns true if the condition is true and false otherwise.
// If r is not nil, it replaces placeholders before comparison.
func
(
i
If
)
True
(
r
*
http
.
Request
)
bool
{
if
c
,
ok
:=
conditions
[
i
.
Operator
];
ok
{
a
,
b
:=
i
.
A
,
i
.
B
if
r
!=
nil
{
replacer
:=
newReplacer
(
r
)
a
=
replacer
.
Replace
(
i
.
A
)
b
=
replacer
.
Replace
(
i
.
B
)
}
return
c
(
a
,
b
)
}
return
false
}
// NewIf creates a new If condition.
func
NewIf
(
a
,
operator
,
b
string
)
(
If
,
error
)
{
if
_
,
ok
:=
conditions
[
operator
];
!
ok
{
return
If
{},
operatorError
(
operator
)
}
return
If
{
A
:
a
,
Operator
:
operator
,
B
:
b
,
},
nil
}
middleware/rewrite/condition_test.go
0 → 100644
View file @
bb23f68a
package
rewrite
import
(
"net/http"
"strings"
"testing"
)
func
TestConditions
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
condition
string
isTrue
bool
}{
{
"a is b"
,
false
},
{
"a is a"
,
true
},
{
"a not b"
,
true
},
{
"a not a"
,
false
},
{
"a has a"
,
true
},
{
"a has b"
,
false
},
{
"ba has b"
,
true
},
{
"bab has b"
,
true
},
{
"bab has bb"
,
false
},
{
"bab starts_with bb"
,
false
},
{
"bab starts_with ba"
,
true
},
{
"bab starts_with bab"
,
true
},
{
"bab ends_with bb"
,
false
},
{
"bab ends_with bab"
,
true
},
{
"bab ends_with ab"
,
true
},
{
"a match *"
,
false
},
{
"a match a"
,
true
},
{
"a match .*"
,
true
},
{
"a match a.*"
,
true
},
{
"a match b.*"
,
false
},
{
"ba match b.*"
,
true
},
{
"ba match b[a-z]"
,
true
},
{
"b0 match b[a-z]"
,
false
},
{
"b0a match b[a-z]"
,
false
},
{
"b0a match b[a-z]+"
,
false
},
{
"b0a match b[a-z0-9]+"
,
true
},
}
for
i
,
test
:=
range
tests
{
str
:=
strings
.
Fields
(
test
.
condition
)
ifCond
,
err
:=
NewIf
(
str
[
0
],
str
[
1
],
str
[
2
])
if
err
!=
nil
{
t
.
Error
(
err
)
}
isTrue
:=
ifCond
.
True
(
nil
)
if
isTrue
!=
test
.
isTrue
{
t
.
Errorf
(
"Test %v: expected %v found %v"
,
i
,
test
.
isTrue
,
isTrue
)
}
}
invalidOperators
:=
[]
string
{
"ss"
,
"and"
,
"if"
}
for
_
,
op
:=
range
invalidOperators
{
_
,
err
:=
NewIf
(
"a"
,
op
,
"b"
)
if
err
==
nil
{
t
.
Errorf
(
"Invalid operator %v used, expected error."
,
op
)
}
}
replaceTests
:=
[]
struct
{
url
string
condition
string
isTrue
bool
}{
{
"/home"
,
"{uri} match /home"
,
true
},
{
"/hom"
,
"{uri} match /home"
,
false
},
{
"/hom"
,
"{uri} starts_with /home"
,
false
},
{
"/hom"
,
"{uri} starts_with /h"
,
true
},
{
"/home/.hiddenfile"
,
`{uri} match \/\.(.*)`
,
true
},
{
"/home/.hiddendir/afile"
,
`{uri} match \/\.(.*)`
,
true
},
}
for
i
,
test
:=
range
replaceTests
{
r
,
err
:=
http
.
NewRequest
(
"GET"
,
test
.
url
,
nil
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
str
:=
strings
.
Fields
(
test
.
condition
)
ifCond
,
err
:=
NewIf
(
str
[
0
],
str
[
1
],
str
[
2
])
if
err
!=
nil
{
t
.
Error
(
err
)
}
isTrue
:=
ifCond
.
True
(
r
)
if
isTrue
!=
test
.
isTrue
{
t
.
Errorf
(
"Test %v: expected %v found %v"
,
i
,
test
.
isTrue
,
isTrue
)
}
}
}
middleware/rewrite/rewrite.go
View file @
bb23f68a
...
...
@@ -5,7 +5,6 @@ package rewrite
import
(
"fmt"
"net/http"
"net/url"
"path"
"path/filepath"
"regexp"
...
...
@@ -17,13 +16,14 @@ import (
// Rewrite is middleware to rewrite request locations internally before being handled.
type
Rewrite
struct
{
Next
middleware
.
Handler
FileSys
http
.
FileSystem
Rules
[]
Rule
}
// ServeHTTP implements the middleware.Handler interface.
func
(
rw
Rewrite
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
for
_
,
rule
:=
range
rw
.
Rules
{
if
ok
:=
rule
.
Rewrite
(
r
);
ok
{
if
ok
:=
rule
.
Rewrite
(
r
w
.
FileSys
,
r
);
ok
{
break
}
}
...
...
@@ -33,7 +33,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
.
Request
)
bool
Rewrite
(
http
.
FileSystem
,
*
http
.
Request
)
bool
}
// SimpleRule is a simple rewrite rule.
...
...
@@ -47,23 +47,20 @@ func NewSimpleRule(from, to string) SimpleRule {
}
// Rewrite rewrites the internal location of the current request.
func
(
s
SimpleRule
)
Rewrite
(
r
*
http
.
Request
)
bool
{
func
(
s
SimpleRule
)
Rewrite
(
fs
http
.
FileSystem
,
r
*
http
.
Request
)
bool
{
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
r
.
Header
.
Set
(
headerFieldName
,
r
.
URL
.
RequestURI
())
// replace variables
to
:=
path
.
Clean
(
middleware
.
NewReplacer
(
r
,
nil
,
""
)
.
Replace
(
s
.
To
))
r
.
URL
.
Path
=
to
return
true
// attempt rewrite
return
To
(
fs
,
r
,
s
.
To
)
}
return
false
}
//
Regexp
Rule is a rewrite rule based on a regular expression
type
Regexp
Rule
struct
{
//
Complex
Rule is a rewrite rule based on a regular expression
type
Complex
Rule
struct
{
// Path base. Request to this path and subpaths will be rewritten
Base
string
...
...
@@ -73,18 +70,26 @@ type RegexpRule struct {
// Extensions to filter by
Exts
[]
string
// Rewrite conditions
Ifs
[]
If
*
regexp
.
Regexp
}
// NewRegexpRule creates a new RegexpRule. It returns an error if regexp
// pattern (pattern) or extensions (ext) are invalid.
func
NewRegexpRule
(
base
,
pattern
,
to
string
,
ext
[]
string
)
(
*
RegexpRule
,
error
)
{
r
,
err
:=
regexp
.
Compile
(
pattern
)
func
NewComplexRule
(
base
,
pattern
,
to
string
,
ext
[]
string
,
ifs
[]
If
)
(
*
ComplexRule
,
error
)
{
// validate regexp if present
var
r
*
regexp
.
Regexp
if
pattern
!=
""
{
var
err
error
r
,
err
=
regexp
.
Compile
(
pattern
)
if
err
!=
nil
{
return
nil
,
err
}
}
// validate extensions
// validate extensions
if present
for
_
,
v
:=
range
ext
{
if
len
(
v
)
<
2
||
(
len
(
v
)
<
3
&&
v
[
0
]
==
'!'
)
{
// check if no extension is specified
...
...
@@ -94,16 +99,17 @@ func NewRegexpRule(base, pattern, to string, ext []string) (*RegexpRule, error)
}
}
return
&
RegexpRule
{
base
,
to
,
ext
,
r
,
return
&
ComplexRule
{
Base
:
base
,
To
:
to
,
Exts
:
ext
,
Ifs
:
ifs
,
Regexp
:
r
,
},
nil
}
// Rewrite rewrites the internal location of the current request.
func
(
r
*
RegexpRule
)
Rewrite
(
req
*
http
.
Request
)
bool
{
func
(
r
*
ComplexRule
)
Rewrite
(
fs
http
.
FileSystem
,
req
*
http
.
Request
)
bool
{
rPath
:=
req
.
URL
.
Path
// validate base
...
...
@@ -122,36 +128,27 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
start
--
}
// validate regexp
// validate regexp if present
if
r
.
Regexp
!=
nil
{
if
!
r
.
MatchString
(
rPath
[
start
:
])
{
return
false
}
}
// replace variables
to
:=
path
.
Clean
(
middleware
.
NewReplacer
(
req
,
nil
,
""
)
.
Replace
(
r
.
To
))
// validate resulting path
url
,
err
:=
url
.
Parse
(
to
)
if
err
!=
nil
{
// validate rewrite conditions
for
_
,
i
:=
range
r
.
Ifs
{
if
!
i
.
True
(
req
)
{
return
false
}
// take note of this rewrite for internal use by fastcgi
// all we need is the URI, not full URL
req
.
Header
.
Set
(
headerFieldName
,
req
.
URL
.
RequestURI
())
// perform rewrite
req
.
URL
.
Path
=
url
.
Path
if
url
.
RawQuery
!=
""
{
// overwrite query string if present
req
.
URL
.
RawQuery
=
url
.
RawQuery
}
return
true
// attempt rewrite
return
To
(
fs
,
req
,
r
.
To
)
}
// matchExt matches rPath against registered file extensions.
// Returns true if a match is found and false otherwise.
func
(
r
*
Regexp
Rule
)
matchExt
(
rPath
string
)
bool
{
func
(
r
*
Complex
Rule
)
matchExt
(
rPath
string
)
bool
{
f
:=
filepath
.
Base
(
rPath
)
ext
:=
path
.
Ext
(
f
)
if
ext
==
""
{
...
...
middleware/rewrite/rewrite_test.go
View file @
bb23f68a
...
...
@@ -4,9 +4,8 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"strings"
"testing"
"github.com/mholt/caddy/middleware"
)
...
...
@@ -19,9 +18,10 @@ func TestRewrite(t *testing.T) {
NewSimpleRule
(
"/a"
,
"/b"
),
NewSimpleRule
(
"/b"
,
"/b{uri}"
),
},
FileSys
:
http
.
Dir
(
"."
),
}
regexp
Rule
s
:=
[][]
string
{
regexps
:=
[][]
string
{
{
"/reg/"
,
".*"
,
"/to"
,
""
},
{
"/r/"
,
"[a-z]+"
,
"/toaz"
,
"!.html|"
},
{
"/url/"
,
"a([a-z0-9]*)s([A-Z]{2})"
,
"/to/{path}"
,
""
},
...
...
@@ -33,12 +33,12 @@ func TestRewrite(t *testing.T) {
{
"/ab/"
,
`.*\.jpg`
,
"/ajpg"
,
""
},
}
for
_
,
regexpRule
:=
range
regexp
Rule
s
{
for
_
,
regexpRule
:=
range
regexps
{
var
ext
[]
string
if
s
:=
strings
.
Split
(
regexpRule
[
3
],
"|"
);
len
(
s
)
>
1
{
ext
=
s
[
:
len
(
s
)
-
1
]
}
rule
,
err
:=
New
RegexpRule
(
regexpRule
[
0
],
regexpRule
[
1
],
regexpRule
[
2
],
ext
)
rule
,
err
:=
New
ComplexRule
(
regexpRule
[
0
],
regexpRule
[
1
],
regexpRule
[
2
],
ext
,
nil
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
...
...
middleware/rewrite/testdata/testdir/empty
0 → 100644
View file @
bb23f68a
middleware/rewrite/testdata/testfile
0 → 100644
View file @
bb23f68a
empty
\ No newline at end of file
middleware/rewrite/to.go
0 → 100644
View file @
bb23f68a
package
rewrite
import
(
"log"
"net/http"
"net/url"
"path"
"strings"
)
// 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
)
bool
{
tos
:=
strings
.
Fields
(
to
)
replacer
:=
newReplacer
(
r
)
// try each rewrite paths
t
:=
""
for
_
,
v
:=
range
tos
{
t
=
path
.
Clean
(
replacer
.
Replace
(
v
))
// add trailing slash for directories, if present
if
strings
.
HasSuffix
(
v
,
"/"
)
&&
!
strings
.
HasSuffix
(
t
,
"/"
)
{
t
+=
"/"
}
// validate file
if
isValidFile
(
fs
,
t
)
{
break
}
}
// validate resulting path
u
,
err
:=
url
.
Parse
(
t
)
if
err
!=
nil
{
// 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
}
// take note of this rewrite for internal use by fastcgi
// all we need is the URI, not full URL
r
.
Header
.
Set
(
headerFieldName
,
r
.
URL
.
RequestURI
())
// perform rewrite
r
.
URL
.
Path
=
u
.
Path
if
u
.
RawQuery
!=
""
{
// overwrite query string if present
r
.
URL
.
RawQuery
=
u
.
RawQuery
}
if
u
.
Fragment
!=
""
{
// overwrite fragment if present
r
.
URL
.
Fragment
=
u
.
Fragment
}
return
true
}
// isValidFile checks if file exists on the filesystem.
// if file ends with `/`, it is validated as a directory.
func
isValidFile
(
fs
http
.
FileSystem
,
file
string
)
bool
{
if
fs
==
nil
{
return
false
}
f
,
err
:=
fs
.
Open
(
file
)
if
err
!=
nil
{
return
false
}
defer
f
.
Close
()
stat
,
err
:=
f
.
Stat
()
if
err
!=
nil
{
return
false
}
// directory
if
strings
.
HasSuffix
(
file
,
"/"
)
{
return
stat
.
IsDir
()
}
// file
return
!
stat
.
IsDir
()
}
middleware/rewrite/to_test.go
0 → 100644
View file @
bb23f68a
package
rewrite
import
(
"net/http"
"net/url"
"testing"
)
func
TestTo
(
t
*
testing
.
T
)
{
fs
:=
http
.
Dir
(
"testdata"
)
tests
:=
[]
struct
{
url
string
to
string
expected
string
}{
{
"/"
,
"/somefiles"
,
"/somefiles"
},
{
"/somefiles"
,
"/somefiles /index.php{uri}"
,
"/index.php/somefiles"
},
{
"/somefiles"
,
"/testfile /index.php{uri}"
,
"/testfile"
},
{
"/somefiles"
,
"/testfile/ /index.php{uri}"
,
"/index.php/somefiles"
},
{
"/somefiles"
,
"/somefiles /index.php{uri}"
,
"/index.php/somefiles"
},
{
"/?a=b"
,
"/somefiles /index.php?{query}"
,
"/index.php?a=b"
},
{
"/?a=b"
,
"/testfile /index.php?{query}"
,
"/testfile?a=b"
},
{
"/?a=b"
,
"/testdir /index.php?{query}"
,
"/index.php?a=b"
},
{
"/?a=b"
,
"/testdir/ /index.php?{query}"
,
"/testdir/?a=b"
},
}
uri
:=
func
(
r
*
url
.
URL
)
string
{
uri
:=
r
.
Path
if
r
.
RawQuery
!=
""
{
uri
+=
"?"
+
r
.
RawQuery
}
return
uri
}
for
i
,
test
:=
range
tests
{
r
,
err
:=
http
.
NewRequest
(
"GET"
,
test
.
url
,
nil
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
To
(
fs
,
r
,
test
.
to
)
if
uri
(
r
.
URL
)
!=
test
.
expected
{
t
.
Errorf
(
"Test %v: expected %v found %v"
,
i
,
test
.
expected
,
uri
(
r
.
URL
))
}
}
}
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