diff --git a/.github/workflows/gateway-sharness.yml b/.github/workflows/gateway-sharness.yml index 3da1845c24..aacaeca72d 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: master - 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 aa36b47e9a..8f397fd1d6 100644 --- a/gateway/gateway_test.go +++ b/gateway/gateway_test.go @@ -548,3 +548,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 4c3fe29fdb..8a5cb13d7a 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" "go.opentelemetry.io/otel/attribute" @@ -305,6 +307,34 @@ func (i *handler) optionsHandler(w http.ResponseWriter, r *http.Request) { i.addUserHeaders(w) // return all custom headers (including CORS ones, if set) } +// handleIpnsPeerIdToCidRedirection redirects from /ipns/b58mh to /ipns/cid in +// the most cost-effective way. +func handleIpnsPeerIdToCidRedirection(w http.ResponseWriter, r *http.Request) bool { + pathParts := strings.Split(r.URL.Path, "/") + if len(pathParts) < 3 { + return false + } + + s := pathParts[2] + + // Similarly to peer.Decode, check the prefix first as it is + // less computationally expensive. + 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 +} + func (i *handler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { begin := time.Now() @@ -325,6 +355,10 @@ func (i *handler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { return } + if handleIpnsPeerIdToCidRedirection(w, r) { + return + } + contentPath := ipath.New(r.URL.Path) ctx := context.WithValue(r.Context(), ContentPathKey, contentPath) r = r.WithContext(ctx)