Skip to content

Commit

Permalink
rewrite: Fix for rewrites with URI placeholders (#3209)
Browse files Browse the repository at this point in the history
If a placeholder in the path component injects a query string such as
the {http.request.uri} placeholder is wont to do, we need to separate it
out from the path.
  • Loading branch information
mholt committed Apr 1, 2020
1 parent 9fb0b1e commit 809e727
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 7 deletions.
39 changes: 32 additions & 7 deletions modules/caddyhttp/rewrite/rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (rewr Rewrite) rewrite(r *http.Request, repl *caddy.Replacer, logger *zap.L
r.Method = strings.ToUpper(repl.ReplaceAll(rewr.Method, ""))
}

// uri (path, query string, and fragment... because why not)
// uri (path, query string and... fragment, because why not)
if uri := rewr.URI; uri != "" {
// find the bounds of each part of the URI that exist
pathStart, qsStart, fragStart := -1, -1, -1
Expand All @@ -136,18 +136,43 @@ func (rewr Rewrite) rewrite(r *http.Request, repl *caddy.Replacer, logger *zap.L
qsEnd = len(uri)
}

// isolate the three main components of the URI
var path, query, frag string
if pathStart > -1 {
path = uri[pathStart:pathEnd]
}
if qsStart > -1 {
query = uri[qsStart:qsEnd]
}
if fragStart > -1 {
frag = uri[fragStart:]
}

// build components which are specified, and store them
// in a temporary variable so that they all read the
// same version of the URI
var newPath, newQuery, newFrag string
if pathStart >= 0 {
newPath = repl.ReplaceAll(uri[pathStart:pathEnd], "")
if path != "" {
newPath = repl.ReplaceAll(path, "")
}
if qsStart >= 0 {
newQuery = buildQueryString(uri[qsStart:qsEnd], repl)

// before continuing, we need to check if a query string
// snuck into the path component during replacements
if quPos := strings.Index(newPath, "?"); quPos > -1 {
// recompute; new path contains a query string
var injectedQuery string
newPath, injectedQuery = newPath[:quPos], newPath[quPos+1:]
// don't overwrite explicitly-configured query string
if query == "" {
query = injectedQuery
}
}
if fragStart >= 0 {
newFrag = repl.ReplaceAll(uri[fragStart:], "")

if query != "" {
newQuery = buildQueryString(query, repl)
}
if frag != "" {
newFrag = repl.ReplaceAll(frag, "")
}

// update the URI with the new components
Expand Down
31 changes: 31 additions & 0 deletions modules/caddyhttp/rewrite/rewrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,36 @@ func TestRewrite(t *testing.T) {
input: newRequest(t, "GET", "/foo/bar?a=b"),
expect: newRequest(t, "GET", "/foo?a=b#frag"),
},
{
rule: Rewrite{URI: "/foo{http.request.uri}"},
input: newRequest(t, "GET", "/bar?a=b"),
expect: newRequest(t, "GET", "/foo/bar?a=b"),
},
{
rule: Rewrite{URI: "/foo{http.request.uri}"},
input: newRequest(t, "GET", "/bar"),
expect: newRequest(t, "GET", "/foo/bar"),
},
{
rule: Rewrite{URI: "/foo{http.request.uri}?c=d"},
input: newRequest(t, "GET", "/bar?a=b"),
expect: newRequest(t, "GET", "/foo/bar?c=d"),
},
{
rule: Rewrite{URI: "/foo{http.request.uri}?{http.request.uri.query}&c=d"},
input: newRequest(t, "GET", "/bar?a=b"),
expect: newRequest(t, "GET", "/foo/bar?a=b&c=d"),
},
{
rule: Rewrite{URI: "{http.request.uri}"},
input: newRequest(t, "GET", "/bar?a=b"),
expect: newRequest(t, "GET", "/bar?a=b"),
},
{
rule: Rewrite{URI: "{http.request.uri.path}bar?c=d"},
input: newRequest(t, "GET", "/foo/?a=b"),
expect: newRequest(t, "GET", "/foo/bar?c=d"),
},

{
rule: Rewrite{StripPathPrefix: "/prefix"},
Expand Down Expand Up @@ -211,6 +241,7 @@ func TestRewrite(t *testing.T) {
}

// populate the replacer just enough for our tests
repl.Set("http.request.uri", tc.input.RequestURI)
repl.Set("http.request.uri.path", tc.input.URL.Path)
repl.Set("http.request.uri.query", tc.input.URL.RawQuery)

Expand Down

0 comments on commit 809e727

Please sign in to comment.