@@ -19,6 +19,8 @@ import (
1919 ipath "github.com/ipfs/boxo/coreiface/path"
2020 cid "github.com/ipfs/go-cid"
2121 logging "github.com/ipfs/go-log"
22+ "github.com/libp2p/go-libp2p/core/peer"
23+ "github.com/multiformats/go-multibase"
2224 prometheus "github.com/prometheus/client_golang/prometheus"
2325 "go.opentelemetry.io/otel/attribute"
2426 "go.opentelemetry.io/otel/trace"
@@ -204,6 +206,10 @@ func (i *handler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) {
204206 return
205207 }
206208
209+ if handleIpnsB58mhToCidRedirection (w , r ) {
210+ return
211+ }
212+
207213 contentPath := ipath .New (r .URL .Path )
208214 ctx := context .WithValue (r .Context (), ContentPathKey , contentPath )
209215 r = r .WithContext (ctx )
@@ -728,6 +734,57 @@ func handleServiceWorkerRegistration(r *http.Request) (err *ErrorResponse) {
728734 return nil
729735}
730736
737+ // handleIpnsB58mhToCidRedirection redirects from /ipns/b58mh to /ipns/cid in
738+ // the most cost-effective way.
739+ func handleIpnsB58mhToCidRedirection (w http.ResponseWriter , r * http.Request ) bool {
740+ if _ , dnslink := r .Context ().Value (DNSLinkHostnameKey ).(string ); dnslink {
741+ // For DNSLink hostnames, do not perform redirection in order to not break
742+ // website. For example, if `example.net` is backed by `/ipns/base58`, we
743+ // must NOT redirect to `example.net/ipns/base36-id`.
744+ return false
745+ }
746+
747+ if w .Header ().Get ("Location" ) != "" {
748+ // Ignore this if there is already a redirection in place. This happens
749+ // if there is a subdomain redirection. In that case, the path is already
750+ // converted to CIDv1.
751+ return false
752+ }
753+
754+ pathParts := strings .Split (r .URL .Path , "/" )
755+ if len (pathParts ) < 3 {
756+ return false
757+ }
758+
759+ if pathParts [1 ] != "ipns" {
760+ return false
761+ }
762+
763+ id , err := peer .Decode (pathParts [2 ])
764+ if err != nil {
765+ return false
766+ }
767+
768+ // Convert the peer ID to a CIDv1.
769+ cid := peer .ToCid (id )
770+
771+ // Encode CID in base36 to match the subdomain URLs.
772+ encodedCID , err := cid .StringOfBase (multibase .Base36 )
773+ if err != nil {
774+ return false
775+ }
776+
777+ // If the CID was already encoded, do not redirect.
778+ if encodedCID == pathParts [2 ] {
779+ return false
780+ }
781+
782+ pathParts [2 ] = encodedCID
783+ r .URL .Path = strings .Join (pathParts , "/" )
784+ http .Redirect (w , r , r .URL .String (), http .StatusFound )
785+ return true
786+ }
787+
731788// Attempt to fix redundant /ipfs/ namespace as long as resulting
732789// 'intended' path is valid. This is in case gremlins were tickled
733790// wrong way and user ended up at /ipfs/ipfs/{cid} or /ipfs/ipns/{id}
0 commit comments