summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/caddyhttp.go
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2022-08-16 08:48:57 -0600
committerGitHub <noreply@github.com>2022-08-16 08:48:57 -0600
commita479943acd70068c4b80d3a8f4b8dd7ab93ca2ba (patch)
treef50a45b3b0c8c8475b783583967c1175f5e6673c /modules/caddyhttp/caddyhttp.go
parentdc62d468e9645f52a5e1b4f6093dff65137ab3fe (diff)
caddyhttp: Smarter path matching and rewriting (#4948)
Co-authored-by: RussellLuo <luopeng.he@gmail.com>
Diffstat (limited to 'modules/caddyhttp/caddyhttp.go')
-rw-r--r--modules/caddyhttp/caddyhttp.go35
1 files changed, 35 insertions, 0 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index 784b2b9..c9cc9e6 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