Skip to content

Commit 6555d31

Browse files
Merge pull request #108 from doomguy-stg/slashes
Allow arbitrary slashes in object keys
2 parents 70603d7 + ac75de9 commit 6555d31

File tree

2 files changed

+98
-15
lines changed

2 files changed

+98
-15
lines changed

routing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
// to degrade, especially around multipart uploads.
2121
func (g *GoFakeS3) routeBase(w http.ResponseWriter, r *http.Request) {
2222
var (
23-
path = strings.Trim(r.URL.Path, "/")
23+
path = strings.TrimPrefix(r.URL.Path, "/")
2424
parts = strings.SplitN(path, "/", 2)
2525
bucket = parts[0]
2626
query = r.URL.Query()

routing_test.go

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,112 @@ import (
44
"testing"
55
)
66

7+
func assertStatus(ts *testServer, t *testing.T, url string, code int, descr string) {
8+
t.Helper()
9+
client := httpClient()
10+
rs, err := client.Head(ts.url(url))
11+
ts.OK(err)
12+
if rs.StatusCode != code {
13+
t.Fatal("expected status", code, "found", rs.StatusCode, "descr", descr)
14+
}
15+
}
16+
717
func TestRoutingSlashes(t *testing.T) {
818
ts := newTestServer(t, withoutInitialBuckets())
919
defer ts.Close()
1020
ts.backendCreateBucket("test")
1121
ts.backendPutString("test", "obj", nil, "yep")
1222

13-
client := httpClient()
1423

15-
assertStatus := func(url string, code int) {
24+
assertStatus(ts, t, "nope", 404, "missing bucket") // sanity check missing URL
25+
assertStatus(ts, t, "test", 200, "only bucket without slash")
26+
assertStatus(ts, t, "test/", 200, "only bucket with slash")
27+
assertStatus(ts, t, "test//", 404, "object \"/\"") // obj '/' does not exist
28+
assertStatus(ts, t, "test/nope", 404, "missing object")
29+
assertStatus(ts, t, "test/obj", 200, "existing object")
30+
assertStatus(ts, t, "test/obj/", 404, "trailing slash in object key")
31+
assertStatus(ts, t, "test/obj//", 404, "two trailing slashes in object key")
32+
}
33+
34+
func TestRoutingMoreSlashes(t *testing.T) {
35+
testData := [][]string{
36+
{".", "singledot", "0"},
37+
{"./", "dotslash", "0"},
38+
{"./.", "dotslashdot", "0"},
39+
{"/.", "slashdot", "0"},
40+
{"/./", "slashdotslash", "0"},
41+
{"foo/../bar", "singledoubledot", "0"},
42+
{"foo/../bar/../baz", "twodoubledots", "0"},
43+
{"/", "singleslash", "0"},
44+
{"//", "doubleslash", "0"},
45+
{"///", "threeslashes", "0"},
46+
{"////", "fourslashes", "0"},
47+
{"/////", "fiveslashes", "0"},
48+
{"/a", "leadingsingleslash", "0"},
49+
{"//a", "leadingdoubleslash", "0"},
50+
{"///a", "leadingthreeslashes", "0"},
51+
{"////a", "leadingfourslashes", "0"},
52+
{"/////a", "leadingfiveslashes", "0"},
53+
{"a/", "trailingsingleslash", "0"},
54+
{"a//", "trailingdoubleslash", "0"},
55+
{"a///", "trailingthreeslashes", "0"},
56+
{"a////", "trailingfourslashes", "0"},
57+
{"a/////", "trailingfiveslashes", "0"},
58+
{"a/a", "middlesingleslash", "0"},
59+
{"a//a", "middledoubleslash", "0"},
60+
{"a///a", "middlethreeslashes", "0"},
61+
{"a////a", "middlefourslashes", "0"},
62+
{"a/////a", "middlefiveslashes", "0"},
63+
{"a/a/a/a", "lotofmiddlesingleslashes", "0"},
64+
{"a//a//a//a", "lotofmiddledoubleslashes", "0"},
65+
{"a///a///a///a", "lotofmiddlethreeslashes", "0"},
66+
{"a////a////a////a", "lotofmiddlefourslashes", "0"},
67+
{"a/////a/////a/////a", "lotofmiddlefiveslashes", "0"},
68+
}
69+
70+
testRoutingMoreSlashes(t, testData)
71+
72+
// reset and reverse testData
73+
for i, j := 0, len(testData)-1; i < j; i, j = i+1, j-1 {
74+
testData[i], testData[j] = testData[j], testData[i];
75+
testData[i][2] = "0"
76+
testData[j][2] = "0"
77+
}
78+
if len(testData) % 2 != 0 {
79+
testData[((len(testData)-1) / 2)][2] = "0"
80+
}
81+
82+
// re-run test in reverse order
83+
testRoutingMoreSlashes(t, testData)
84+
85+
}
86+
87+
func testRoutingMoreSlashes(t *testing.T, testData [][]string) {
88+
ts := newTestServer(t, withoutInitialBuckets())
89+
defer ts.Close()
90+
ts.backendCreateBucket("test")
91+
92+
checkObjects := func() {
1693
t.Helper()
17-
rs, err := client.Head(ts.url(url))
18-
ts.OK(err)
19-
if rs.StatusCode != code {
20-
t.Fatal("expected status", code, "found", rs.StatusCode)
94+
for _, obj := range testData {
95+
if obj[2] == "0" {
96+
assertStatus(ts, t, obj[0], 404, obj[1])
97+
continue
98+
}
99+
if obj[2] == "1" {
100+
ts.assertObject("test", obj[0], nil, obj[1])
101+
continue
102+
}
103+
t.Fatal("unexpected test data", obj)
21104
}
22105
}
23106

24-
assertStatus("nope", 404) // sanity check missing URL
25-
assertStatus("test", 200)
26-
assertStatus("test/", 200)
27-
assertStatus("test//", 200) // don't care how many slashes
28-
assertStatus("test/nope", 404)
29-
assertStatus("test/obj", 200)
30-
assertStatus("test/obj/", 200)
31-
assertStatus("test/obj//", 200)
107+
checkObjects()
108+
109+
for _, obj := range testData {
110+
ts.backendPutString("test", obj[0], nil, obj[1])
111+
obj[2] = "1"
112+
ts.assertObject("test", obj[0], nil, obj[1])
113+
checkObjects()
114+
}
32115
}

0 commit comments

Comments
 (0)