mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 18:47:20 -04:00 
			
		
		
		
	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.
		
			
				
	
	
		
			270 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			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
 | |
| }
 |