Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
rewrite: Fix for rewrites with URI placeholders (#3209)
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.
Matthew Holt committed
Apr 1, 2020 at 00:43 UTC
809e72792c501ceeadac4a3b9b327dfc575b9dfd
2 files changed
+63
-7
modules/caddyhttp/rewrite/rewrite.go
+32
-7
@@ -114,7 +114,7 @@ func (rewr Rewrite) rewrite(r *http.Request, repl *caddy.Replacer, logger *zap.L
114
r.Method = strings.ToUpper(repl.ReplaceAll(rewr.Method, ""))
115
}
116
117
- // uri (path, query string, and fragment... because why not)
117
+ // uri (path, query string and... fragment, because why not)
118
if uri := rewr.URI; uri != "" {
119
// find the bounds of each part of the URI that exist
120
pathStart, qsStart, fragStart := -1, -1, -1
@@ -136,18 +136,43 @@ func (rewr Rewrite) rewrite(r *http.Request, repl *caddy.Replacer, logger *zap.L
136
qsEnd = len(uri)
137
}
138
139
+ // isolate the three main components of the URI
140
+ var path, query, frag string
141
+ if pathStart > -1 {
142
+ path = uri[pathStart:pathEnd]
143
+ }
144
+ if qsStart > -1 {
145
+ query = uri[qsStart:qsEnd]
146
+ }
147
+ if fragStart > -1 {
148
+ frag = uri[fragStart:]
149
+ }
150
+
151
// build components which are specified, and store them
152
// in a temporary variable so that they all read the
153
// same version of the URI
154
var newPath, newQuery, newFrag string
143
- if pathStart >= 0 {
144
- newPath = repl.ReplaceAll(uri[pathStart:pathEnd], "")
155
+ if path != "" {
156
+ newPath = repl.ReplaceAll(path, "")
157
}
146
- if qsStart >= 0 {
147
- newQuery = buildQueryString(uri[qsStart:qsEnd], repl)
158
+
159
+ // before continuing, we need to check if a query string
160
+ // snuck into the path component during replacements
161
+ if quPos := strings.Index(newPath, "?"); quPos > -1 {
162
+ // recompute; new path contains a query string
163
+ var injectedQuery string
164
+ newPath, injectedQuery = newPath[:quPos], newPath[quPos+1:]
165
+ // don't overwrite explicitly-configured query string
166
+ if query == "" {
167
+ query = injectedQuery
168
+ }
169
}
149
- if fragStart >= 0 {
150
- newFrag = repl.ReplaceAll(uri[fragStart:], "")
170
+
171
+ if query != "" {
172
+ newQuery = buildQueryString(query, repl)
173
+ }
174
+ if frag != "" {
175
+ newFrag = repl.ReplaceAll(frag, "")
176
}
177
178
// update the URI with the new components
modules/caddyhttp/rewrite/rewrite_test.go
+31
@@ -158,6 +158,36 @@ func TestRewrite(t *testing.T) {
158
input: newRequest(t, "GET", "/foo/bar?a=b"),
159
expect: newRequest(t, "GET", "/foo?a=b#frag"),
160
},
161
+ {
162
+ rule: Rewrite{URI: "/foo{http.request.uri}"},
163
+ input: newRequest(t, "GET", "/bar?a=b"),
164
+ expect: newRequest(t, "GET", "/foo/bar?a=b"),
165
+ },
166
+ {
167
+ rule: Rewrite{URI: "/foo{http.request.uri}"},
168
+ input: newRequest(t, "GET", "/bar"),
169
+ expect: newRequest(t, "GET", "/foo/bar"),
170
+ },
171
+ {
172
+ rule: Rewrite{URI: "/foo{http.request.uri}?c=d"},
173
+ input: newRequest(t, "GET", "/bar?a=b"),
174
+ expect: newRequest(t, "GET", "/foo/bar?c=d"),
175
+ },
176
+ {
177
+ rule: Rewrite{URI: "/foo{http.request.uri}?{http.request.uri.query}&c=d"},
178
+ input: newRequest(t, "GET", "/bar?a=b"),
179
+ expect: newRequest(t, "GET", "/foo/bar?a=b&c=d"),
180
+ },
181
+ {
182
+ rule: Rewrite{URI: "{http.request.uri}"},
183
+ input: newRequest(t, "GET", "/bar?a=b"),
184
+ expect: newRequest(t, "GET", "/bar?a=b"),
185
+ },
186
+ {
187
+ rule: Rewrite{URI: "{http.request.uri.path}bar?c=d"},
188
+ input: newRequest(t, "GET", "/foo/?a=b"),
189
+ expect: newRequest(t, "GET", "/foo/bar?c=d"),
190
+ },
191
192
{
193
rule: Rewrite{StripPathPrefix: "/prefix"},
@@ -211,6 +241,7 @@ func TestRewrite(t *testing.T) {
241
}
242
243
// populate the replacer just enough for our tests
244
+ repl.Set("http.request.uri", tc.input.RequestURI)
245
repl.Set("http.request.uri.path", tc.input.URL.Path)
246
repl.Set("http.request.uri.query", tc.input.URL.RawQuery)
247