diff options
author | Matt Holt <[email protected]> | 2022-08-16 08:48:57 -0600 |
---|---|---|
committer | GitHub <[email protected]> | 2022-08-16 08:48:57 -0600 |
commit | a479943acd70068c4b80d3a8f4b8dd7ab93ca2ba (patch) | |
tree | f50a45b3b0c8c8475b783583967c1175f5e6673c /modules/caddyhttp/caddyhttp.go | |
parent | dc62d468e9645f52a5e1b4f6093dff65137ab3fe (diff) | |
download | caddy-a479943acd70068c4b80d3a8f4b8dd7ab93ca2ba.tar.gz caddy-a479943acd70068c4b80d3a8f4b8dd7ab93ca2ba.zip |
caddyhttp: Smarter path matching and rewriting (#4948)
Co-authored-by: RussellLuo <[email protected]>
Diffstat (limited to 'modules/caddyhttp/caddyhttp.go')
-rw-r--r-- | modules/caddyhttp/caddyhttp.go | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index 784b2b900..c9cc9e6d9 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -20,6 +20,7 @@ import ( "io" "net" "net/http" + "path" "path/filepath" "strconv" "strings" @@ -244,6 +245,40 @@ func SanitizedPathJoin(root, reqPath string) string { return path } +// CleanPath cleans path p according to path.Clean(), but only +// merges repeated slashes if collapseSlashes is true, and always +// preserves trailing slashes. +func CleanPath(p string, collapseSlashes bool) string { + if collapseSlashes { + return cleanPath(p) + } + + // insert an invalid/impossible URI character into each two consecutive + // slashes to expand empty path segments; then clean the path as usual, + // and then remove the remaining temporary characters. + const tmpCh = 0xff + var sb strings.Builder + for i, ch := range p { + if ch == '/' && i > 0 && p[i-1] == '/' { + sb.WriteByte(tmpCh) + } + sb.WriteRune(ch) + } + halfCleaned := cleanPath(sb.String()) + halfCleaned = strings.ReplaceAll(halfCleaned, string([]byte{tmpCh}), "") + + return halfCleaned +} + +// cleanPath does path.Clean(p) but preserves any trailing slash. +func cleanPath(p string) string { + cleaned := path.Clean(p) + if cleaned != "/" && strings.HasSuffix(p, "/") { + cleaned = cleaned + "/" + } + return cleaned +} + // tlsPlaceholderWrapper is a no-op listener wrapper that marks // where the TLS listener should be in a chain of listener wrappers. // It should only be used if another listener wrapper must be placed |