caddy/modules/caddyhttp/rewrite/rewrite_test.go
Matthew Holt 07ad4655db
rewrite: Make URI modifications more transactional (#2891)
Before, modifying the path might have affected how a new query string
was built if the query string relied on the path. Now, we build each
component in isolation and only change the URI on the request later.

Also, prevent trailing & in query string.
2020-01-15 11:44:21 -07:00

270 lines
8.2 KiB
Go

// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rewrite
import (
"net/http"
"testing"
"github.com/caddyserver/caddy/v2"
)
func TestRewrite(t *testing.T) {
repl := caddy.NewReplacer()
for i, tc := range []struct {
input, expect *http.Request
rule Rewrite
}{
{
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/"),
},
{
rule: Rewrite{Method: "GET", URI: "/"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/"),
},
{
rule: Rewrite{Method: "POST"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "POST", "/"),
},
{
rule: Rewrite{URI: "/foo"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/foo"),
},
{
rule: Rewrite{URI: "/foo"},
input: newRequest(t, "GET", "/bar"),
expect: newRequest(t, "GET", "/foo"),
},
{
rule: Rewrite{URI: "foo"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "foo"),
},
{
rule: Rewrite{URI: "/foo{http.request.uri.path}"},
input: newRequest(t, "GET", "/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{URI: "/index.php?p={http.request.uri.path}"},
input: newRequest(t, "GET", "/foo/bar"),
expect: newRequest(t, "GET", "/index.php?p=%2Ffoo%2Fbar"),
},
{
rule: Rewrite{URI: "?a=b&{http.request.uri.query}"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/?a=b"),
},
{
rule: Rewrite{URI: "/?c=d"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/?c=d"),
},
{
rule: Rewrite{URI: "/?c=d"},
input: newRequest(t, "GET", "/?a=b"),
expect: newRequest(t, "GET", "/?c=d"),
},
{
rule: Rewrite{URI: "?c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/foo?c=d"),
},
{
rule: Rewrite{URI: "/?c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/?c=d"),
},
{
rule: Rewrite{URI: "/?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/?c=d"),
},
{
rule: Rewrite{URI: "/foo?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/"),
expect: newRequest(t, "GET", "/foo?c=d"),
},
{
rule: Rewrite{URI: "?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/foo?c=d"),
},
{
rule: Rewrite{URI: "{http.request.uri.path}?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/foo?c=d"),
},
{
rule: Rewrite{URI: "{http.request.uri.path}?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/foo?c=d"),
},
{
rule: Rewrite{URI: "/index.php?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/index.php?c=d"),
},
{
rule: Rewrite{URI: "?a=b&c=d"},
input: newRequest(t, "GET", "/foo"),
expect: newRequest(t, "GET", "/foo?a=b&c=d"),
},
{
rule: Rewrite{URI: "/index.php?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/?a=b"),
expect: newRequest(t, "GET", "/index.php?a=b&c=d"),
},
{
rule: Rewrite{URI: "/index.php?c=d&{http.request.uri.query}"},
input: newRequest(t, "GET", "/?a=b"),
expect: newRequest(t, "GET", "/index.php?c=d&a=b"),
},
{
rule: Rewrite{URI: "/index.php?{http.request.uri.query}&p={http.request.uri.path}"},
input: newRequest(t, "GET", "/foo/bar?a=b"),
expect: newRequest(t, "GET", "/index.php?a=b&p=%2Ffoo%2Fbar"),
},
{
rule: Rewrite{URI: "{http.request.uri.path}?"},
input: newRequest(t, "GET", "/foo/bar?a=b&c=d"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{URI: "?qs={http.request.uri.query}"},
input: newRequest(t, "GET", "/foo?a=b&c=d"),
expect: newRequest(t, "GET", "/foo?qs=a%3Db%26c%3Dd"),
},
{
rule: Rewrite{URI: "/foo?{http.request.uri.query}#frag"},
input: newRequest(t, "GET", "/foo/bar?a=b"),
expect: newRequest(t, "GET", "/foo?a=b#frag"),
},
{
rule: Rewrite{StripPathPrefix: "/prefix"},
input: newRequest(t, "GET", "/foo/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{StripPathPrefix: "/prefix"},
input: newRequest(t, "GET", "/prefix/foo/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{StripPathPrefix: "/prefix"},
input: newRequest(t, "GET", "/foo/prefix/bar"),
expect: newRequest(t, "GET", "/foo/prefix/bar"),
},
{
rule: Rewrite{StripPathSuffix: "/suffix"},
input: newRequest(t, "GET", "/foo/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{StripPathSuffix: "suffix"},
input: newRequest(t, "GET", "/foo/bar/suffix"),
expect: newRequest(t, "GET", "/foo/bar/"),
},
{
rule: Rewrite{StripPathSuffix: "/suffix"},
input: newRequest(t, "GET", "/foo/suffix/bar"),
expect: newRequest(t, "GET", "/foo/suffix/bar"),
},
{
rule: Rewrite{URISubstring: []replacer{{Find: "findme", Replace: "replaced"}}},
input: newRequest(t, "GET", "/foo/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{URISubstring: []replacer{{Find: "findme", Replace: "replaced"}}},
input: newRequest(t, "GET", "/foo/findme/bar"),
expect: newRequest(t, "GET", "/foo/replaced/bar"),
},
} {
// copy the original input just enough so that we can
// compare it after the rewrite to see if it changed
originalInput := &http.Request{
Method: tc.input.Method,
RequestURI: tc.input.RequestURI,
URL: &*tc.input.URL,
}
// populate the replacer just enough for our tests
repl.Set("http.request.uri.path", tc.input.URL.Path)
repl.Set("http.request.uri.query", tc.input.URL.RawQuery)
changed := tc.rule.rewrite(tc.input, repl, nil)
if expected, actual := !reqEqual(originalInput, tc.input), changed; expected != actual {
t.Errorf("Test %d: Expected changed=%t but was %t", i, expected, actual)
}
if expected, actual := tc.expect.Method, tc.input.Method; expected != actual {
t.Errorf("Test %d: Expected Method='%s' but got '%s'", i, expected, actual)
}
if expected, actual := tc.expect.RequestURI, tc.input.RequestURI; expected != actual {
t.Errorf("Test %d: Expected RequestURI='%s' but got '%s'", i, expected, actual)
}
if expected, actual := tc.expect.URL.String(), tc.input.URL.String(); expected != actual {
t.Errorf("Test %d: Expected URL='%s' but got '%s'", i, expected, actual)
}
if expected, actual := tc.expect.URL.RequestURI(), tc.input.URL.RequestURI(); expected != actual {
t.Errorf("Test %d: Expected URL.RequestURI()='%s' but got '%s'", i, expected, actual)
}
if expected, actual := tc.expect.URL.Fragment, tc.input.URL.Fragment; expected != actual {
t.Errorf("Test %d: Expected URL.Fragment='%s' but got '%s'", i, expected, actual)
}
}
}
func newRequest(t *testing.T, method, uri string) *http.Request {
req, err := http.NewRequest(method, uri, nil)
if err != nil {
t.Fatalf("error creating request: %v", err)
}
req.RequestURI = req.URL.RequestURI() // simulate incoming request
return req
}
// reqEqual if r1 and r2 are equal enough for our purposes.
func reqEqual(r1, r2 *http.Request) bool {
if r1.Method != r2.Method {
return false
}
if r1.RequestURI != r2.RequestURI {
return false
}
if (r1.URL == nil && r2.URL != nil) || (r1.URL != nil && r2.URL == nil) {
return false
}
if r1.URL == nil && r2.URL == nil {
return true
}
return r1.URL.Scheme == r2.URL.Scheme &&
r1.URL.Host == r2.URL.Host &&
r1.URL.Path == r2.URL.Path &&
r1.URL.RawPath == r2.URL.RawPath &&
r1.URL.RawQuery == r2.URL.RawQuery &&
r1.URL.Fragment == r2.URL.Fragment
}