diff options
Diffstat (limited to 'weed/s3api/s3_constants')
| -rw-r--r-- | weed/s3api/s3_constants/header.go | 3 | ||||
| -rw-r--r-- | weed/s3api/s3_constants/header_test.go | 132 |
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) + } + }) + } +} + |
