1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-20 16:36:46 +08:00
Files
kubo/test/cli/gateway_test.go
2023-10-18 10:23:50 +02:00

645 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cli
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"github.com/ipfs/kubo/config"
"github.com/ipfs/kubo/test/cli/harness"
. "github.com/ipfs/kubo/test/cli/testutils"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/multiformats/go-multibase"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGateway(t *testing.T) {
t.Parallel()
h := harness.NewT(t)
node := h.NewNode().Init().StartDaemon("--offline")
cid := node.IPFSAddStr("Hello Worlds!")
peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36)
assert.NoError(t, err)
client := node.GatewayClient()
client.TemplateData = map[string]string{
"CID": cid,
"PeerID": peerID,
}
t.Run("GET IPFS path succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.CID}}")
assert.Equal(t, 200, resp.StatusCode)
})
t.Run("GET IPFS path with explicit ?filename succeeds with proper header", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.CID}}?filename=testтест.pdf")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t,
`inline; filename="test____.pdf"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.pdf`,
resp.Headers.Get("Content-Disposition"),
)
})
t.Run("GET IPFS path with explicit ?filename and &download=true succeeds with proper header", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.CID}}?filename=testтест.mp4&download=true")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t,
`attachment; filename="test____.mp4"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.mp4`,
resp.Headers.Get("Content-Disposition"),
)
})
// https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616
t.Run("GET for Server Worker registration outside of an IPFS content root errors", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.CID}}?filename=sw.js", client.WithHeader("Service-Worker", "script"))
assert.Equal(t, 400, resp.StatusCode)
assert.Contains(t, resp.Body, "navigator.serviceWorker: registration is not allowed for this scope")
})
t.Run("GET IPFS directory path succeeds", func(t *testing.T) {
t.Parallel()
client := node.GatewayClient().DisableRedirects()
pageContents := "hello i am a webpage"
fileContents := "12345"
h.WriteFile("dir/test", fileContents)
h.WriteFile("dir/dirwithindex/index.html", pageContents)
cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "dir")).Stdout.Lines()
rootCID := cids[len(cids)-1]
client.TemplateData = map[string]string{
"IndexFileCID": cids[0],
"TestFileCID": cids[1],
"RootCID": rootCID,
}
t.Run("GET IPFS the index file CID", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.IndexFileCID}}")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, pageContents, resp.Body)
})
t.Run("GET IPFS the test file CID", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.TestFileCID}}")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, fileContents, resp.Body)
})
t.Run("GET IPFS directory with index.html returns redirect to add trailing slash", func(t *testing.T) {
t.Parallel()
resp := client.Head("/ipfs/{{.RootCID}}/dirwithindex?query=to-remember")
assert.Equal(t, 301, resp.StatusCode)
assert.Equal(t,
fmt.Sprintf("/ipfs/%s/dirwithindex/?query=to-remember", rootCID),
resp.Headers.Get("Location"),
)
})
// This enables go get to parse go-import meta tags from index.html files stored in IPFS
// https://github.com/ipfs/kubo/pull/3963
t.Run("GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex?go-get=1")
assert.Equal(t, pageContents, resp.Body)
})
t.Run("GET IPFS directory with index.html and trailing slash returns expected output", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex/?query=to-remember")
assert.Equal(t, pageContents, resp.Body)
})
t.Run("GET IPFS nonexistent file returns 404 (Not Found)", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/{{.RootCID}}/pleaseDontAddMe")
assert.Equal(t, 404, resp.StatusCode)
})
t.Run("GET IPFS invalid CID returns 400 (Bad Request)", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/QmInvalid/pleaseDontAddMe")
assert.Equal(t, 400, resp.StatusCode)
})
t.Run("GET IPFS inlined zero-length data object returns ok code (200)", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/bafkqaaa")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
assert.Equal(t, "", resp.Body)
})
t.Run("GET IPFS inlined zero-length data object with byte range returns ok code (200)", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/bafkqaaa", client.WithHeader("Range", "bytes=0-1048575"))
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
assert.Equal(t, "text/plain", resp.Resp.Header.Get("Content-Type"))
})
t.Run("GET /ipfs/ipfs/{cid} returns redirect to the valid path", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/ipfs/bafkqaaa?query=to-remember")
assert.Contains(t,
resp.Body,
`<meta http-equiv="refresh" content="10;url=/ipfs/bafkqaaa?query=to-remember" />`,
)
assert.Contains(t,
resp.Body,
`<link rel="canonical" href="/ipfs/bafkqaaa?query=to-remember" />`,
)
})
})
t.Run("IPNS", func(t *testing.T) {
t.Parallel()
node.IPFS("name", "publish", "--allow-offline", "--ttl", "42h", cid)
t.Run("GET invalid IPNS root returns 500 (Internal Server Error)", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipns/QmInvalid/pleaseDontAddMe")
assert.Equal(t, 500, resp.StatusCode)
})
t.Run("GET IPNS path succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipns/{{.PeerID}}")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "Hello Worlds!", resp.Body)
})
t.Run("GET IPNS path has correct Cache-Control", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipns/{{.PeerID}}")
assert.Equal(t, 200, resp.StatusCode)
cacheControl := resp.Headers.Get("Cache-Control")
assert.True(t, strings.HasPrefix(cacheControl, "public, max-age="))
maxAge, err := strconv.Atoi(strings.TrimPrefix(cacheControl, "public, max-age="))
assert.NoError(t, err)
assert.True(t, maxAge-151200 < 60) // MaxAge within 42h and 42h-1m
})
t.Run("GET /ipfs/ipns/{peerid} returns redirect to the valid path", func(t *testing.T) {
t.Parallel()
resp := client.Get("/ipfs/ipns/{{.PeerID}}?query=to-remember")
assert.Contains(t,
resp.Body,
fmt.Sprintf(`<meta http-equiv="refresh" content="10;url=/ipns/%s?query=to-remember" />`, peerID),
)
assert.Contains(t,
resp.Body,
fmt.Sprintf(`<link rel="canonical" href="/ipns/%s?query=to-remember" />`, peerID),
)
})
})
t.Run("GET invalid IPFS path errors", func(t *testing.T) {
t.Parallel()
assert.Equal(t, 400, client.Get("/ipfs/12345").StatusCode)
})
t.Run("GET invalid path errors", func(t *testing.T) {
t.Parallel()
assert.Equal(t, 404, client.Get("/12345").StatusCode)
})
// TODO: these tests that use the API URL shouldn't be part of gateway tests...
t.Run("GET /webui returns 301 or 302", func(t *testing.T) {
t.Parallel()
resp := node.APIClient().DisableRedirects().Get("/webui")
assert.Contains(t, []int{302, 301}, resp.StatusCode)
})
t.Run("GET /webui/ returns 301 or 302", func(t *testing.T) {
t.Parallel()
resp := node.APIClient().DisableRedirects().Get("/webui/")
assert.Contains(t, []int{302, 301}, resp.StatusCode)
})
t.Run("GET /webui/ returns user-specified headers", func(t *testing.T) {
t.Parallel()
header := "Access-Control-Allow-Origin"
values := []string{"http://localhost:3000", "https://webui.ipfs.io"}
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.API.HTTPHeaders = map[string][]string{header: values}
})
node.StartDaemon()
resp := node.APIClient().DisableRedirects().Get("/webui/")
assert.Equal(t, resp.Headers.Values(header), values)
assert.Contains(t, []int{302, 301}, resp.StatusCode)
})
t.Run("GET /logs returns logs", func(t *testing.T) {
t.Parallel()
apiClient := node.APIClient()
reqURL := apiClient.BuildURL("/logs")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
require.NoError(t, err)
resp, err := apiClient.Client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
// read the first line of the output and parse its JSON
dec := json.NewDecoder(resp.Body)
event := struct{ Event string }{}
err = dec.Decode(&event)
require.NoError(t, err)
assert.Equal(t, "log API client connected", event.Event)
})
t.Run("POST /api/v0/version succeeds", func(t *testing.T) {
t.Parallel()
resp := node.APIClient().Post("/api/v0/version", nil)
assert.Equal(t, 200, resp.StatusCode)
assert.Len(t, resp.Resp.TransferEncoding, 1)
assert.Equal(t, "chunked", resp.Resp.TransferEncoding[0])
vers := struct{ Version string }{}
err := json.Unmarshal([]byte(resp.Body), &vers)
require.NoError(t, err)
assert.NotEmpty(t, vers.Version)
})
t.Run("pprof", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon()
apiClient := node.APIClient()
t.Run("mutex", func(t *testing.T) {
t.Parallel()
t.Run("setting the mutex fraction works (negative so it doesn't enable)", func(t *testing.T) {
t.Parallel()
resp := apiClient.Post("/debug/pprof-mutex/?fraction=-1", nil)
assert.Equal(t, 200, resp.StatusCode)
})
t.Run("mutex endpoint doesn't accept a string as an argument", func(t *testing.T) {
t.Parallel()
resp := apiClient.Post("/debug/pprof-mutex/?fraction=that_is_a_string", nil)
assert.Equal(t, 400, resp.StatusCode)
})
t.Run("mutex endpoint returns 405 on GET", func(t *testing.T) {
t.Parallel()
resp := apiClient.Get("/debug/pprof-mutex/?fraction=-1")
assert.Equal(t, 405, resp.StatusCode)
})
})
t.Run("block", func(t *testing.T) {
t.Parallel()
t.Run("setting the block profiler rate works (0 so it doesn't enable)", func(t *testing.T) {
t.Parallel()
resp := apiClient.Post("/debug/pprof-block/?rate=0", nil)
assert.Equal(t, 200, resp.StatusCode)
})
t.Run("block profiler endpoint doesn't accept a string as an argument", func(t *testing.T) {
t.Parallel()
resp := apiClient.Post("/debug/pprof-block/?rate=that_is_a_string", nil)
assert.Equal(t, 400, resp.StatusCode)
})
t.Run("block profiler endpoint returns 405 on GET", func(t *testing.T) {
t.Parallel()
resp := apiClient.Get("/debug/pprof-block/?rate=0")
assert.Equal(t, 405, resp.StatusCode)
})
})
})
t.Run("index content types", func(t *testing.T) {
t.Parallel()
h := harness.NewT(t)
node := h.NewNode().Init().StartDaemon()
h.WriteFile("index/index.html", "<p></p>")
cid := node.IPFS("add", "-Q", "-r", filepath.Join(h.Dir, "index")).Stderr.Trimmed()
apiClient := node.APIClient()
apiClient.TemplateData = map[string]string{"CID": cid}
t.Run("GET index.html has correct content type", func(t *testing.T) {
t.Parallel()
res := apiClient.Get("/ipfs/{{.CID}}/")
assert.Equal(t, "text/html; charset=utf-8", res.Resp.Header.Get("Content-Type"))
})
t.Run("HEAD index.html has no content", func(t *testing.T) {
t.Parallel()
res := apiClient.Head("/ipfs/{{.CID}}/")
assert.Equal(t, "", res.Body)
assert.Equal(t, "", res.Resp.Header.Get("Content-Length"))
})
})
t.Run("readonly API", func(t *testing.T) {
t.Parallel()
client := node.GatewayClient()
fileContents := "12345"
h.WriteFile("readonly/dir/test", fileContents)
cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "readonly/dir")).Stdout.Lines()
rootCID := cids[len(cids)-1]
client.TemplateData = map[string]string{"RootCID": rootCID}
t.Run("Get IPFS directory file through readonly API succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/api/v0/cat?arg={{.RootCID}}/test")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, fileContents, resp.Body)
})
t.Run("refs IPFS directory file through readonly API succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/api/v0/refs?arg={{.RootCID}}/test")
assert.Equal(t, 200, resp.StatusCode)
})
t.Run("test gateway API is sanitized", func(t *testing.T) {
t.Parallel()
for _, cmd := range []string{
"add",
"block/put",
"bootstrap",
"config",
"dag/put",
"dag/import",
"dht",
"diag",
"id",
"mount",
"name/publish",
"object/put",
"object/new",
"object/patch",
"pin",
"ping",
"repo",
"stats",
"swarm",
"file",
"update",
"bitswap",
} {
t.Run(cmd, func(t *testing.T) {
cmd := cmd
t.Parallel()
assert.Equal(t, 404, client.Get("/api/v0/"+cmd).StatusCode)
})
}
})
})
t.Run("refs/local", func(t *testing.T) {
t.Parallel()
gatewayAddr := URLStrToMultiaddr(node.GatewayURL())
res := node.RunIPFS("--api", gatewayAddr.String(), "refs", "local")
assert.Contains(t,
res.Stderr.Trimmed(),
`Error: invalid path "local":`,
)
})
t.Run("raw leaves node", func(t *testing.T) {
t.Parallel()
contents := "This is RAW!"
cid := node.IPFSAddStr(contents, "--raw-leaves")
assert.Equal(t, contents, client.Get("/ipfs/"+cid).Body)
})
t.Run("compact blocks", func(t *testing.T) {
t.Parallel()
block1 := "\x0a\x09\x08\x02\x12\x03\x66\x6f\x6f\x18\x03"
block2 := "\x0a\x04\x08\x02\x18\x06\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c" +
"\x8b\x05\xeb\x66\x2b\xe0\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65" +
"\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c\x8b\x05\xeb\x66\x2b\xe0" +
"\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65"
node.PipeStrToIPFS(block1, "block", "put")
block2CID := node.PipeStrToIPFS(block2, "block", "put", "--cid-codec=dag-pb").Stdout.Trimmed()
resp := client.Get("/ipfs/" + block2CID)
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "foofoo", resp.Body)
})
t.Run("verify gateway file", func(t *testing.T) {
t.Parallel()
r := regexp.MustCompile(`Gateway server listening on (?P<addr>.+)\s`)
matches := r.FindStringSubmatch(node.Daemon.Stdout.String())
ma, err := multiaddr.NewMultiaddr(matches[1])
require.NoError(t, err)
netAddr, err := manet.ToNetAddr(ma)
require.NoError(t, err)
expURL := "http://" + netAddr.String()
b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
require.NoError(t, err)
assert.Equal(t, expURL, string(b))
})
t.Run("verify gateway file diallable while on unspecified", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.Addresses.Gateway = config.Strings{"/ip4/127.0.0.1/tcp/32563"}
})
node.StartDaemon()
b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
require.NoError(t, err)
assert.Equal(t, "http://127.0.0.1:32563", string(b))
})
t.Run("NoFetch", func(t *testing.T) {
t.Parallel()
nodes := harness.NewT(t).NewNodes(2).Init()
node1 := nodes[0]
node2 := nodes[1]
node1.UpdateConfig(func(cfg *config.Config) {
cfg.Gateway.NoFetch = true
})
node2PeerID, err := peer.ToCid(node2.PeerID()).StringOfBase(multibase.Base36)
assert.NoError(t, err)
nodes.StartDaemons().Connect()
t.Run("not present", func(t *testing.T) {
cidFoo := node2.IPFSAddStr("foo")
t.Run("not present key from node 1", func(t *testing.T) {
t.Parallel()
assert.Equal(t, 500, node1.GatewayClient().Get("/ipfs/"+cidFoo).StatusCode)
})
t.Run("not present IPNS key from node 1", func(t *testing.T) {
t.Parallel()
assert.Equal(t, 500, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode)
})
})
t.Run("present", func(t *testing.T) {
cidBar := node1.IPFSAddStr("bar")
t.Run("present key from node 1", func(t *testing.T) {
t.Parallel()
assert.Equal(t, 200, node1.GatewayClient().Get("/ipfs/"+cidBar).StatusCode)
})
t.Run("present IPNS key from node 1", func(t *testing.T) {
t.Parallel()
node2.IPFS("name", "publish", "/ipfs/"+cidBar)
assert.Equal(t, 200, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode)
})
})
})
t.Run("DeserializedResponses", func(t *testing.T) {
type testCase struct {
globalValue config.Flag
gatewayValue config.Flag
deserializedGlobalStatusCode int
deserializedGatewayStaticCode int
message string
}
setHost := func(r *http.Request) {
r.Host = "example.com"
}
withAccept := func(accept string) func(r *http.Request) {
return func(r *http.Request) {
r.Header.Set("Accept", accept)
}
}
withHostAndAccept := func(accept string) func(r *http.Request) {
return func(r *http.Request) {
setHost(r)
withAccept(accept)(r)
}
}
makeTest := func(test *testCase) func(t *testing.T) {
return func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.Gateway.DeserializedResponses = test.globalValue
cfg.Gateway.PublicGateways = map[string]*config.GatewaySpec{
"example.com": {
Paths: []string{"/ipfs", "/ipns"},
DeserializedResponses: test.gatewayValue,
},
}
})
node.StartDaemon()
cidFoo := node.IPFSAddStr("foo")
client := node.GatewayClient()
deserializedPath := "/ipfs/" + cidFoo
blockPath := deserializedPath + "?format=raw"
carPath := deserializedPath + "?format=car"
// Global Check (Gateway.DeserializedResponses)
assert.Equal(t, http.StatusOK, client.Get(blockPath).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.raw")).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(carPath).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.car")).StatusCode)
assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath).StatusCode)
assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath, withAccept("application/json")).StatusCode)
// Public Gateway (example.com) Check (Gateway.PublicGateways[example.com].DeserializedResponses)
assert.Equal(t, http.StatusOK, client.Get(blockPath, setHost).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.raw")).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(carPath, setHost).StatusCode)
assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.car")).StatusCode)
assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, setHost).StatusCode)
assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, withHostAndAccept("application/json")).StatusCode)
}
}
for _, test := range []*testCase{
{config.True, config.Default, http.StatusOK, http.StatusOK, "when Gateway.DeserializedResponses is globally enabled, leaving implicit default for Gateway.PublicGateways[example.com] should inherit the global setting (enabled)"},
{config.False, config.Default, http.StatusNotAcceptable, http.StatusNotAcceptable, "when Gateway.DeserializedResponses is globally disabled, leaving implicit default on Gateway.PublicGateways[example.com] should inherit the global setting (disabled)"},
{config.False, config.True, http.StatusNotAcceptable, http.StatusOK, "when Gateway.DeserializedResponses is globally disabled, explicitly enabling on Gateway.PublicGateways[example.com] should override global (enabled)"},
{config.True, config.False, http.StatusOK, http.StatusNotAcceptable, "when Gateway.DeserializedResponses is globally enabled, explicitly disabling on Gateway.PublicGateways[example.com] should override global (disabled)"},
} {
t.Run(test.message, makeTest(test))
}
})
t.Run("DisableHTMLErrors", func(t *testing.T) {
t.Parallel()
t.Run("Returns HTML error without DisableHTMLErrors, Accept contains text/html", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init()
node.StartDaemon()
client := node.GatewayClient()
res := client.Get("/ipfs/invalid-thing", func(r *http.Request) {
r.Header.Set("Accept", "text/html")
})
assert.NotEqual(t, http.StatusOK, res.StatusCode)
assert.Contains(t, res.Resp.Header.Get("Content-Type"), "text/html")
})
t.Run("Does not return HTML error with DisableHTMLErrors enabled, and Accept contains text/html", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.Gateway.DisableHTMLErrors = config.True
})
node.StartDaemon()
client := node.GatewayClient()
res := client.Get("/ipfs/invalid-thing", func(r *http.Request) {
r.Header.Set("Accept", "text/html")
})
assert.NotEqual(t, http.StatusOK, res.StatusCode)
assert.NotContains(t, res.Resp.Header.Get("Content-Type"), "text/html")
})
})
}