@marcel91200 / caddy Mirror · read-only / commits / 809e7279
Fork 0

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