test: port gateway sharness tests to Go tests
Gus Eggert committed
Dec 14, 2022 at 11:10 UTC
5d864faac71b877ae30bd7b2f01c9dfaba68d8eb
9 files changed
+727
-378
test/cli/gateway_test.go
new
+492
@@ -0,0 +1,492 @@
1
+package cli
2
+
3
+import (
4
+ "context"
5
+ "encoding/json"
6
+ "fmt"
7
+ "net/http"
8
+ "os"
9
+ "path/filepath"
10
+ "regexp"
11
+ "testing"
12
+
13
+ "github.com/ipfs/kubo/config"
14
+ "github.com/ipfs/kubo/test/cli/harness"
15
+ . "github.com/ipfs/kubo/test/cli/testutils"
16
+ "github.com/multiformats/go-multiaddr"
17
+ manet "github.com/multiformats/go-multiaddr/net"
18
+ "github.com/stretchr/testify/assert"
19
+ "github.com/stretchr/testify/require"
20
+)
21
+
22
+func TestGateway(t *testing.T) {
23
+ t.Parallel()
24
+ h := harness.NewT(t)
25
+ node := h.NewNode().Init().StartDaemon("--offline")
26
+ cid := node.IPFSAddStr("Hello Worlds!")
27
+
28
+ client := node.GatewayClient()
29
+ client.TemplateData = map[string]string{
30
+ "CID": cid,
31
+ "PeerID": node.PeerID().String(),
32
+ }
33
+
34
+ t.Run("GET IPFS path succeeds", func(t *testing.T) {
35
+ t.Parallel()
36
+ resp := client.Get("/ipfs/{{.CID}}")
37
+ assert.Equal(t, 200, resp.StatusCode)
38
+ })
39
+
40
+ t.Run("GET IPFS path with explicit ?filename succeeds with proper header", func(t *testing.T) {
41
+ t.Parallel()
42
+ resp := client.Get("/ipfs/{{.CID}}?filename=testтест.pdf")
43
+ assert.Equal(t, 200, resp.StatusCode)
44
+ assert.Equal(t,
45
+ `inline; filename="test____.pdf"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.pdf`,
46
+ resp.Headers.Get("Content-Disposition"),
47
+ )
48
+ })
49
+
50
+ t.Run("GET IPFS path with explicit ?filename and &download=true succeeds with proper header", func(t *testing.T) {
51
+ t.Parallel()
52
+ resp := client.Get("/ipfs/{{.CID}}?filename=testтест.mp4&download=true")
53
+ assert.Equal(t, 200, resp.StatusCode)
54
+ assert.Equal(t,
55
+ `attachment; filename="test____.mp4"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.mp4`,
56
+ resp.Headers.Get("Content-Disposition"),
57
+ )
58
+ })
59
+
60
+ // https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616
61
+ t.Run("GET for Server Worker registration outside of an IPFS content root errors", func(t *testing.T) {
62
+ t.Parallel()
63
+ resp := client.Get("/ipfs/{{.CID}}?filename=sw.js", client.WithHeader("Service-Worker", "script"))
64
+ assert.Equal(t, 400, resp.StatusCode)
65
+ assert.Contains(t, resp.Body, "navigator.serviceWorker: registration is not allowed for this scope")
66
+ })
67
+
68
+ t.Run("GET IPFS directory path succeeds", func(t *testing.T) {
69
+ t.Parallel()
70
+ client := node.GatewayClient().DisableRedirects()
71
+
72
+ pageContents := "hello i am a webpage"
73
+ fileContents := "12345"
74
+ h.WriteFile("dir/test", fileContents)
75
+ h.WriteFile("dir/dirwithindex/index.html", pageContents)
76
+ cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "dir")).Stdout.Lines()
77
+
78
+ rootCID := cids[len(cids)-1]
79
+ client.TemplateData = map[string]string{
80
+ "IndexFileCID": cids[0],
81
+ "TestFileCID": cids[1],
82
+ "RootCID": rootCID,
83
+ }
84
+
85
+ t.Run("GET IPFS the index file CID", func(t *testing.T) {
86
+ t.Parallel()
87
+ resp := client.Get("/ipfs/{{.IndexFileCID}}")
88
+ assert.Equal(t, 200, resp.StatusCode)
89
+ assert.Equal(t, pageContents, resp.Body)
90
+ })
91
+
92
+ t.Run("GET IPFS the test file CID", func(t *testing.T) {
93
+ t.Parallel()
94
+ resp := client.Get("/ipfs/{{.TestFileCID}}")
95
+ assert.Equal(t, 200, resp.StatusCode)
96
+ assert.Equal(t, fileContents, resp.Body)
97
+ })
98
+
99
+ t.Run("GET IPFS directory with index.html returns redirect to add trailing slash", func(t *testing.T) {
100
+ t.Parallel()
101
+ resp := client.Head("/ipfs/{{.RootCID}}/dirwithindex?query=to-remember")
102
+ assert.Equal(t, 301, resp.StatusCode)
103
+ assert.Equal(t,
104
+ fmt.Sprintf("/ipfs/%s/dirwithindex/?query=to-remember", rootCID),
105
+ resp.Headers.Get("Location"),
106
+ )
107
+ })
108
+
109
+ // This enables go get to parse go-import meta tags from index.html files stored in IPFS
110
+ // https://github.com/ipfs/kubo/pull/3963
111
+ t.Run("GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed", func(t *testing.T) {
112
+ t.Parallel()
113
+ resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex?go-get=1")
114
+ assert.Equal(t, pageContents, resp.Body)
115
+ })
116
+
117
+ t.Run("GET IPFS directory with index.html and trailing slash returns expected output", func(t *testing.T) {
118
+ t.Parallel()
119
+ resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex/?query=to-remember")
120
+ assert.Equal(t, pageContents, resp.Body)
121
+ })
122
+
123
+ t.Run("GET IPFS nonexistent file returns 404 (Not Found)", func(t *testing.T) {
124
+ t.Parallel()
125
+ resp := client.Get("/ipfs/{{.RootCID}}/pleaseDontAddMe")
126
+ assert.Equal(t, 404, resp.StatusCode)
127
+ })
128
+
129
+ t.Run("GET IPFS invalid CID returns 400 (Bad Request)", func(t *testing.T) {
130
+ t.Parallel()
131
+ resp := client.Get("/ipfs/QmInvalid/pleaseDontAddMe")
132
+ assert.Equal(t, 400, resp.StatusCode)
133
+ })
134
+
135
+ t.Run("GET IPFS inlined zero-length data object returns ok code (200)", func(t *testing.T) {
136
+ t.Parallel()
137
+ resp := client.Get("/ipfs/bafkqaaa")
138
+ assert.Equal(t, 200, resp.StatusCode)
139
+ assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
140
+ assert.Equal(t, "", resp.Body)
141
+ })
142
+
143
+ t.Run("GET IPFS inlined zero-length data object with byte range returns ok code (200)", func(t *testing.T) {
144
+ t.Parallel()
145
+ resp := client.Get("/ipfs/bafkqaaa", client.WithHeader("Range", "bytes=0-1048575"))
146
+ assert.Equal(t, 200, resp.StatusCode)
147
+ assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length"))
148
+ assert.Equal(t, "text/plain", resp.Resp.Header.Get("Content-Type"))
149
+ })
150
+
151
+ t.Run("GET /ipfs/ipfs/{cid} returns redirect to the valid path", func(t *testing.T) {
152
+ t.Parallel()
153
+ resp := client.Get("/ipfs/ipfs/bafkqaaa?query=to-remember")
154
+ assert.Contains(t,
155
+ resp.Body,
156
+ `<meta http-equiv="refresh" content="10;url=/ipfs/bafkqaaa?query=to-remember" />`,
157
+ )
158
+ assert.Contains(t,
159
+ resp.Body,
160
+ `<link rel="canonical" href="/ipfs/bafkqaaa?query=to-remember" />`,
161
+ )
162
+ })
163
+ })
164
+
165
+ t.Run("IPNS", func(t *testing.T) {
166
+ t.Parallel()
167
+ node.IPFS("name", "publish", "--allow-offline", cid)
168
+
169
+ t.Run("GET invalid IPNS root returns 400 (Bad Request)", func(t *testing.T) {
170
+ t.Parallel()
171
+ resp := client.Get("/ipns/QmInvalid/pleaseDontAddMe")
172
+ assert.Equal(t, 400, resp.StatusCode)
173
+ })
174
+
175
+ t.Run("GET IPNS path succeeds", func(t *testing.T) {
176
+ t.Parallel()
177
+ resp := client.Get("/ipns/{{.PeerID}}")
178
+ assert.Equal(t, 200, resp.StatusCode)
179
+ assert.Equal(t, "Hello Worlds!", resp.Body)
180
+ })
181
+
182
+ t.Run("GET /ipfs/ipns/{peerid} returns redirect to the valid path", func(t *testing.T) {
183
+ t.Parallel()
184
+ resp := client.Get("/ipfs/ipns/{{.PeerID}}?query=to-remember")
185
+ peerID := node.PeerID().String()
186
+ assert.Contains(t,
187
+ resp.Body,
188
+ fmt.Sprintf(`<meta http-equiv="refresh" content="10;url=/ipns/%s?query=to-remember" />`, peerID),
189
+ )
190
+ assert.Contains(t,
191
+ resp.Body,
192
+ fmt.Sprintf(`<link rel="canonical" href="/ipns/%s?query=to-remember" />`, peerID),
193
+ )
194
+
195
+ })
196
+
197
+ })
198
+
199
+ t.Run("GET invalid IPFS path errors", func(t *testing.T) {
200
+ t.Parallel()
201
+ assert.Equal(t, 400, client.Get("/ipfs/12345").StatusCode)
202
+ })
203
+
204
+ t.Run("GET invalid path errors", func(t *testing.T) {
205
+ t.Parallel()
206
+ assert.Equal(t, 404, client.Get("/12345").StatusCode)
207
+ })
208
+
209
+ // TODO: these tests that use the API URL shouldn't be part of gateway tests...
210
+ t.Run("GET /webui returns 301 or 302", func(t *testing.T) {
211
+ t.Parallel()
212
+ resp := node.APIClient().DisableRedirects().Get("/webui")
213
+ assert.Contains(t, []int{302, 301}, resp.StatusCode)
214
+ })
215
+
216
+ t.Run("GET /webui/ returns 301 or 302", func(t *testing.T) {
217
+ t.Parallel()
218
+ resp := node.APIClient().DisableRedirects().Get("/webui/")
219
+ assert.Contains(t, []int{302, 301}, resp.StatusCode)
220
+ })
221
+
222
+ t.Run("GET /logs returns logs", func(t *testing.T) {
223
+ t.Parallel()
224
+ apiClient := node.APIClient()
225
+ reqURL := apiClient.BuildURL("/logs")
226
+
227
+ ctx, cancel := context.WithCancel(context.Background())
228
+ defer cancel()
229
+
230
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
231
+ require.NoError(t, err)
232
+
233
+ resp, err := apiClient.Client.Do(req)
234
+ require.NoError(t, err)
235
+ defer resp.Body.Close()
236
+
237
+ // read the first line of the output and parse its JSON
238
+ dec := json.NewDecoder(resp.Body)
239
+ event := struct{ Event string }{}
240
+ err = dec.Decode(&event)
241
+ require.NoError(t, err)
242
+
243
+ assert.Equal(t, "log API client connected", event.Event)
244
+ })
245
+
246
+ t.Run("POST /api/v0/version succeeds", func(t *testing.T) {
247
+ t.Parallel()
248
+ resp := node.APIClient().Post("/api/v0/version", nil)
249
+ assert.Equal(t, 200, resp.StatusCode)
250
+
251
+ assert.Len(t, resp.Resp.TransferEncoding, 1)
252
+ assert.Equal(t, "chunked", resp.Resp.TransferEncoding[0])
253
+
254
+ vers := struct{ Version string }{}
255
+ err := json.Unmarshal([]byte(resp.Body), &vers)
256
+ require.NoError(t, err)
257
+ assert.NotEmpty(t, vers.Version)
258
+ })
259
+
260
+ t.Run("pprof", func(t *testing.T) {
261
+ t.Parallel()
262
+ node := harness.NewT(t).NewNode().Init().StartDaemon()
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
+
307
+ h.WriteFile("index/index.html", "<p></p>")
308
+ cid := node.IPFS("add", "-Q", "-r", filepath.Join(h.Dir, "index")).Stderr.Trimmed()
309
+
310
+ apiClient := node.APIClient()
311
+ apiClient.TemplateData = map[string]string{"CID": cid}
312
+
313
+ t.Run("GET index.html has correct content type", func(t *testing.T) {
314
+ t.Parallel()
315
+ res := apiClient.Get("/ipfs/{{.CID}}/")
316
+ assert.Equal(t, "text/html; charset=utf-8", res.Resp.Header.Get("Content-Type"))
317
+ })
318
+
319
+ t.Run("HEAD index.html has no content", func(t *testing.T) {
320
+ t.Parallel()
321
+ res := apiClient.Head("/ipfs/{{.CID}}/")
322
+ assert.Equal(t, "", res.Body)
323
+ assert.Equal(t, "", res.Resp.Header.Get("Content-Length"))
324
+ })
325
+ })
326
+
327
+ t.Run("readonly API", func(t *testing.T) {
328
+ t.Parallel()
329
+
330
+ client := node.GatewayClient()
331
+
332
+ fileContents := "12345"
333
+ h.WriteFile("readonly/dir/test", fileContents)
334
+ cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "readonly/dir")).Stdout.Lines()
335
+
336
+ rootCID := cids[len(cids)-1]
337
+ client.TemplateData = map[string]string{"RootCID": rootCID}
338
+
339
+ t.Run("Get IPFS directory file through readonly API succeeds", func(t *testing.T) {
340
+ t.Parallel()
341
+ resp := client.Get("/api/v0/cat?arg={{.RootCID}}/test")
342
+ assert.Equal(t, 200, resp.StatusCode)
343
+ assert.Equal(t, fileContents, resp.Body)
344
+ })
345
+
346
+ t.Run("refs IPFS directory file through readonly API succeeds", func(t *testing.T) {
347
+ t.Parallel()
348
+ resp := client.Get("/api/v0/refs?arg={{.RootCID}}/test")
349
+ assert.Equal(t, 200, resp.StatusCode)
350
+ })
351
+
352
+ t.Run("test gateway API is sanitized", func(t *testing.T) {
353
+ t.Parallel()
354
+ for _, cmd := range []string{
355
+ "add",
356
+ "block/put",
357
+ "bootstrap",
358
+ "config",
359
+ "dag/put",
360
+ "dag/import",
361
+ "dht",
362
+ "diag",
363
+ "id",
364
+ "mount",
365
+ "name/publish",
366
+ "object/put",
367
+ "object/new",
368
+ "object/patch",
369
+ "pin",
370
+ "ping",
371
+ "repo",
372
+ "stats",
373
+ "swarm",
374
+ "file",
375
+ "update",
376
+ "bitswap",
377
+ } {
378
+ t.Run(cmd, func(t *testing.T) {
379
+ cmd := cmd
380
+ t.Parallel()
381
+ assert.Equal(t, 404, client.Get("/api/v0/"+cmd).StatusCode)
382
+ })
383
+ }
384
+ })
385
+ })
386
+
387
+ t.Run("refs/local", func(t *testing.T) {
388
+ t.Parallel()
389
+ gatewayAddr := URLStrToMultiaddr(node.GatewayURL())
390
+ res := node.RunIPFS("--api", gatewayAddr.String(), "refs", "local")
391
+ assert.Equal(t,
392
+ `Error: invalid path "local": selected encoding not supported`,
393
+ res.Stderr.Trimmed(),
394
+ )
395
+ })
396
+
397
+ t.Run("raw leaves node", func(t *testing.T) {
398
+ t.Parallel()
399
+ contents := "This is RAW!"
400
+ cid := node.IPFSAddStr(contents, "--raw-leaves")
401
+ assert.Equal(t, contents, client.Get("/ipfs/"+cid).Body)
402
+ })
403
+
404
+ t.Run("compact blocks", func(t *testing.T) {
405
+ t.Parallel()
406
+ block1 := "\x0a\x09\x08\x02\x12\x03\x66\x6f\x6f\x18\x03"
407
+ block2 := "\x0a\x04\x08\x02\x18\x06\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c" +
408
+ "\x8b\x05\xeb\x66\x2b\xe0\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65" +
409
+ "\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c\x8b\x05\xeb\x66\x2b\xe0" +
410
+ "\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65"
411
+
412
+ node.PipeStrToIPFS(block1, "block", "put")
413
+ block2CID := node.PipeStrToIPFS(block2, "block", "put", "--cid-codec=dag-pb").Stdout.Trimmed()
414
+
415
+ resp := client.Get("/ipfs/" + block2CID)
416
+ assert.Equal(t, 200, resp.StatusCode)
417
+ assert.Equal(t, "foofoo", resp.Body)
418
+ })
419
+
420
+ t.Run("verify gateway file", func(t *testing.T) {
421
+ t.Parallel()
422
+ r := regexp.MustCompile(`Gateway \(readonly\) server listening on (?P<addr>.+)\s`)
423
+ matches := r.FindStringSubmatch(node.Daemon.Stdout.String())
424
+ ma, err := multiaddr.NewMultiaddr(matches[1])
425
+ require.NoError(t, err)
426
+ netAddr, err := manet.ToNetAddr(ma)
427
+ require.NoError(t, err)
428
+ expURL := "http://" + netAddr.String()
429
+
430
+ b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
431
+ require.NoError(t, err)
432
+
433
+ assert.Equal(t, expURL, string(b))
434
+ })
435
+
436
+ t.Run("verify gateway file diallable while on unspecified", func(t *testing.T) {
437
+ t.Parallel()
438
+ node := harness.NewT(t).NewNode().Init()
439
+ node.UpdateConfig(func(cfg *config.Config) {
440
+ cfg.Addresses.Gateway = config.Strings{"/ip4/127.0.0.1/tcp/32563"}
441
+ })
442
+ node.StartDaemon()
443
+
444
+ b, err := os.ReadFile(filepath.Join(node.Dir, "gateway"))
445
+ require.NoError(t, err)
446
+
447
+ assert.Equal(t, "http://127.0.0.1:32563", string(b))
448
+ })
449
+
450
+ t.Run("NoFetch", func(t *testing.T) {
451
+ t.Parallel()
452
+ nodes := harness.NewT(t).NewNodes(2).Init()
453
+ node1 := nodes[0]
454
+ node2 := nodes[1]
455
+
456
+ node1.UpdateConfig(func(cfg *config.Config) {
457
+ cfg.Gateway.NoFetch = true
458
+ })
459
+
460
+ nodes.StartDaemons().Connect()
461
+
462
+ t.Run("not present", func(t *testing.T) {
463
+ cidFoo := node2.IPFSAddStr("foo")
464
+
465
+ t.Run("not present key from node 1", func(t *testing.T) {
466
+ t.Parallel()
467
+ assert.Equal(t, 404, node1.GatewayClient().Get("/ipfs/"+cidFoo).StatusCode)
468
+ })
469
+
470
+ t.Run("not present IPNS key from node 1", func(t *testing.T) {
471
+ t.Parallel()
472
+ assert.Equal(t, 400, node1.GatewayClient().Get("/ipns/"+node2.PeerID().String()).StatusCode)
473
+ })
474
+ })
475
+
476
+ t.Run("present", func(t *testing.T) {
477
+ cidBar := node1.IPFSAddStr("bar")
478
+
479
+ t.Run("present key from node 1", func(t *testing.T) {
480
+ t.Parallel()
481
+ assert.Equal(t, 200, node1.GatewayClient().Get("/ipfs/"+cidBar).StatusCode)
482
+ })
483
+
484
+ t.Run("present IPNS key from node 1", func(t *testing.T) {
485
+ t.Parallel()
486
+ node2.IPFS("name", "publish", "/ipfs/"+cidBar)
487
+ assert.Equal(t, 200, node1.GatewayClient().Get("/ipns/"+node2.PeerID().String()).StatusCode)
488
+
489
+ })
490
+ })
491
+ })
492
+}
test/cli/harness/harness.go
+8
-5
@@ -119,15 +119,19 @@ func (h *Harness) TempFile() *os.File {
119
}
120
121
// WriteFile writes a file given a filename and its contents.
122
-// The filename should be a relative path.
122
+// The filename must be a relative path, or this panics.
123
func (h *Harness) WriteFile(filename, contents string) {
124
if filepath.IsAbs(filename) {
125
log.Panicf("%s must be a relative path", filename)
126
}
127
absPath := filepath.Join(h.Runner.Dir, filename)
128
- err := os.WriteFile(absPath, []byte(contents), 0644)
128
+ err := os.MkdirAll(filepath.Dir(absPath), 0777)
129
if err != nil {
130
- log.Panicf("writing '%s' ('%s'): %s", filename, absPath, err.Error())
130
+ log.Panicf("creating intermediate dirs for %q: %s", filename, err.Error())
131
+ }
132
+ err = os.WriteFile(absPath, []byte(contents), 0644)
133
+ if err != nil {
134
+ log.Panicf("writing %q (%q): %s", filename, absPath, err.Error())
135
}
136
}
137
@@ -140,8 +144,7 @@ func WaitForFile(path string, timeout time.Duration) error {
144
for {
145
select {
146
case <-timer.C:
143
- end := time.Now()
144
- return fmt.Errorf("timeout waiting for %s after %v", path, end.Sub(start))
147
+ return fmt.Errorf("timeout waiting for %s after %v", path, time.Since(start))
148
case <-ticker.C:
149
_, err := os.Stat(path)
150
if err == nil {
test/cli/harness/http_client.go
new
+116
@@ -0,0 +1,116 @@
1
+package harness
2
+
3
+import (
4
+ "io"
5
+ "net/http"
6
+ "strings"
7
+ "text/template"
8
+ "time"
9
+)
10
+
11
+// HTTPClient is an HTTP client with some conveniences for testing.
12
+// URLs are constructed from a base URL.
13
+// The response body is buffered into a string.
14
+// Internal errors cause panics so that tests don't need to check errors.
15
+// The paths are evaluated as Go templates for readable string interpolation.
16
+type HTTPClient struct {
17
+ Client *http.Client
18
+ BaseURL string
19
+
20
+ Timeout time.Duration
21
+ TemplateData any
22
+}
23
+
24
+type HTTPResponse struct {
25
+ Body string
26
+ StatusCode int
27
+ Headers http.Header
28
+
29
+ // The raw response. The body will be closed on this response.
30
+ Resp *http.Response
31
+}
32
+
33
+func (c *HTTPClient) WithHeader(k, v string) func(h *http.Request) {
34
+ return func(h *http.Request) {
35
+ h.Header.Add(k, v)
36
+ }
37
+}
38
+
39
+func (c *HTTPClient) DisableRedirects() *HTTPClient {
40
+ c.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
41
+ return http.ErrUseLastResponse
42
+ }
43
+ return c
44
+}
45
+
46
+// Do executes the request unchanged.
47
+func (c *HTTPClient) Do(req *http.Request) *HTTPResponse {
48
+ log.Debugf("making HTTP req %s to %q with headers %+v", req.Method, req.URL.String(), req.Header)
49
+ resp, err := c.Client.Do(req)
50
+ if resp != nil && resp.Body != nil {
51
+ defer resp.Body.Close()
52
+ }
53
+ if err != nil {
54
+ panic(err)
55
+ }
56
+ bodyStr, err := io.ReadAll(resp.Body)
57
+ if err != nil {
58
+ panic(err)
59
+ }
60
+
61
+ return &HTTPResponse{
62
+ Body: string(bodyStr),
63
+ StatusCode: resp.StatusCode,
64
+ Headers: resp.Header,
65
+ Resp: resp,
66
+ }
67
+}
68
+
69
+// BuildURL constructs a request URL from the given path by interpolating the string and then appending it to the base URL.
70
+func (c *HTTPClient) BuildURL(urlPath string) string {
71
+ sb := &strings.Builder{}
72
+ err := template.Must(template.New("test").Parse(urlPath)).Execute(sb, c.TemplateData)
73
+ if err != nil {
74
+ panic(err)
75
+ }
76
+ renderedPath := sb.String()
77
+ return c.BaseURL + renderedPath
78
+}
79
+
80
+func (c *HTTPClient) Get(urlPath string, opts ...func(*http.Request)) *HTTPResponse {
81
+ req, err := http.NewRequest(http.MethodGet, c.BuildURL(urlPath), nil)
82
+ if err != nil {
83
+ panic(err)
84
+ }
85
+ for _, o := range opts {
86
+ o(req)
87
+ }
88
+ return c.Do(req)
89
+}
90
+
91
+func (c *HTTPClient) Post(urlPath string, body io.Reader, opts ...func(*http.Request)) *HTTPResponse {
92
+ req, err := http.NewRequest(http.MethodPost, c.BuildURL(urlPath), body)
93
+ if err != nil {
94
+ panic(err)
95
+ }
96
+ for _, o := range opts {
97
+ o(req)
98
+ }
99
+ return c.Do(req)
100
+}
101
+
102
+func (c *HTTPClient) PostStr(urlpath, body string, opts ...func(*http.Request)) *HTTPResponse {
103
+ r := strings.NewReader(body)
104
+ return c.Post(urlpath, r, opts...)
105
+}
106
+
107
+func (c *HTTPClient) Head(urlPath string, opts ...func(*http.Request)) *HTTPResponse {
108
+ req, err := http.NewRequest(http.MethodHead, c.BuildURL(urlPath), nil)
109
+ if err != nil {
110
+ panic(err)
111
+ }
112
+ for _, o := range opts {
113
+ o(req)
114
+ }
115
+ return c.Do(req)
116
+}
test/cli/harness/node.go
+68
-12
@@ -5,6 +5,7 @@ import (
5
"errors"
6
"fmt"
7
"io"
8
+ "io/fs"
9
"net/http"
10
"os"
11
"os/exec"
@@ -19,6 +20,7 @@ import (
20
serial "github.com/ipfs/kubo/config/serialize"
21
"github.com/libp2p/go-libp2p/core/peer"
22
"github.com/multiformats/go-multiaddr"
23
+ manet "github.com/multiformats/go-multiaddr/net"
24
)
25
26
var log = logging.Logger("testharness")
@@ -29,14 +31,15 @@ type Node struct {
31
ID int
32
Dir string
33
32
- APIListenAddr multiaddr.Multiaddr
33
- SwarmAddr multiaddr.Multiaddr
34
- EnableMDNS bool
34
+ APIListenAddr multiaddr.Multiaddr
35
+ GatewayListenAddr multiaddr.Multiaddr
36
+ SwarmAddr multiaddr.Multiaddr
37
+ EnableMDNS bool
38
39
IPFSBin string
40
Runner *Runner
41
39
- daemon *RunResult
42
+ Daemon *RunResult
43
}
44
45
func BuildNode(ipfsBin, baseDir string, id int) *Node {
@@ -134,11 +137,19 @@ func (n *Node) Init(ipfsArgs ...string) *Node {
137
n.APIListenAddr = apiAddr
138
}
139
140
+ if n.GatewayListenAddr == nil {
141
+ gatewayAddr, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
142
+ if err != nil {
143
+ panic(err)
144
+ }
145
+ n.GatewayListenAddr = gatewayAddr
146
+ }
147
+
148
n.UpdateConfig(func(cfg *config.Config) {
149
cfg.Bootstrap = []string{}
150
cfg.Addresses.Swarm = []string{n.SwarmAddr.String()}
151
cfg.Addresses.API = []string{n.APIListenAddr.String()}
141
- cfg.Addresses.Gateway = []string{""}
152
+ cfg.Addresses.Gateway = []string{n.GatewayListenAddr.String()}
153
cfg.Swarm.DisableNatPortMap = true
154
cfg.Discovery.MDNS.Enabled = n.EnableMDNS
155
})
@@ -159,7 +170,7 @@ func (n *Node) StartDaemon(ipfsArgs ...string) *Node {
170
RunFunc: (*exec.Cmd).Start,
171
})
172
162
- n.daemon = &res
173
+ n.Daemon = &res
174
175
log.Debugf("node %d started, checking API", n.ID)
176
n.WaitOnAPI()
@@ -167,7 +178,7 @@ func (n *Node) StartDaemon(ipfsArgs ...string) *Node {
178
}
179
180
func (n *Node) signalAndWait(watch <-chan struct{}, signal os.Signal, t time.Duration) bool {
170
- err := n.daemon.Cmd.Process.Signal(signal)
181
+ err := n.Daemon.Cmd.Process.Signal(signal)
182
if err != nil {
183
if errors.Is(err, os.ErrProcessDone) {
184
log.Debugf("process for node %d has already finished", n.ID)
@@ -187,13 +198,13 @@ func (n *Node) signalAndWait(watch <-chan struct{}, signal os.Signal, t time.Dur
198
199
func (n *Node) StopDaemon() *Node {
200
log.Debugf("stopping node %d", n.ID)
190
- if n.daemon == nil {
201
+ if n.Daemon == nil {
202
log.Debugf("didn't stop node %d since no daemon present", n.ID)
203
return n
204
}
205
watch := make(chan struct{}, 1)
206
go func() {
196
- _, _ = n.daemon.Cmd.Process.Wait()
207
+ _, _ = n.Daemon.Cmd.Process.Wait()
208
watch <- struct{}{}
209
}()
210
log.Debugf("signaling node %d with SIGTERM", n.ID)
@@ -224,6 +235,15 @@ func (n *Node) APIAddr() multiaddr.Multiaddr {
235
return ma
236
}
237
238
+func (n *Node) APIURL() string {
239
+ apiAddr := n.APIAddr()
240
+ netAddr, err := manet.ToNetAddr(apiAddr)
241
+ if err != nil {
242
+ panic(err)
243
+ }
244
+ return "http://" + netAddr.String()
245
+}
246
+
247
func (n *Node) TryAPIAddr() (multiaddr.Multiaddr, error) {
248
b, err := os.ReadFile(filepath.Join(n.Dir, "api"))
249
if err != nil {
@@ -305,20 +325,21 @@ func (n *Node) WaitOnAPI() *Node {
325
log.Debugf("waiting on API for node %d", n.ID)
326
for i := 0; i < 50; i++ {
327
if n.checkAPI() {
328
+ log.Debugf("daemon API found, daemon stdout: %s", n.Daemon.Stdout.String())
329
return n
330
}
331
time.Sleep(400 * time.Millisecond)
332
}
312
- log.Panicf("node %d with peer ID %s failed to come online: \n%s\n\n%s", n.ID, n.PeerID(), n.daemon.Stderr.String(), n.daemon.Stdout.String())
333
+ log.Panicf("node %d with peer ID %s failed to come online: \n%s\n\n%s", n.ID, n.PeerID(), n.Daemon.Stderr.String(), n.Daemon.Stdout.String())
334
return n
335
}
336
337
func (n *Node) IsAlive() bool {
317
- if n.daemon == nil || n.daemon.Cmd == nil || n.daemon.Cmd.Process == nil {
338
+ if n.Daemon == nil || n.Daemon.Cmd == nil || n.Daemon.Cmd.Process == nil {
339
return false
340
}
341
log.Debugf("signaling node %d daemon process for liveness check", n.ID)
321
- err := n.daemon.Cmd.Process.Signal(syscall.Signal(0))
342
+ err := n.Daemon.Cmd.Process.Signal(syscall.Signal(0))
343
if err == nil {
344
log.Debugf("node %d daemon is alive", n.ID)
345
return true
@@ -381,3 +402,38 @@ func (n *Node) Peers() []multiaddr.Multiaddr {
402
}
403
return addrs
404
}
405
+
406
+// GatewayURL waits for the gateway file and then returns its contents or times out.
407
+func (n *Node) GatewayURL() string {
408
+ timer := time.NewTimer(1 * time.Second)
409
+ defer timer.Stop()
410
+ for {
411
+ select {
412
+ case <-timer.C:
413
+ panic("timeout waiting for gateway file")
414
+ default:
415
+ b, err := os.ReadFile(filepath.Join(n.Dir, "gateway"))
416
+ if err == nil {
417
+ return strings.TrimSpace(string(b))
418
+ }
419
+ if !errors.Is(err, fs.ErrNotExist) {
420
+ panic(err)
421
+ }
422
+ time.Sleep(1 * time.Millisecond)
423
+ }
424
+ }
425
+}
426
+
427
+func (n *Node) GatewayClient() *HTTPClient {
428
+ return &HTTPClient{
429
+ Client: http.DefaultClient,
430
+ BaseURL: n.GatewayURL(),
431
+ }
432
+}
433
+
434
+func (n *Node) APIClient() *HTTPClient {
435
+ return &HTTPClient{
436
+ Client: http.DefaultClient,
437
+ BaseURL: n.APIURL(),
438
+ }
439
+}
test/cli/harness/nodes.go
+19
-2
@@ -1,6 +1,8 @@
1
package harness
2
3
import (
4
+ "sync"
5
+
6
"github.com/multiformats/go-multiaddr"
7
)
8
@@ -15,14 +17,22 @@ func (n Nodes) Init(args ...string) Nodes {
17
}
18
19
func (n Nodes) Connect() Nodes {
20
+ wg := sync.WaitGroup{}
21
for i, node := range n {
22
for j, otherNode := range n {
23
if i == j {
24
continue
25
}
23
- node.Connect(otherNode)
26
+ node := node
27
+ otherNode := otherNode
28
+ wg.Add(1)
29
+ go func() {
30
+ defer wg.Done()
31
+ node.Connect(otherNode)
32
+ }()
33
}
34
}
35
+ wg.Wait()
36
for _, node := range n {
37
firstPeer := node.Peers()[0]
38
if _, err := firstPeer.ValueForProtocol(multiaddr.P_P2P); err != nil {
@@ -33,9 +43,16 @@ func (n Nodes) Connect() Nodes {
43
}
44
45
func (n Nodes) StartDaemons() Nodes {
46
+ wg := sync.WaitGroup{}
47
for _, node := range n {
37
- node.StartDaemon()
48
+ wg.Add(1)
49
+ node := node
50
+ go func() {
51
+ defer wg.Done()
52
+ node.StartDaemon()
53
+ }()
54
}
55
+ wg.Wait()
56
return n
57
}
58
test/cli/testutils/strings.go
+24
@@ -3,7 +3,13 @@ package testutils
3
import (
4
"bufio"
5
"fmt"
6
+ "net"
7
+ "net/netip"
8
+ "net/url"
9
"strings"
10
+
11
+ "github.com/multiformats/go-multiaddr"
12
+ manet "github.com/multiformats/go-multiaddr/net"
13
)
14
15
// StrCat takes a bunch of strings or string slices
@@ -51,3 +57,21 @@ func SplitLines(s string) []string {
57
}
58
return lines
59
}
60
+
61
+// URLStrToMultiaddr converts a URL string like http://localhost:80 to a multiaddr.
62
+func URLStrToMultiaddr(u string) multiaddr.Multiaddr {
63
+ parsedURL, err := url.Parse(u)
64
+ if err != nil {
65
+ panic(err)
66
+ }
67
+ addrPort, err := netip.ParseAddrPort(parsedURL.Host)
68
+ if err != nil {
69
+ panic(err)
70
+ }
71
+ tcpAddr := net.TCPAddrFromAddrPort(addrPort)
72
+ ma, err := manet.FromNetAddr(tcpAddr)
73
+ if err != nil {
74
+ panic(err)
75
+ }
76
+ return ma
77
+}
test/sharness/t0110-gateway-data/foo.block
deleted
-2
@@ -1,2 +0,0 @@
1
-
2
- foo
\ No newline at end of file
test/sharness/t0110-gateway-data/foofoo.block
Binary files a/test/sharness/t0110-gateway-data/foofoo.block and /dev/null differ
test/sharness/t0110-gateway.sh
deleted
-357
@@ -1,357 +0,0 @@
1
-#!/usr/bin/env bash
2
-#
3
-# Copyright (c) 2015 Matt Bell
4
-# MIT Licensed; see the LICENSE file in this repository.
5
-#
6
-
7
-test_description="Test HTTP Gateway"
8
-
9
-. lib/test-lib.sh
10
-
11
-test_init_ipfs
12
-test_launch_ipfs_daemon
13
-
14
-port=$GWAY_PORT
15
-apiport=$API_PORT
16
-
17
-# TODO check both 5001 and 5002.
18
-# 5001 should have a readable gateway (part of the API)
19
-# 5002 should have a readable gateway (using ipfs config Addresses.Gateway)
20
-# but ideally we should only write the tests once. so maybe we need to
21
-# define a function to test a gateway, and do so for each port.
22
-# for now we check 5001 here as 5002 will be checked in gateway-writable.
23
-
24
-test_expect_success "Make a file to test with" '
25
- echo "Hello Worlds!" >expected &&
26
- HASH=$(ipfs add -q expected) ||
27
- test_fsh cat daemon_err
28
-'
29
-
30
-test_expect_success "GET IPFS path succeeds" '
31
- curl -sfo actual "http://127.0.0.1:$port/ipfs/$HASH"
32
-'
33
-
34
-test_expect_success "GET IPFS path with explicit ?filename succeeds with proper header" "
35
- curl -fo actual -D actual_headers 'http://127.0.0.1:$port/ipfs/$HASH?filename=testтест.pdf' &&
36
- grep -F 'Content-Disposition: inline; filename=\"test____.pdf\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.pdf' actual_headers
37
-"
38
-
39
-test_expect_success "GET IPFS path with explicit ?filename and &download=true succeeds with proper header" "
40
- curl -fo actual -D actual_headers 'http://127.0.0.1:$port/ipfs/$HASH?filename=testтест.mp4&download=true' &&
41
- grep -F 'Content-Disposition: attachment; filename=\"test____.mp4\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.mp4' actual_headers
42
-"
43
-
44
-# https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616
45
-test_expect_success "GET for Service Worker registration outside of an IPFS content root errors" "
46
- curl -H 'Service-Worker: script' -svX GET 'http://127.0.0.1:$port/ipfs/$HASH?filename=sw.js' > curl_sw_out 2>&1 &&
47
- grep 'HTTP/1.1 400 Bad Request' curl_sw_out &&
48
- grep 'navigator.serviceWorker: registration is not allowed for this scope' curl_sw_out
49
-"
50
-
51
-test_expect_success "GET IPFS path output looks good" '
52
- test_cmp expected actual &&
53
- rm actual
54
-'
55
-
56
-test_expect_success "GET IPFS directory path succeeds" '
57
- mkdir -p dir/dirwithindex &&
58
- echo "12345" >dir/test &&
59
- echo "hello i am a webpage" >dir/dirwithindex/index.html &&
60
- ipfs add -r -q dir >actual &&
61
- HASH2=$(tail -n 1 actual) &&
62
- curl -sf "http://127.0.0.1:$port/ipfs/$HASH2"
63
-'
64
-
65
-test_expect_success "GET IPFS directory file succeeds" '
66
- curl -sfo actual "http://127.0.0.1:$port/ipfs/$HASH2/test"
67
-'
68
-
69
-test_expect_success "GET IPFS directory file output looks good" '
70
- test_cmp dir/test actual
71
-'
72
-
73
-test_expect_success "GET IPFS directory with index.html returns redirect to add trailing slash" "
74
- curl -sI -o response_without_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?query=to-remember\" &&
75
- test_should_contain \"HTTP/1.1 301 Moved Permanently\" response_without_slash &&
76
- test_should_contain \"Location: /ipfs/$HASH2/dirwithindex/?query=to-remember\" response_without_slash
77
-"
78
-
79
-# This enables go get to parse go-import meta tags from index.html files stored in IPFS
80
-# https://github.com/ipfs/kubo/pull/3963
81
-test_expect_success "GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed" "
82
- curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?go-get=1\" &&
83
- test_should_contain \"hello i am a webpage\" response_with_slash
84
-"
85
-
86
-test_expect_success "GET IPFS directory with index.html and trailing slash returns expected output" "
87
- curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex/?query=to-remember\" &&
88
- test_should_contain \"hello i am a webpage\" response_with_slash
89
-"
90
-
91
-test_expect_success "GET IPFS nonexistent file returns 404 (Not Found)" '
92
- test_curl_resp_http_code "http://127.0.0.1:$port/ipfs/$HASH2/pleaseDontAddMe" "HTTP/1.1 404 Not Found"
93
-'
94
-
95
-test_expect_success "GET IPFS invalid CID returns 400 (Bad Request)" '
96
- test_curl_resp_http_code "http://127.0.0.1:$port/ipfs/QmInvalid/pleaseDontAddMe" "HTTP/1.1 400 Bad Request"
97
-'
98
-
99
-# https://github.com/ipfs/go-ipfs/issues/8230
100
-test_expect_success "GET IPFS inlined zero-length data object returns ok code (200)" '
101
- curl -sD - "http://127.0.0.1:$port/ipfs/bafkqaaa" > empty_ok_response &&
102
- test_should_contain "HTTP/1.1 200 OK" empty_ok_response &&
103
- test_should_contain "Content-Length: 0" empty_ok_response
104
-'
105
-
106
-# https://github.com/ipfs/kubo/issues/9238
107
-test_expect_success "GET IPFS inlined zero-length data object with byte range returns ok code (200)" '
108
- curl -sD - "http://127.0.0.1:$port/ipfs/bafkqaaa" -H "Range: bytes=0-1048575" > empty_ok_response &&
109
- test_should_contain "HTTP/1.1 200 OK" empty_ok_response &&
110
- test_should_contain "Content-Length: 0" empty_ok_response &&
111
- test_should_contain "Content-Type: text/plain" empty_ok_response
112
-'
113
-
114
-test_expect_success "GET /ipfs/ipfs/{cid} returns redirect to the valid path" '
115
- curl -sD - "http://127.0.0.1:$port/ipfs/ipfs/bafkqaaa?query=to-remember" > response_with_double_ipfs_ns &&
116
- test_should_contain "<meta http-equiv=\"refresh\" content=\"10;url=/ipfs/bafkqaaa?query=to-remember\" />" response_with_double_ipfs_ns &&
117
- test_should_contain "<link rel=\"canonical\" href=\"/ipfs/bafkqaaa?query=to-remember\" />" response_with_double_ipfs_ns
118
-'
119
-
120
-test_expect_success "GET invalid IPNS root returns 400 (Bad Request)" '
121
- test_curl_resp_http_code "http://127.0.0.1:$port/ipns/QmInvalid/pleaseDontAddMe" "HTTP/1.1 400 Bad Request"
122
-'
123
-
124
-test_expect_success "GET IPNS path succeeds" '
125
- ipfs name publish --allow-offline "$HASH" &&
126
- PEERID=$(ipfs config Identity.PeerID) &&
127
- test_check_peerid "$PEERID" &&
128
- curl -sfo actual "http://127.0.0.1:$port/ipns/$PEERID"
129
-'
130
-
131
-test_expect_success "GET IPNS path output looks good" '
132
- test_cmp expected actual
133
-'
134
-
135
-test_expect_success "GET /ipfs/ipns/{peerid} returns redirect to the valid path" '
136
- PEERID=$(ipfs config Identity.PeerID) &&
137
- curl -sD - "http://127.0.0.1:$port/ipfs/ipns/${PEERID}?query=to-remember" > response_with_ipfs_ipns_ns &&
138
- test_should_contain "<meta http-equiv=\"refresh\" content=\"10;url=/ipns/${PEERID}?query=to-remember\" />" response_with_ipfs_ipns_ns &&
139
- test_should_contain "<link rel=\"canonical\" href=\"/ipns/${PEERID}?query=to-remember\" />" response_with_ipfs_ipns_ns
140
-'
141
-
142
-test_expect_success "GET invalid IPFS path errors" '
143
- test_must_fail curl -sf "http://127.0.0.1:$port/ipfs/12345"
144
-'
145
-
146
-test_expect_success "GET invalid path errors" '
147
- test_must_fail curl -sf "http://127.0.0.1:$port/12345"
148
-'
149
-
150
-test_expect_success "GET /webui returns code expected" '
151
- test_curl_resp_http_code "http://127.0.0.1:$apiport/webui" "HTTP/1.1 302 Found" "HTTP/1.1 301 Moved Permanently"
152
-'
153
-
154
-test_expect_success "GET /webui/ returns code expected" '
155
- test_curl_resp_http_code "http://127.0.0.1:$apiport/webui/" "HTTP/1.1 302 Found" "HTTP/1.1 301 Moved Permanently"
156
-'
157
-
158
-test_expect_success "GET /logs returns logs" '
159
- test_expect_code 28 curl http://127.0.0.1:$apiport/logs -m1 > log_out
160
-'
161
-
162
-test_expect_success "log output looks good" '
163
- grep "log API client connected" log_out
164
-'
165
-
166
-test_expect_success "GET /api/v0/version succeeds" '
167
- curl -X POST -v "http://127.0.0.1:$apiport/api/v0/version" 2> version_out
168
-'
169
-
170
-test_expect_success "output only has one transfer encoding header" '
171
- grep "Transfer-Encoding: chunked" version_out | wc -l | xargs echo > tecount_out &&
172
- echo "1" > tecount_exp &&
173
- test_cmp tecount_out tecount_exp
174
-'
175
-
176
-curl_pprofmutex() {
177
- curl -f -X POST "http://127.0.0.1:$apiport/debug/pprof-mutex/?fraction=$1"
178
-}
179
-
180
-test_expect_success "set mutex fraction for pprof (negative so it doesn't enable)" '
181
- curl_pprofmutex -1
182
-'
183
-
184
-test_expect_success "test failure conditions of mutex pprof endpoint" '
185
- test_must_fail curl_pprofmutex &&
186
- test_must_fail curl_pprofmutex that_is_string &&
187
- test_must_fail curl -f -X GET "http://127.0.0.1:$apiport/debug/pprof-mutex/?fraction=-1"
188
-'
189
-
190
-curl_pprofblock() {
191
- curl -f -X POST "http://127.0.0.1:$apiport/debug/pprof-block/?rate=$1"
192
-}
193
-
194
-test_expect_success "set blocking profiler rate for pprof (0 so it doesn't enable)" '
195
- curl_pprofblock 0
196
-'
197
-
198
-test_expect_success "test failure conditions of mutex block endpoint" '
199
- test_must_fail curl_pprofblock &&
200
- test_must_fail curl_pprofblock that_is_string &&
201
- test_must_fail curl -f -X GET "http://127.0.0.1:$apiport/debug/pprof-block/?rate=0"
202
-'
203
-
204
-test_expect_success "setup index hash" '
205
- mkdir index &&
206
- echo "<p></p>" > index/index.html &&
207
- INDEXHASH=$(ipfs add -Q -r index)
208
- echo index: $INDEXHASH
209
-'
210
-
211
-test_expect_success "GET 'index.html' has correct content type" '
212
- curl -I "http://127.0.0.1:$port/ipfs/$INDEXHASH/" > indexout
213
-'
214
-
215
-test_expect_success "output looks good" '
216
- grep "Content-Type: text/html" indexout
217
-'
218
-
219
-test_expect_success "HEAD 'index.html' has no content" '
220
- curl -X HEAD --max-time 1 http://127.0.0.1:$port/ipfs/$INDEXHASH/ > output;
221
- [ ! -s output ]
222
-'
223
-
224
-# test ipfs readonly api
225
-
226
-test_curl_gateway_api() {
227
- curl -sfo actual "http://127.0.0.1:$port/api/v0/$1"
228
-}
229
-
230
-test_expect_success "get IPFS directory file through readonly API succeeds" '
231
- test_curl_gateway_api "cat?arg=$HASH2/test"
232
-'
233
-
234
-test_expect_success "get IPFS directory file through readonly API output looks good" '
235
- test_cmp dir/test actual
236
-'
237
-
238
-test_expect_success "refs IPFS directory file through readonly API succeeds" '
239
- test_curl_gateway_api "refs?arg=$HASH2/test"
240
-'
241
-
242
-for cmd in add \
243
- block/put \
244
- bootstrap \
245
- config \
246
- dag/put \
247
- dag/import \
248
- dht \
249
- diag \
250
- id \
251
- mount \
252
- name/publish \
253
- object/put \
254
- object/new \
255
- object/patch \
256
- pin \
257
- ping \
258
- repo \
259
- stats \
260
- swarm \
261
- file \
262
- update \
263
- bitswap
264
-do
265
- test_expect_success "test gateway api is sanitized: $cmd" '
266
- test_curl_resp_http_code "http://127.0.0.1:$port/api/v0/$cmd" "HTTP/1.1 404 Not Found"
267
- '
268
-done
269
-
270
-# This one is different. `local` will be interpreted as a path if the command isn't defined.
271
-test_expect_success "test gateway api is sanitized: refs/local" '
272
- echo "Error: invalid path \"local\": selected encoding not supported" > refs_local_expected &&
273
- ! ipfs --api /ip4/127.0.0.1/tcp/$port refs local > refs_local_actual 2>&1 &&
274
- test_cmp refs_local_expected refs_local_actual
275
- '
276
-
277
-test_expect_success "create raw-leaves node" '
278
- echo "This is RAW!" > rfile &&
279
- echo "This is RAW!" | ipfs add --raw-leaves -q > rhash
280
-'
281
-
282
-test_expect_success "try fetching it from gateway" '
283
- curl http://127.0.0.1:$port/ipfs/$(cat rhash) > ffile &&
284
- test_cmp rfile ffile
285
-'
286
-
287
-test_expect_success "Add compact blocks" '
288
- ipfs block put ../t0110-gateway-data/foo.block &&
289
- FOO2_HASH=$(ipfs block put --cid-codec=dag-pb ../t0110-gateway-data/foofoo.block) &&
290
- printf "foofoo" > expected
291
-'
292
-
293
-test_expect_success "GET compact blocks succeeds" '
294
- curl -o actual "http://127.0.0.1:$port/ipfs/$FOO2_HASH" &&
295
- test_cmp expected actual
296
-'
297
-
298
-test_expect_success "Verify gateway file" '
299
- cat "$IPFS_PATH/gateway" > gateway_file_actual &&
300
- echo -n "http://$GWAY_ADDR" > gateway_daemon_actual &&
301
- test_cmp gateway_daemon_actual gateway_file_actual
302
-'
303
-
304
-test_kill_ipfs_daemon
305
-
306
-GWPORT=32563
307
-
308
-test_expect_success "Verify gateway file diallable while on unspecified" '
309
- ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/$GWPORT &&
310
- test_launch_ipfs_daemon &&
311
- cat "$IPFS_PATH/gateway" > gateway_file_actual &&
312
- echo -n "http://127.0.0.1:$GWPORT" > gateway_file_expected &&
313
- test_cmp gateway_file_expected gateway_file_actual
314
-'
315
-
316
-test_kill_ipfs_daemon
317
-
318
-test_expect_success "set up iptb testbed" '
319
- iptb testbed create -type localipfs -count 5 -force -init &&
320
- ipfsi 0 config Addresses.Gateway /ip4/127.0.0.1/tcp/$GWPORT &&
321
- PEERID_1=$(iptb attr get 1 id)
322
-'
323
-
324
-test_expect_success "set NoFetch to true in config of node 0" '
325
- ipfsi 0 config --bool=true Gateway.NoFetch true
326
-'
327
-
328
-test_expect_success "start ipfs nodes" '
329
- iptb start -wait &&
330
- iptb connect 0 1
331
-'
332
-
333
-test_expect_success "try fetching not present key from node 0" '
334
- FOO=$(echo "foo" | ipfsi 1 add -Q) &&
335
- test_expect_code 22 curl -f "http://127.0.0.1:$GWPORT/ipfs/$FOO"
336
-'
337
-
338
-test_expect_success "try fetching not present ipns key from node 0" '
339
- ipfsi 1 name publish /ipfs/$FOO &&
340
- test_expect_code 22 curl -f "http://127.0.0.1:$GWPORT/ipns/$PEERID_1"
341
-'
342
-
343
-test_expect_success "try fetching present key from node 0" '
344
- BAR=$(echo "bar" | ipfsi 0 add -Q) &&
345
- curl -f "http://127.0.0.1:$GWPORT/ipfs/$BAR"
346
-'
347
-
348
-test_expect_success "try fetching present ipns key from node 0" '
349
- ipfsi 1 name publish /ipfs/$BAR &&
350
- curl "http://127.0.0.1:$GWPORT/ipns/$PEERID_1"
351
-'
352
-
353
-test_expect_success "stop testbed" '
354
- iptb stop
355
-'
356
-
357
-test_done