diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go
index 49fa519fb3d..d4e357740bd 100644
--- a/core/corehttp/gateway_test.go
+++ b/core/corehttp/gateway_test.go
@@ -6,7 +6,6 @@ import (
"io"
"net/http"
"net/http/httptest"
- "regexp"
"strings"
"testing"
@@ -18,19 +17,14 @@ import (
datastore "github.com/ipfs/go-datastore"
syncds "github.com/ipfs/go-datastore/sync"
- "github.com/ipfs/go-libipfs/files"
path "github.com/ipfs/go-path"
iface "github.com/ipfs/interface-go-ipfs-core"
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
- ipath "github.com/ipfs/interface-go-ipfs-core/path"
config "github.com/ipfs/kubo/config"
ci "github.com/libp2p/go-libp2p/core/crypto"
id "github.com/libp2p/go-libp2p/p2p/protocol/identify"
)
-// `ipfs object new unixfs-dir`
-var emptyDir = "/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
-
type mockNamesys map[string]path.Path
func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...nsopts.ResolveOpt) (value path.Path, err error) {
@@ -145,478 +139,6 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface
return ts, api, n.Context()
}
-func matchPathOrBreadcrumbs(s string, expected string) bool {
- matched, _ := regexp.MatchString("Index of\n[\t ]*"+regexp.QuoteMeta(expected), s)
- return matched
-}
-
-func TestUriQueryRedirect(t *testing.T) {
- ts, _, _ := newTestServerAndNode(t, mockNamesys{})
-
- cid := "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
- for i, test := range []struct {
- path string
- status int
- location string
- }{
- // - Browsers will send original URI in URL-escaped form
- // - We expect query parameters to be persisted
- // - We drop fragments, as those should not be sent by a browser
- {"/ipfs/?uri=ipfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"},
- {"/ipfs/?uri=ipns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/ipns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"},
- {"/ipfs/?uri=ipfs://" + 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=ipfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"},
- {"/ipns/?uri=ipns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/ipns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"},
- {"/ipns?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/?uri=ipns://" + cid},
- {"/ipns/?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/" + cid},
- {"/ipns/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid},
- {"/ipfs/?uri=unsupported://" + cid, http.StatusBadRequest, ""},
- {"/ipfs/?uri=invaliduri", 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)
- if err != nil {
- t.Fatal(err)
- }
- 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)
-
- k, err := api.Unixfs().Add(ctx, files.NewBytesFile([]byte("fnord")))
- if err != nil {
- t.Fatal(err)
- }
- ns["/ipns/example.com"] = path.FromString(k.String())
- ns["/ipns/working.example.com"] = path.FromString(k.String())
- ns["/ipns/double.example.com"] = path.FromString("/ipns/working.example.com")
- ns["/ipns/triple.example.com"] = path.FromString("/ipns/double.example.com")
- ns["/ipns/broken.example.com"] = path.FromString("/ipns/" + k.Cid().String())
- // We picked .man because:
- // 1. It's a valid TLD.
- // 2. Go treats it as the file extension for "man" files (even though
- // nobody actually *uses* this extension, AFAIK).
- //
- // Unfortunately, this may not work on all platforms as file type
- // detection is platform dependent.
- ns["/ipns/example.man"] = path.FromString(k.String())
-
- t.Log(ts.URL)
- for i, test := range []struct {
- host string
- path string
- status int
- text string
- }{
- {"127.0.0.1:8080", "/", http.StatusNotFound, "404 page not found\n"},
- {"127.0.0.1:8080", "/" + k.Cid().String(), http.StatusNotFound, "404 page not found\n"},
- {"127.0.0.1:8080", k.String(), http.StatusOK, "fnord"},
- {"127.0.0.1:8080", "/ipns/nxdomain.example.com", http.StatusBadRequest, "ipfs resolve -r /ipns/nxdomain.example.com: " + namesys.ErrResolveFailed.Error() + "\n"},
- {"127.0.0.1:8080", "/ipns/%0D%0A%0D%0Ahello", http.StatusBadRequest, "ipfs resolve -r /ipns/\\r\\n\\r\\nhello: " + namesys.ErrResolveFailed.Error() + "\n"},
- {"127.0.0.1:8080", "/ipns/example.com", http.StatusOK, "fnord"},
- {"example.com", "/", http.StatusOK, "fnord"},
-
- {"working.example.com", "/", http.StatusOK, "fnord"},
- {"double.example.com", "/", http.StatusOK, "fnord"},
- {"triple.example.com", "/", http.StatusOK, "fnord"},
- {"working.example.com", k.String(), http.StatusNotFound, "ipfs resolve -r /ipns/working.example.com" + k.String() + ": no link named \"ipfs\" under " + k.Cid().String() + "\n"},
- {"broken.example.com", "/", http.StatusBadRequest, "ipfs resolve -r /ipns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"},
- {"broken.example.com", k.String(), http.StatusBadRequest, "ipfs resolve -r /ipns/broken.example.com" + k.String() + ": " + namesys.ErrResolveFailed.Error() + "\n"},
- // This test case ensures we don't treat the TLD as a file extension.
- {"example.man", "/", http.StatusOK, "fnord"},
- } {
- var c http.Client
- r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil)
- if err != nil {
- t.Fatal(err)
- }
- r.Host = test.host
- resp, err := c.Do(r)
-
- urlstr := "http://" + test.host + test.path
- if err != nil {
- t.Errorf("error requesting %s: %s", urlstr, err)
- continue
- }
- defer resp.Body.Close()
- contentType := resp.Header.Get("Content-Type")
- if contentType != "text/plain; charset=utf-8" {
- t.Errorf("expected content type to be text/plain, got %s", contentType)
- }
- body, err := io.ReadAll(resp.Body)
- if resp.StatusCode != test.status {
- t.Errorf("(%d) got %d, expected %d from %s", i, resp.StatusCode, test.status, urlstr)
- t.Errorf("Body: %s", body)
- continue
- }
- if err != nil {
- t.Fatalf("error reading response from %s: %s", urlstr, err)
- }
- if string(body) != test.text {
- t.Errorf("unexpected response body from %s: expected %q; got %q", urlstr, test.text, body)
- continue
- }
- }
-}
-
-func TestPretty404(t *testing.T) {
- ns := mockNamesys{}
- ts, api, ctx := newTestServerAndNode(t, ns)
-
- f1 := files.NewMapDirectory(map[string]files.Node{
- "ipfs-404.html": files.NewBytesFile([]byte("Custom 404")),
- "deeper": files.NewMapDirectory(map[string]files.Node{
- "ipfs-404.html": files.NewBytesFile([]byte("Deep custom 404")),
- }),
- })
-
- k, err := api.Unixfs().Add(ctx, f1)
- if err != nil {
- t.Fatal(err)
- }
-
- host := "example.net"
- ns["/ipns/"+host] = path.FromString(k.String())
-
- for _, test := range []struct {
- path string
- accept string
- status int
- text string
- }{
- {"/ipfs-404.html", "text/html", http.StatusOK, "Custom 404"},
- {"/nope", "text/html", http.StatusNotFound, "Custom 404"},
- {"/nope", "text/*", http.StatusNotFound, "Custom 404"},
- {"/nope", "*/*", http.StatusNotFound, "Custom 404"},
- {"/nope", "application/json", http.StatusNotFound, "ipfs resolve -r /ipns/example.net/nope: no link named \"nope\" under QmcmnF7XG5G34RdqYErYDwCKNFQ6jb8oKVR21WAJgubiaj\n"},
- {"/deeper/nope", "text/html", http.StatusNotFound, "Deep custom 404"},
- {"/deeper/", "text/html", http.StatusOK, ""},
- {"/deeper", "text/html", http.StatusOK, ""},
- {"/nope/nope", "text/html", http.StatusNotFound, "Custom 404"},
- } {
- var c http.Client
- req, err := http.NewRequest("GET", ts.URL+test.path, nil)
- if err != nil {
- t.Fatal(err)
- }
- req.Header.Add("Accept", test.accept)
- req.Host = host
- resp, err := c.Do(req)
-
- if err != nil {
- t.Fatalf("error requesting %s: %s", test.path, err)
- }
-
- defer resp.Body.Close()
- if resp.StatusCode != test.status {
- t.Fatalf("got %d, expected %d, from %s", resp.StatusCode, test.status, test.path)
- }
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("error reading response from %s: %s", test.path, err)
- }
-
- if test.text != "" && string(body) != test.text {
- t.Fatalf("unexpected response body from %s: got %q, expected %q", test.path, body, test.text)
- }
- }
-}
-
-func TestIPNSHostnameRedirect(t *testing.T) {
- ns := mockNamesys{}
- ts, api, ctx := newTestServerAndNode(t, ns)
- t.Logf("test server url: %s", ts.URL)
-
- // create /ipns/example.net/foo/index.html
-
- f1 := files.NewMapDirectory(map[string]files.Node{
- "_": files.NewBytesFile([]byte("_")),
- "foo": files.NewMapDirectory(map[string]files.Node{
- "index.html": files.NewBytesFile([]byte("_")),
- }),
- })
-
- k, err := api.Unixfs().Add(ctx, f1)
- if err != nil {
- t.Fatal(err)
- }
-
- t.Logf("k: %s\n", k)
- ns["/ipns/example.net"] = path.FromString(k.String())
-
- // make request to directory containing index.html
- req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo", nil)
- if err != nil {
- t.Fatal(err)
- }
- req.Host = "example.net"
-
- res, err := doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- // 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 {
- t.Errorf("location header not present")
- } else if hdr[0] != "/foo/" {
- t.Errorf("location header is %v, expected /foo/", hdr[0])
- }
-
- // make request with prefix to directory containing index.html
- req, err = http.NewRequest(http.MethodGet, ts.URL+"/foo", nil)
- if err != nil {
- t.Fatal(err)
- }
- req.Host = "example.net"
-
- res, err = doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- // 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 {
- t.Errorf("location header not present")
- } else if hdr[0] != "/foo/" {
- t.Errorf("location header is %v, expected /foo/", hdr[0])
- }
-
- // make sure /version isn't exposed
- req, err = http.NewRequest(http.MethodGet, ts.URL+"/version", nil)
- if err != nil {
- t.Fatal(err)
- }
- req.Host = "example.net"
-
- res, err = doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- if res.StatusCode != 404 {
- t.Fatalf("expected a 404 error, got: %s", res.Status)
- }
-}
-
-// Test directory listing on DNSLink website
-// (scenario when Host header is the same as URL hostname)
-// This is basic regression test: additional end-to-end tests
-// can be found in test/sharness/t0115-gateway-dir-listing.sh
-func TestIPNSHostnameBacklinks(t *testing.T) {
- ns := mockNamesys{}
- ts, api, ctx := newTestServerAndNode(t, ns)
- t.Logf("test server url: %s", ts.URL)
-
- f1 := files.NewMapDirectory(map[string]files.Node{
- "file.txt": files.NewBytesFile([]byte("1")),
- "foo? #<'": files.NewMapDirectory(map[string]files.Node{
- "file.txt": files.NewBytesFile([]byte("2")),
- "bar": files.NewMapDirectory(map[string]files.Node{
- "file.txt": files.NewBytesFile([]byte("3")),
- }),
- }),
- })
-
- // create /ipns/example.net/foo/
- k, err := api.Unixfs().Add(ctx, f1)
- if err != nil {
- t.Fatal(err)
- }
-
- k2, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'"))
- if err != nil {
- t.Fatal(err)
- }
-
- k3, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'/bar"))
- if err != nil {
- t.Fatal(err)
- }
-
- t.Logf("k: %s\n", k)
- ns["/ipns/example.net"] = path.FromString(k.String())
-
- // make request to directory listing
- req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo%3F%20%23%3C%27/", nil)
- if err != nil {
- t.Fatal(err)
- }
- req.Host = "example.net"
-
- res, err := doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- // expect correct links
- body, err := io.ReadAll(res.Body)
- if err != nil {
- t.Fatalf("error reading response: %s", err)
- }
- s := string(body)
- t.Logf("body: %s\n", string(body))
-
- if !matchPathOrBreadcrumbs(s, "/ipns/example.net/foo? #<'") {
- t.Fatalf("expected a path in directory listing")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected backlink in directory listing")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected file in directory listing")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected no backlink in directory listing of the root CID")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected file in directory listing")
- }
- if !strings.Contains(s, "example.net/foo? #<'/bar") {
- t.Fatalf("expected a path in directory listing")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected backlink in directory listing")
- }
- if !strings.Contains(s, "") {
- t.Fatalf("expected file in directory listing")
- }
- if !strings.Contains(s, k3.Cid().String()) {
- t.Fatalf("expected hash in directory listing")
- }
-}
-
-func TestCacheControlImmutable(t *testing.T) {
- ts, _, _ := newTestServerAndNode(t, nil)
- t.Logf("test server url: %s", ts.URL)
-
- req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"/", nil)
- if err != nil {
- t.Fatal(err)
- }
-
- res, err := doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- // check the immutable tag isn't set
- hdrs, ok := res.Header["Cache-Control"]
- if ok {
- for _, hdr := range hdrs {
- if strings.Contains(hdr, "immutable") {
- t.Fatalf("unexpected Cache-Control: immutable on directory listing: %s", hdr)
- }
- }
- }
-}
-
-func TestGoGetSupport(t *testing.T) {
- ts, _, _ := newTestServerAndNode(t, nil)
- t.Logf("test server url: %s", ts.URL)
-
- // mimic go-get
- req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"?go-get=1", nil)
- if err != nil {
- t.Fatal(err)
- }
-
- res, err := doWithoutRedirect(req)
- if err != nil {
- t.Fatal(err)
- }
-
- if res.StatusCode != 200 {
- t.Errorf("status is %d, expected 200", res.StatusCode)
- }
-}
-
func TestVersion(t *testing.T) {
version.CurrentCommit = "theshortcommithash"