master
go 592 lines 20.2 KB
Raw
1 package cli
2
3 import (
4 "bufio"
5 "context"
6 "encoding/json"
7 "fmt"
8 "net/http"
9 "os"
10 "path/filepath"
11 "regexp"
12 "strconv"
13 "strings"
14 "testing"
15 "time"
16
17 "github.com/ipfs/kubo/config"
18 "github.com/ipfs/kubo/test/cli/harness"
19 "github.com/libp2p/go-libp2p/core/peer"
20 "github.com/multiformats/go-multiaddr"
21 manet "github.com/multiformats/go-multiaddr/net"
22 "github.com/multiformats/go-multibase"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
25 )
26
27 func TestGateway(t *testing.T) {
28 t.Parallel()
29 h := harness.NewT(t)
30 node := h.NewNode().Init().StartDaemon("--offline")
31 t.Cleanup(func() { node.StopDaemon() })
32 cid := node.IPFSAddStr("Hello Worlds!")
33
34 peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36)
35 assert.NoError(t, err)
36
37 client := node.GatewayClient()
38 client.TemplateData = map[string]string{
39 "CID": cid,
40 "PeerID": peerID,
41 }
42
43 t.Run("GET IPFS path succeeds", func(t *testing.T) {
44 t.Parallel()
45 resp := client.Get("/ipfs/{{.CID}}")
46 assert.Equal(t, 200, resp.StatusCode)
47 })
48
49 t.Run("GET IPFS path with explicit ?filename succeeds with proper header", func(t *testing.T) {
50 t.Parallel()
51 resp := client.Get("/ipfs/{{.CID}}?filename=testтест.pdf")
52 assert.Equal(t, 200, resp.StatusCode)
53 assert.Equal(t,
54 `inline; filename="test____.pdf"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.pdf`,
55 resp.Headers.Get("Content-Disposition"),
56 )
57 })
58
59 t.Run("GET IPFS path with explicit ?filename and &download=true succeeds with proper header", func(t *testing.T) {
60 t.Parallel()
61 resp := client.Get("/ipfs/{{.CID}}?filename=testтест.mp4&download=true")
62 assert.Equal(t, 200, resp.StatusCode)
63 assert.Equal(t,
64 `attachment; filename="test____.mp4"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.mp4`,
65 resp.Headers.Get("Content-Disposition"),
66 )
67 })
68
69 // https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616
70 t.Run("GET for Server Worker registration outside of an IPFS content root errors", func(t *testing.T) {
71 t.Parallel()
72 resp := client.Get("/ipfs/{{.CID}}?filename=sw.js", client.WithHeader("Service-Worker", "script"))
73 assert.Equal(t, 400, resp.StatusCode)
74 assert.Contains(t, resp.Body, "navigator.serviceWorker: registration is not allowed for this scope")
75 })
76
77 t.Run("GET IPFS directory path succeeds", func(t *testing.T) {
78 t.Parallel()
79 client := node.GatewayClient().DisableRedirects()
80
81 pageContents := "hello i am a webpage"
82 fileContents := "12345"
83 h.WriteFile("dir/test", fileContents)
84 h.WriteFile("dir/dirwithindex/index.html", pageContents)
85 cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "dir")).Stdout.Lines()
86
87 rootCID := cids[len(cids)-1]
88 client.TemplateData = map[string]string{
89 "IndexFileCID": cids[0],
90 "TestFileCID": cids[1],
91 "RootCID": rootCID,
92 }
93
94 t.Run("GET IPFS the index file CID", func(t *testing.T) {
95 t.Parallel()
96 resp := client.Get("/ipfs/{{.IndexFileCID}}")
97 assert.Equal(t, 200, resp.StatusCode)
98 assert.Equal(t, pageContents, resp.Body)
99 })
100
101 t.Run("GET IPFS the test file CID", func(t *testing.T) {
102 t.Parallel()
103 resp := client.Get("/ipfs/{{.TestFileCID}}")
104 assert.Equal(t, 200, resp.StatusCode)
105 assert.Equal(t, fileContents, resp.Body)
106 })
107
108 t.Run("GET IPFS directory with index.html returns redirect to add trailing slash", func(t *testing.T) {
109 t.Parallel()
110 resp := client.Head("/ipfs/{{.RootCID}}/dirwithindex?query=to-remember")
111 assert.Equal(t, 301, resp.StatusCode)
112 assert.Equal(t,
113 fmt.Sprintf("/ipfs/%s/dirwithindex/?query=to-remember", rootCID),
114 resp.Headers.Get("Location"),
115 )
116 })
117
118 // This enables go get to parse go-import meta tags from index.html files stored in IPFS
119 // https://github.com/ipfs/kubo/pull/3963
120 t.Run("GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed", func(t *testing.T) {
121 t.Parallel()
122 resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex?go-get=1")
123 assert.Equal(t, pageContents, resp.Body)
124 })
125
126 t.Run("GET IPFS directory with index.html and trailing slash returns expected output", func(t *testing.T) {
127 t.Parallel()
128 resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex/?query=to-remember")
129 assert.Equal(t, pageContents, resp.Body)
130 })
131
132 t.Run("GET IPFS nonexistent file returns 404 (Not Found)", func(t *testing.T) {
133 t.Parallel()
134 resp := client.Get("/ipfs/{{.RootCID}}/pleaseDontAddMe")
135 assert.Equal(t, 404, resp.StatusCode)
136 })
137
138 t.Run("GET IPFS invalid CID returns 400 (Bad Request)", func(t *testing.T) {
139 t.Parallel()
140 resp := client.Get("/ipfs/QmInvalid/pleaseDontAddMe")
141 assert.Equal(t, 400, resp.StatusCode)
142 })
143
144 t.Run("GET IPFS inlined zero-length data object returns ok code (200)", func(t *testing.T) {
145 t.Parallel()
146 resp := client.Get("/ipfs/bafkqaaa")
147 assert.Equal(t, 200, resp.StatusCode)
148 assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
149 assert.Equal(t, "", resp.Body)
150 })
151
152 t.Run("GET IPFS inlined zero-length data object with byte range returns ok code (200)", func(t *testing.T) {
153 t.Parallel()
154 resp := client.Get("/ipfs/bafkqaaa", client.WithHeader("Range", "bytes=0-1048575"))
155 assert.Equal(t, 200, resp.StatusCode)
156 assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
157 assert.Equal(t, "text/plain", resp.Resp.Header.Get("Content-Type"))
158 })
159
160 t.Run("GET /ipfs/ipfs/{cid} returns redirect to the valid path", func(t *testing.T) {
161 t.Parallel()
162 resp := client.Get("/ipfs/ipfs/bafkqaaa?query=to-remember")
163 assert.Equal(t, 301, resp.StatusCode)
164 assert.Equal(t, "/ipfs/bafkqaaa?query=to-remember", resp.Resp.Header.Get("Location"))
165 })
166 })
167
168 t.Run("IPNS", func(t *testing.T) {
169 t.Parallel()
170 node.IPFS("name", "publish", "--allow-offline", "--ttl", "42h", cid)
171
172 t.Run("GET invalid IPNS root returns 500 (Internal Server Error)", func(t *testing.T) {
173 t.Parallel()
174 resp := client.Get("/ipns/QmInvalid/pleaseDontAddMe")
175 assert.Equal(t, 500, resp.StatusCode)
176 })
177
178 t.Run("GET IPNS path succeeds", func(t *testing.T) {
179 t.Parallel()
180 resp := client.Get("/ipns/{{.PeerID}}")
181 assert.Equal(t, 200, resp.StatusCode)
182 assert.Equal(t, "Hello Worlds!", resp.Body)
183 })
184
185 t.Run("GET IPNS path has correct Cache-Control", func(t *testing.T) {
186 t.Parallel()
187 resp := client.Get("/ipns/{{.PeerID}}")
188 assert.Equal(t, 200, resp.StatusCode)
189 cacheControl := resp.Headers.Get("Cache-Control")
190 assert.True(t, strings.HasPrefix(cacheControl, "public, max-age="))
191 maxAge, err := strconv.Atoi(strings.TrimPrefix(cacheControl, "public, max-age="))
192 assert.NoError(t, err)
193 assert.True(t, maxAge-151200 < 60) // MaxAge within 42h and 42h-1m
194 })
195
196 t.Run("GET /ipfs/ipns/{peerid} returns redirect to the valid path", func(t *testing.T) {
197 t.Parallel()
198 resp := client.Get("/ipfs/ipns/{{.PeerID}}?query=to-remember")
199 assert.Equal(t, 301, resp.StatusCode)
200 assert.Equal(t, fmt.Sprintf("/ipns/%s?query=to-remember", peerID), resp.Resp.Header.Get("Location"))
201 })
202 })
203
204 t.Run("GET invalid IPFS path errors", func(t *testing.T) {
205 t.Parallel()
206 assert.Equal(t, 400, client.Get("/ipfs/12345").StatusCode)
207 })
208
209 t.Run("GET invalid path errors", func(t *testing.T) {
210 t.Parallel()
211 assert.Equal(t, 404, client.Get("/12345").StatusCode)
212 })
213
214 // TODO: these tests that use the API URL shouldn't be part of gateway tests...
215 t.Run("GET /webui returns 301 or 302", func(t *testing.T) {
216 t.Parallel()
217 resp := node.APIClient().DisableRedirects().Get("/webui")
218 assert.Contains(t, []int{302, 301, 307, 308}, resp.StatusCode)
219 })
220
221 t.Run("GET /webui/ returns 301 or 302", func(t *testing.T) {
222 t.Parallel()
223 resp := node.APIClient().DisableRedirects().Get("/webui/")
224 assert.Contains(t, []int{302, 301, 307, 308}, resp.StatusCode)
225 })
226
227 t.Run("GET /webui/ returns user-specified headers", func(t *testing.T) {
228 t.Parallel()
229
230 header := "Access-Control-Allow-Origin"
231 values := []string{"http://localhost:3000", "https://webui.ipfs.io"}
232
233 node := harness.NewT(t).NewNode().Init()
234 node.UpdateConfig(func(cfg *config.Config) {
235 cfg.API.HTTPHeaders = map[string][]string{header: values}
236 })
237 node.StartDaemon()
238 defer node.StopDaemon()
239
240 resp := node.APIClient().DisableRedirects().Get("/webui/")
241 assert.Equal(t, resp.Headers.Values(header), values)
242 assert.Contains(t, []int{302, 301}, resp.StatusCode)
243 })
244
245 t.Run("POST /api/v0/version succeeds", func(t *testing.T) {
246 t.Parallel()
247 resp := node.APIClient().Post("/api/v0/version", nil)
248 assert.Equal(t, 200, resp.StatusCode)
249
250 assert.Len(t, resp.Resp.TransferEncoding, 1)
251 assert.Equal(t, "chunked", resp.Resp.TransferEncoding[0])
252
253 vers := struct{ Version string }{}
254 err := json.Unmarshal([]byte(resp.Body), &vers)
255 require.NoError(t, err)
256 assert.NotEmpty(t, vers.Version)
257 })
258
259 t.Run("pprof", func(t *testing.T) {
260 t.Parallel()
261 node := harness.NewT(t).NewNode().Init().StartDaemon()
262 t.Cleanup(func() { node.StopDaemon() })
263 apiClient := node.APIClient()
264 t.Run("mutex", func(t *testing.T) {
265 t.Parallel()
266 t.Run("setting the mutex fraction works (negative so it doesn't enable)", func(t *testing.T) {
267 t.Parallel()
268 resp := apiClient.Post("/debug/pprof-mutex/?fraction=-1", nil)
269 assert.Equal(t, 200, resp.StatusCode)
270 })
271 t.Run("mutex endpoint doesn't accept a string as an argument", func(t *testing.T) {
272 t.Parallel()
273 resp := apiClient.Post("/debug/pprof-mutex/?fraction=that_is_a_string", nil)
274 assert.Equal(t, 400, resp.StatusCode)
275 })
276 t.Run("mutex endpoint returns 405 on GET", func(t *testing.T) {
277 t.Parallel()
278 resp := apiClient.Get("/debug/pprof-mutex/?fraction=-1")
279 assert.Equal(t, 405, resp.StatusCode)
280 })
281 })
282 t.Run("block", func(t *testing.T) {
283 t.Parallel()
284 t.Run("setting the block profiler rate works (0 so it doesn't enable)", func(t *testing.T) {
285 t.Parallel()
286 resp := apiClient.Post("/debug/pprof-block/?rate=0", nil)
287 assert.Equal(t, 200, resp.StatusCode)
288 })
289 t.Run("block profiler endpoint doesn't accept a string as an argument", func(t *testing.T) {
290 t.Parallel()
291 resp := apiClient.Post("/debug/pprof-block/?rate=that_is_a_string", nil)
292 assert.Equal(t, 400, resp.StatusCode)
293 })
294 t.Run("block profiler endpoint returns 405 on GET", func(t *testing.T) {
295 t.Parallel()
296 resp := apiClient.Get("/debug/pprof-block/?rate=0")
297 assert.Equal(t, 405, resp.StatusCode)
298 })
299 })
300 })
301
302 t.Run("index content types", func(t *testing.T) {
303 t.Parallel()
304 h := harness.NewT(t)
305 node := h.NewNode().Init().StartDaemon()
306 t.Cleanup(func() { node.StopDaemon() })
307
308 h.WriteFile("index/index.html", "<p></p>")
309 cid := node.IPFS("add", "-Q", "-r", filepath.Join(h.Dir, "index")).Stderr.Trimmed()
310
311 apiClient := node.APIClient()
312 apiClient.TemplateData = map[string]string{"CID": cid}
313
314 t.Run("GET index.html has correct content type", func(t *testing.T) {
315 t.Parallel()
316 res := apiClient.Get("/ipfs/{{.CID}}/")
317 assert.Equal(t, "text/html; charset=utf-8", res.Resp.Header.Get("Content-Type"))
318 })
319
320 t.Run("HEAD index.html has no content", func(t *testing.T) {
321 t.Parallel()
322 res := apiClient.Head("/ipfs/{{.CID}}/")
323 assert.Equal(t, "", res.Body)
324 assert.Equal(t, "", res.Resp.Header.Get("Content-Length"))
325 })
326 })
327
328 t.Run("raw leaves node", func(t *testing.T) {
329 t.Parallel()
330 contents := "This is RAW!"
331 cid := node.IPFSAddStr(contents, "--raw-leaves")
332 assert.Equal(t, contents, client.Get("/ipfs/"+cid).Body)
333 })
334
335 t.Run("compact blocks", func(t *testing.T) {
336 t.Parallel()
337 block1 := "\x0a\x09\x08\x02\x12\x03\x66\x6f\x6f\x18\x03"
338 block2 := "\x0a\x04\x08\x02\x18\x06\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c" +
339 "\x8b\x05\xeb\x66\x2b\xe0\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65" +
340 "\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c\x8b\x05\xeb\x66\x2b\xe0" +
341 "\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65"
342
343 node.PipeStrToIPFS(block1, "block", "put")
344 block2CID := node.PipeStrToIPFS(block2, "block", "put", "--cid-codec=dag-pb").Stdout.Trimmed()
345
346 resp := client.Get("/ipfs/" + block2CID)
347 assert.Equal(t, 200, resp.StatusCode)
348 assert.Equal(t, "foofoo", resp.Body)
349 })
350
351 t.Run("verify gateway file", func(t *testing.T) {
352 t.Parallel()
353 r := regexp.MustCompile(`Gateway server listening on (?P<addr>.+)\s`)
354 matches := r.FindStringSubmatch(node.Daemon.Stdout.String())
355 ma, err := multiaddr.NewMultiaddr(matches[1])
356 require.NoError(t, err)
357 netAddr, err := manet.ToNetAddr(ma)
358 require.NoError(t, err)
359 expURL := "http://" + netAddr.String()
360
361 b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
362 require.NoError(t, err)
363
364 assert.Equal(t, expURL, string(b))
365 })
366
367 t.Run("verify gateway file diallable while on unspecified", func(t *testing.T) {
368 t.Parallel()
369 node := harness.NewT(t).NewNode().Init()
370 node.UpdateConfig(func(cfg *config.Config) {
371 cfg.Addresses.Gateway = config.Strings{"/ip4/127.0.0.1/tcp/32563"}
372 })
373 node.StartDaemon()
374 defer node.StopDaemon()
375
376 b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
377 require.NoError(t, err)
378
379 assert.Equal(t, "http://127.0.0.1:32563", string(b))
380 })
381
382 t.Run("NoFetch", func(t *testing.T) {
383 t.Parallel()
384 nodes := harness.NewT(t).NewNodes(2).Init()
385 node1 := nodes[0]
386 node2 := nodes[1]
387
388 node1.UpdateConfig(func(cfg *config.Config) {
389 cfg.Gateway.NoFetch = true
390 })
391
392 node2PeerID, err := peer.ToCid(node2.PeerID()).StringOfBase(multibase.Base36)
393 assert.NoError(t, err)
394
395 nodes.StartDaemons().Connect()
396 t.Cleanup(func() { nodes.StopDaemons() })
397
398 t.Run("not present", func(t *testing.T) {
399 cidFoo := node2.IPFSAddStr("foo")
400
401 t.Run("not present CID from node 1", func(t *testing.T) {
402 t.Parallel()
403 assert.Equal(t, 404, node1.GatewayClient().Get("/ipfs/"+cidFoo).StatusCode)
404 })
405
406 t.Run("not present IPNS Record from node 1", func(t *testing.T) {
407 t.Parallel()
408 assert.Equal(t, 500, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode)
409 })
410 })
411
412 t.Run("present", func(t *testing.T) {
413 cidBar := node1.IPFSAddStr("bar")
414
415 t.Run("present CID from node 1", func(t *testing.T) {
416 t.Parallel()
417 assert.Equal(t, 200, node1.GatewayClient().Get("/ipfs/"+cidBar).StatusCode)
418 })
419
420 t.Run("present IPNS Record from node 1", func(t *testing.T) {
421 t.Parallel()
422 node2.IPFS("name", "publish", "/ipfs/"+cidBar)
423 assert.Equal(t, 200, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode)
424 })
425 })
426 })
427
428 t.Run("DeserializedResponses", func(t *testing.T) {
429 type testCase struct {
430 globalValue config.Flag
431 gatewayValue config.Flag
432 deserializedGlobalStatusCode int
433 deserializedGatewayStaticCode int
434 message string
435 }
436
437 setHost := func(r *http.Request) {
438 r.Host = "example.com"
439 }
440
441 withAccept := func(accept string) func(r *http.Request) {
442 return func(r *http.Request) {
443 r.Header.Set("Accept", accept)
444 }
445 }
446
447 withHostAndAccept := func(accept string) func(r *http.Request) {
448 return func(r *http.Request) {
449 setHost(r)
450 withAccept(accept)(r)
451 }
452 }
453
454 makeTest := func(test *testCase) func(t *testing.T) {
455 return func(t *testing.T) {
456 t.Parallel()
457
458 node := harness.NewT(t).NewNode().Init()
459 node.UpdateConfig(func(cfg *config.Config) {
460 cfg.Gateway.DeserializedResponses = test.globalValue
461 cfg.Gateway.PublicGateways = map[string]*config.GatewaySpec{
462 "example.com": {
463 Paths: []string{"/ipfs", "/ipns"},
464 DeserializedResponses: test.gatewayValue,
465 },
466 }
467 })
468 node.StartDaemon()
469 defer node.StopDaemon()
470
471 cidFoo := node.IPFSAddStr("foo")
472 client := node.GatewayClient()
473
474 deserializedPath := "/ipfs/" + cidFoo
475
476 blockPath := deserializedPath + "?format=raw"
477 carPath := deserializedPath + "?format=car"
478
479 // Global Check (Gateway.DeserializedResponses)
480 assert.Equal(t, http.StatusOK, client.Get(blockPath).StatusCode)
481 assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.raw")).StatusCode)
482
483 assert.Equal(t, http.StatusOK, client.Get(carPath).StatusCode)
484 assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.car")).StatusCode)
485
486 assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath).StatusCode)
487 assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath, withAccept("application/json")).StatusCode)
488
489 // Public Gateway (example.com) Check (Gateway.PublicGateways[example.com].DeserializedResponses)
490 assert.Equal(t, http.StatusOK, client.Get(blockPath, setHost).StatusCode)
491 assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.raw")).StatusCode)
492
493 assert.Equal(t, http.StatusOK, client.Get(carPath, setHost).StatusCode)
494 assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.car")).StatusCode)
495
496 assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, setHost).StatusCode)
497 assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, withHostAndAccept("application/json")).StatusCode)
498 }
499 }
500
501 for _, test := range []*testCase{
502 {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)"},
503 {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)"},
504 {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)"},
505 {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)"},
506 } {
507 t.Run(test.message, makeTest(test))
508 }
509 })
510
511 t.Run("DisableHTMLErrors", func(t *testing.T) {
512 t.Parallel()
513
514 t.Run("Returns HTML error without DisableHTMLErrors, Accept contains text/html", func(t *testing.T) {
515 t.Parallel()
516
517 node := harness.NewT(t).NewNode().Init()
518 node.StartDaemon()
519 defer node.StopDaemon()
520 client := node.GatewayClient()
521
522 res := client.Get("/ipfs/invalid-thing", func(r *http.Request) {
523 r.Header.Set("Accept", "text/html")
524 })
525 assert.NotEqual(t, http.StatusOK, res.StatusCode)
526 assert.Contains(t, res.Resp.Header.Get("Content-Type"), "text/html")
527 })
528
529 t.Run("Does not return HTML error with DisableHTMLErrors enabled, and Accept contains text/html", func(t *testing.T) {
530 t.Parallel()
531
532 node := harness.NewT(t).NewNode().Init()
533 node.UpdateConfig(func(cfg *config.Config) {
534 cfg.Gateway.DisableHTMLErrors = config.True
535 })
536 node.StartDaemon()
537 defer node.StopDaemon()
538 client := node.GatewayClient()
539
540 res := client.Get("/ipfs/invalid-thing", func(r *http.Request) {
541 r.Header.Set("Accept", "text/html")
542 })
543 assert.NotEqual(t, http.StatusOK, res.StatusCode)
544 assert.NotContains(t, res.Resp.Header.Get("Content-Type"), "text/html")
545 })
546 })
547 }
548
549 // TestLogs tests that GET /logs returns log messages. This test is separate
550 // because it requires setting the server's log level to "info" which may
551 // change the output expected by other tests.
552 func TestLogs(t *testing.T) {
553 h := harness.NewT(t)
554
555 t.Setenv("GOLOG_LOG_LEVEL", "info")
556
557 node := h.NewNode().Init().StartDaemon("--offline")
558 defer node.StopDaemon()
559 cid := node.IPFSAddStr("Hello Worlds!")
560
561 peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36)
562 assert.NoError(t, err)
563
564 client := node.GatewayClient()
565 client.TemplateData = map[string]string{
566 "CID": cid,
567 "PeerID": peerID,
568 }
569
570 apiClient := node.APIClient()
571 reqURL := apiClient.BuildURL("/logs")
572
573 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
574 defer cancel()
575
576 req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
577 require.NoError(t, err)
578
579 resp, err := apiClient.Client.Do(req)
580 require.NoError(t, err)
581 defer resp.Body.Close()
582
583 var found bool
584 scanner := bufio.NewScanner(resp.Body)
585 for scanner.Scan() {
586 if strings.Contains(scanner.Text(), "log API client connected") {
587 found = true
588 break
589 }
590 }
591 assert.True(t, found)
592 }