diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index b78bacb02e54..8c34ba4541ee 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -181,6 +181,23 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } originalUrlPath := prefix + requestURI.Path + // Query parameter handling to support requests produced by navigator.registerProtocolHandler. + // E.g. This code will redirect calls to /ipfs/?uri=ipfs%3A%2F%2Fcontent-identifier + // to /ipfs/content-identifier. + if uri := r.URL.Query().Get("uri"); uri != "" { + u, err := url.Parse(uri) + if err != nil { + webError(w, "failed to parse uri query parameter", err, http.StatusBadRequest) + return + } + if u.Scheme != "ipfs" && u.Scheme != "ipns" { + webError(w, "uri query parameter scheme must be ipfs or ipns", err, http.StatusBadRequest) + return + } + http.Redirect(w, r, gopath.Join("/", prefix, u.Scheme, u.Host), http.StatusMovedPermanently) + return + } + // Service Worker registration request if r.Header.Get("Service-Worker") == "script" { // Disallow Service Worker registration on namespace roots diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index f98b4a7737b0..ffe6b8fd6d98 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -161,6 +161,44 @@ func matchPathOrBreadcrumbs(s string, expected string) bool { return matched } +func TestUriQueryRedirect(t *testing.T) { + ts, _, _ := newTestServerAndNode(t, mockNamesys{}) + + cid := "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR" + for i, test := range []struct { + path string + status int + location string + }{ + {"/ipfs/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, + {"/ipfs/?uri=ipfs%3A%2F%2F" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, + {"/ipfs?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/?uri=ipfs://" + cid}, + {"/ipfs/?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/" + cid}, + {"/ipns?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/?uri=ipns://" + cid}, + {"/ipns/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, + {"/ipns/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, + {"/ipfs/?uri=unsupported://" + cid, http.StatusBadRequest, ""}, + {"/ipfs/?uri=" + cid, http.StatusBadRequest, ""}, + } { + + r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) + if err != nil { + t.Fatal(err) + } + resp, err := doWithoutRedirect(r) + defer resp.Body.Close() + + if resp.StatusCode != test.status { + t.Errorf("(%d) got %d, expected %d from %s", i, resp.StatusCode, test.status, ts.URL+test.path) + } + + locHdr := resp.Header.Get("Location") + if locHdr != test.location { + t.Errorf("(%d) location header got %s, expected %s from %s", i, locHdr, test.location, ts.URL+test.path) + } + } +} + func TestGatewayGet(t *testing.T) { ns := mockNamesys{} ts, api, ctx := newTestServerAndNode(t, ns)