diff --git a/.github/workflows/gateway-sharness.yml b/.github/workflows/gateway-sharness.yml index 3da1845c24..d99b2a3741 100644 --- a/.github/workflows/gateway-sharness.yml +++ b/.github/workflows/gateway-sharness.yml @@ -28,7 +28,7 @@ jobs: with: repository: ipfs/kubo path: kubo - ref: 503edee648e29c62888f05fa146ab13d9c65077d + ref: ef55bd4252b3df4dbf998778f66a80143101702b - name: Install Missing Tools run: sudo apt install -y socat net-tools fish libxml2-utils - name: Restore Go Cache diff --git a/gateway/gateway_test.go b/gateway/gateway_test.go index e6f96858a6..8c299772c1 100644 --- a/gateway/gateway_test.go +++ b/gateway/gateway_test.go @@ -544,3 +544,15 @@ func TestGoGetSupport(t *testing.T) { assert.Nil(t, err) assert.Equal(t, http.StatusOK, res.StatusCode) } + +func TestIpnsBase58MultihashRedirect(t *testing.T) { + ts, _, _ := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/ipns/12D3KooWRBy97UB99e3J6hiPesre1MZeuNQvfan4gBziswrRJsNK?keep=query", nil) + assert.Nil(t, err) + + res, err := doWithoutRedirect(req) + assert.Nil(t, err) + assert.Equal(t, "/ipns/bafzaajaiaejcbzdibmxyzdjbbehgvizh6g5tikvy47mshdy6gwbruvgwvd24seje?keep=query", res.Header.Get("Location")) +} diff --git a/gateway/handler.go b/gateway/handler.go index 6a5f17e87c..498a74a876 100644 --- a/gateway/handler.go +++ b/gateway/handler.go @@ -19,6 +19,8 @@ import ( ipath "github.com/ipfs/boxo/coreiface/path" cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multihash" prometheus "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -208,6 +210,10 @@ func (i *handler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { return } + if handleIpnsB58mhToCidRedirection(w, r) { + return + } + contentPath := ipath.New(r.URL.Path) ctx := context.WithValue(r.Context(), ContentPathKey, contentPath) r = r.WithContext(ctx) @@ -727,6 +733,45 @@ func handleServiceWorkerRegistration(r *http.Request) (err *ErrorResponse) { return nil } +// handleIpnsB58mhToCidRedirection redirects from /ipns/b58mh to /ipns/cid in +// the most cost-effective way. +func handleIpnsB58mhToCidRedirection(w http.ResponseWriter, r *http.Request) bool { + if w.Header().Get("Location") != "" { + // Ignore this if there is already a redirection in place. This happens + // if there is a subdomain redirection. In that case, the path is already + // converted to CIDv1. + return false + } + + pathParts := strings.Split(r.URL.Path, "/") + if len(pathParts) < 3 { + return false + } + + if pathParts[1] != "ipns" { + return false + } + + s := pathParts[2] + + // Similarly to peer.Decode, check the prefix first as it is less computationally + // expensive. Base58 encoded peer IDs will start with a '1'. + if !strings.HasPrefix(s, "1") { + return false + } + + // Decode the base58 encoded sha256 or identity multihash. + m, err := multihash.FromB58String(s) + if err != nil { + return false + } + + pathParts[2] = peer.ToCid(peer.ID(m)).String() + r.URL.Path = strings.Join(pathParts, "/") + http.Redirect(w, r, r.URL.String(), http.StatusFound) + return true +} + // Attempt to fix redundant /ipfs/ namespace as long as resulting // 'intended' path is valid. This is in case gremlins were tickled // wrong way and user ended up at /ipfs/ipfs/{cid} or /ipfs/ipns/{id}