diff --git a/core/corehttp/gateway_handler_unixfs_dir.go b/core/corehttp/gateway_handler_unixfs_dir.go
index 9ba9044751a..a6ab7cb55fd 100644
--- a/core/corehttp/gateway_handler_unixfs_dir.go
+++ b/core/corehttp/gateway_handler_unixfs_dir.go
@@ -41,28 +41,30 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
}
originalUrlPath := requestURI.Path
- // Check if directory has index.html, if so, serveFile
- idxPath := ipath.Join(contentPath, "index.html")
- idx, err := i.api.Unixfs().Get(ctx, idxPath)
- switch err.(type) {
- case nil:
- cpath := contentPath.String()
- dirwithoutslash := cpath[len(cpath)-1] != '/'
+ // Ensure directory paths end with '/'
+ if originalUrlPath[len(originalUrlPath)-1] != '/' {
+ // don't redirect to trailing slash if it's go get
+ // https://github.com/ipfs/kubo/pull/3963
goget := r.URL.Query().Get("go-get") == "1"
- if dirwithoutslash && !goget {
- // See comment above where originalUrlPath is declared.
+ if !goget {
suffix := "/"
+ // preserve query parameters
if r.URL.RawQuery != "" {
- // preserve query parameters
suffix = suffix + "?" + r.URL.RawQuery
}
-
+ // /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar
redirectURL := originalUrlPath + suffix
- logger.Debugw("serving index.html file", "to", redirectURL, "status", http.StatusFound, "path", idxPath)
- http.Redirect(w, r, redirectURL, http.StatusFound)
+ logger.Debugw("directory location moved permanently", "status", http.StatusMovedPermanently)
+ http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
return
}
+ }
+ // Check if directory has index.html, if so, serveFile
+ idxPath := ipath.Join(contentPath, "index.html")
+ idx, err := i.api.Unixfs().Get(ctx, idxPath)
+ switch err.(type) {
+ case nil:
f, ok := idx.(files.File)
if !ok {
internalWebError(w, files.ErrNotReader)
@@ -163,7 +165,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
// add the correct link depending on whether the path ends with a slash
default:
if strings.HasSuffix(backLink, "/") {
- backLink += "./.."
+ backLink += ".."
} else {
backLink += "/.."
}
diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go
index 5ac27341d5d..b6b504907aa 100644
--- a/core/corehttp/gateway_test.go
+++ b/core/corehttp/gateway_test.go
@@ -380,9 +380,9 @@ func TestIPNSHostnameRedirect(t *testing.T) {
t.Fatal(err)
}
- // expect 302 redirect to same path, but with trailing slash
- if res.StatusCode != 302 {
- t.Errorf("status is %d, expected 302", res.StatusCode)
+ // expect 301 redirect to same path, but with trailing slash
+ if res.StatusCode != 301 {
+ t.Errorf("status is %d, expected 301", res.StatusCode)
}
hdr := res.Header["Location"]
if len(hdr) < 1 {
@@ -403,9 +403,9 @@ func TestIPNSHostnameRedirect(t *testing.T) {
t.Fatal(err)
}
- // expect 302 redirect to same path, but with prefix and trailing slash
- if res.StatusCode != 302 {
- t.Errorf("status is %d, expected 302", res.StatusCode)
+ // expect 301 redirect to same path, but with prefix and trailing slash
+ if res.StatusCode != 301 {
+ t.Errorf("status is %d, expected 301", res.StatusCode)
}
hdr = res.Header["Location"]
if len(hdr) < 1 {
@@ -492,7 +492,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
if !matchPathOrBreadcrumbs(s, "/ipns/example.net/foo? #<'") {
t.Fatalf("expected a path in directory listing")
}
- if !strings.Contains(s, "") {
+ if !strings.Contains(s, "") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "") {
@@ -566,7 +566,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
if !matchPathOrBreadcrumbs(s, "/ipns/example.net/foo? #<'/bar") {
t.Fatalf("expected a path in directory listing")
}
- if !strings.Contains(s, "") {
+ if !strings.Contains(s, "") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "") {
diff --git a/test/sharness/t0110-gateway.sh b/test/sharness/t0110-gateway.sh
index 11220347819..8cf28dbf41a 100755
--- a/test/sharness/t0110-gateway.sh
+++ b/test/sharness/t0110-gateway.sh
@@ -72,9 +72,17 @@ test_expect_success "GET IPFS directory file output looks good" '
test_expect_success "GET IPFS directory with index.html returns redirect to add trailing slash" "
curl -sI -o response_without_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?query=to-remember\" &&
+ test_should_contain \"HTTP/1.1 301 Moved Permanently\" response_without_slash &&
test_should_contain \"Location: /ipfs/$HASH2/dirwithindex/?query=to-remember\" response_without_slash
"
+# This enables go get to parse go-import meta tags from index.html files stored in IPFS
+# https://github.com/ipfs/kubo/pull/3963
+test_expect_success "GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed" "
+ curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?go-get=1\" &&
+ test_should_contain \"hello i am a webpage\" response_with_slash
+"
+
test_expect_success "GET IPFS directory with index.html and trailing slash returns expected output" "
curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex/?query=to-remember\" &&
test_should_contain \"hello i am a webpage\" response_with_slash
diff --git a/test/sharness/t0113-gateway-symlink.sh b/test/sharness/t0113-gateway-symlink.sh
index d1f94bde7e9..9fa7ffa6edf 100755
--- a/test/sharness/t0113-gateway-symlink.sh
+++ b/test/sharness/t0113-gateway-symlink.sh
@@ -22,7 +22,7 @@ test_expect_success "Add the test directory" '
'
test_expect_success "Test the directory listing" '
- curl "$GWAY_ADDR/ipfs/$HASH" > list_response &&
+ curl "$GWAY_ADDR/ipfs/$HASH/" > list_response &&
test_should_contain ">foo<" list_response &&
test_should_contain ">bar<" list_response
'
diff --git a/test/sharness/t0114-gateway-subdomains.sh b/test/sharness/t0114-gateway-subdomains.sh
index f9ab0d82427..0dad1e95c1a 100755
--- a/test/sharness/t0114-gateway-subdomains.sh
+++ b/test/sharness/t0114-gateway-subdomains.sh
@@ -268,7 +268,7 @@ test_expect_success "valid file and subdirectory paths in directory listing at {
test_expect_success "valid parent directory path in directory listing at {cid}.ipfs.localhost/sub/dir" '
curl -s --resolve $DIR_HOSTNAME:127.0.0.1 "http://$DIR_HOSTNAME/ipfs/ipns/" > list_response &&
- test_should_contain ".." list_response &&
+ test_should_contain ".." list_response &&
test_should_contain "bar" list_response
'
@@ -441,7 +441,7 @@ test_expect_success "valid file and directory paths in directory listing at {cid
test_expect_success "valid parent directory path in directory listing at {cid}.ipfs.example.com/sub/dir" '
curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT/ipfs/ipns/ > list_response &&
- test_should_contain ".." list_response &&
+ test_should_contain ".." list_response &&
test_should_contain "bar" list_response
'
diff --git a/test/sharness/t0115-gateway-dir-listing.sh b/test/sharness/t0115-gateway-dir-listing.sh
index bd634388df5..bb84203bc02 100755
--- a/test/sharness/t0115-gateway-dir-listing.sh
+++ b/test/sharness/t0115-gateway-dir-listing.sh
@@ -43,8 +43,14 @@ test_expect_success "path gw: backlink on root CID should be hidden" '
test_should_not_contain ".." list_response
'
-test_expect_success "path gw: Etag should be present" '
+test_expect_success "path gw: redirect dir listing to URL with trailing slash" '
curl -sD - http://127.0.0.1:$GWAY_PORT/ipfs/${DIR_CID}/ą/ę > list_response &&
+ test_should_contain "HTTP/1.1 301 Moved Permanently" list_response &&
+ test_should_contain "Location: /ipfs/${DIR_CID}/%c4%85/%c4%99/" list_response
+'
+
+test_expect_success "path gw: Etag should be present" '
+ curl -sD - http://127.0.0.1:$GWAY_PORT/ipfs/${DIR_CID}/ą/ę/ > list_response &&
test_should_contain "Index of" list_response &&
test_should_contain "Etag: \"DirIndex-" list_response
'
@@ -72,19 +78,25 @@ test_expect_success "path gw: hash column should be a CID link with filename par
DIR_HOSTNAME="${DIR_CID}.ipfs.localhost"
# note: we skip DNS lookup by running curl with --resolve $DIR_HOSTNAME:127.0.0.1
-test_expect_success "path gw: backlink on root CID should be hidden" '
+test_expect_success "subdomain gw: backlink on root CID should be hidden" '
curl -sD - --resolve $DIR_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DIR_HOSTNAME:$GWAY_PORT/ > list_response &&
test_should_contain "Index of" list_response &&
test_should_not_contain ".." list_response
'
-test_expect_success "path gw: Etag should be present" '
+test_expect_success "subdomain gw: redirect dir listing to URL with trailing slash" '
curl -sD - --resolve $DIR_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DIR_HOSTNAME:$GWAY_PORT/ą/ę > list_response &&
+ test_should_contain "HTTP/1.1 301 Moved Permanently" list_response &&
+ test_should_contain "Location: /%c4%85/%c4%99/" list_response
+'
+
+test_expect_success "subdomain gw: Etag should be present" '
+ curl -sD - --resolve $DIR_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DIR_HOSTNAME:$GWAY_PORT/ą/ę/ > list_response &&
test_should_contain "Index of" list_response &&
test_should_contain "Etag: \"DirIndex-" list_response
'
-test_expect_success "path gw: backlink on subdirectory should point at parent directory" '
+test_expect_success "subdomain gw: backlink on subdirectory should point at parent directory" '
test_should_contain ".." list_response
'
@@ -92,11 +104,11 @@ test_expect_success "subdomain gw: breadcrumbs should leverage path-based router
test_should_contain "/ipfs/$DIR_CID/ą/ę" list_response
'
-test_expect_success "path gw: name column should be a link to content root mounted at subdomain origin" '
+test_expect_success "subdomain gw: name column should be a link to content root mounted at subdomain origin" '
test_should_contain "file-źł.txt" list_response
'
-test_expect_success "path gw: hash column should be a CID link to path router with filename param" '
+test_expect_success "subdomain gw: hash column should be a CID link to path router with filename param" '
test_should_contain "" list_response
'
@@ -121,8 +133,14 @@ test_expect_success "dnslink gw: backlink on root CID should be hidden" '
test_should_not_contain ".." list_response
'
-test_expect_success "dnslink gw: Etag should be present" '
+test_expect_success "dnslink gw: redirect dir listing to URL with trailing slash" '
curl -sD - --resolve $DNSLINK_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DNSLINK_HOSTNAME:$GWAY_PORT/ą/ę > list_response &&
+ test_should_contain "HTTP/1.1 301 Moved Permanently" list_response &&
+ test_should_contain "Location: /%c4%85/%c4%99/" list_response
+'
+
+test_expect_success "dnslink gw: Etag should be present" '
+ curl -sD - --resolve $DNSLINK_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DNSLINK_HOSTNAME:$GWAY_PORT/ą/ę/ > list_response &&
test_should_contain "Index of" list_response &&
test_should_contain "Etag: \"DirIndex-" list_response
'