aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/s3_constants
diff options
context:
space:
mode:
Diffstat (limited to 'weed/s3api/s3_constants')
-rw-r--r--weed/s3api/s3_constants/header.go3
-rw-r--r--weed/s3api/s3_constants/header_test.go132
2 files changed, 135 insertions, 0 deletions
diff --git a/weed/s3api/s3_constants/header.go b/weed/s3api/s3_constants/header.go
index b7e1be9e5..4b34f397e 100644
--- a/weed/s3api/s3_constants/header.go
+++ b/weed/s3api/s3_constants/header.go
@@ -146,7 +146,10 @@ func GetBucketAndObject(r *http.Request) (bucket, object string) {
// NormalizeObjectKey ensures the object key has a leading slash and no duplicate slashes.
// This normalizes keys from various sources (URL path, form values, etc.) to a consistent format.
+// It also converts Windows-style backslashes to forward slashes for cross-platform compatibility.
func NormalizeObjectKey(object string) string {
+ // Convert Windows-style backslashes to forward slashes
+ object = strings.ReplaceAll(object, "\\", "/")
object = removeDuplicateSlashes(object)
if !strings.HasPrefix(object, "/") {
object = "/" + object
diff --git a/weed/s3api/s3_constants/header_test.go b/weed/s3api/s3_constants/header_test.go
new file mode 100644
index 000000000..b16cfc6a8
--- /dev/null
+++ b/weed/s3api/s3_constants/header_test.go
@@ -0,0 +1,132 @@
+package s3_constants
+
+import (
+ "testing"
+)
+
+func TestNormalizeObjectKey(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ }{
+ {
+ name: "simple key",
+ input: "file.txt",
+ expected: "/file.txt",
+ },
+ {
+ name: "key with leading slash",
+ input: "/file.txt",
+ expected: "/file.txt",
+ },
+ {
+ name: "key with directory",
+ input: "folder/file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "key with leading slash and directory",
+ input: "/folder/file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "key with duplicate slashes",
+ input: "folder//subfolder///file.txt",
+ expected: "/folder/subfolder/file.txt",
+ },
+ {
+ name: "Windows backslash - simple",
+ input: "folder\\file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "Windows backslash - nested",
+ input: "folder\\subfolder\\file.txt",
+ expected: "/folder/subfolder/file.txt",
+ },
+ {
+ name: "Windows backslash - with leading slash",
+ input: "/folder\\subfolder\\file.txt",
+ expected: "/folder/subfolder/file.txt",
+ },
+ {
+ name: "mixed slashes",
+ input: "folder\\subfolder/another\\file.txt",
+ expected: "/folder/subfolder/another/file.txt",
+ },
+ {
+ name: "Windows full path style (edge case)",
+ input: "C:\\Users\\test\\file.txt",
+ expected: "/C:/Users/test/file.txt",
+ },
+ {
+ name: "empty string",
+ input: "",
+ expected: "/",
+ },
+ {
+ name: "just a slash",
+ input: "/",
+ expected: "/",
+ },
+ {
+ name: "just a backslash",
+ input: "\\",
+ expected: "/",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := NormalizeObjectKey(tt.input)
+ if result != tt.expected {
+ t.Errorf("NormalizeObjectKey(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ })
+ }
+}
+
+func TestRemoveDuplicateSlashes(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ }{
+ {
+ name: "no duplicates",
+ input: "/folder/file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "double slash",
+ input: "/folder//file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "triple slash",
+ input: "/folder///file.txt",
+ expected: "/folder/file.txt",
+ },
+ {
+ name: "multiple duplicate locations",
+ input: "//folder//subfolder///file.txt",
+ expected: "/folder/subfolder/file.txt",
+ },
+ {
+ name: "empty string",
+ input: "",
+ expected: "",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := removeDuplicateSlashes(tt.input)
+ if result != tt.expected {
+ t.Errorf("removeDuplicateSlashes(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ })
+ }
+}
+