github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/swarm/api/http/server_test.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 // 10 // 11 // 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 25 package http 26 27 import ( 28 "archive/tar" 29 "bytes" 30 "context" 31 "crypto/rand" 32 "encoding/json" 33 "errors" 34 "flag" 35 "fmt" 36 "io" 37 "io/ioutil" 38 "mime/multipart" 39 "net/http" 40 "os" 41 "strconv" 42 "strings" 43 "testing" 44 "time" 45 46 "github.com/ethereum/go-ethereum/common" 47 "github.com/ethereum/go-ethereum/crypto" 48 "github.com/ethereum/go-ethereum/log" 49 "github.com/ethereum/go-ethereum/swarm/api" 50 swarm "github.com/ethereum/go-ethereum/swarm/api/client" 51 "github.com/ethereum/go-ethereum/swarm/multihash" 52 "github.com/ethereum/go-ethereum/swarm/storage" 53 "github.com/ethereum/go-ethereum/swarm/storage/mru" 54 "github.com/ethereum/go-ethereum/swarm/testutil" 55 ) 56 57 func init() { 58 loglevel := flag.Int("loglevel", 2, "loglevel") 59 flag.Parse() 60 log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))) 61 } 62 63 func TestResourcePostMode(t *testing.T) { 64 path := "" 65 errstr := "resourcePostMode for '%s' should be raw %v frequency %d, was raw %v, frequency %d" 66 r, f, err := resourcePostMode(path) 67 if err != nil { 68 t.Fatal(err) 69 } else if r || f != 0 { 70 t.Fatalf(errstr, path, false, 0, r, f) 71 } 72 73 path = "raw" 74 r, f, err = resourcePostMode(path) 75 if err != nil { 76 t.Fatal(err) 77 } else if !r || f != 0 { 78 t.Fatalf(errstr, path, true, 0, r, f) 79 } 80 81 path = "13" 82 r, f, err = resourcePostMode(path) 83 if err != nil { 84 t.Fatal(err) 85 } else if r || f == 0 { 86 t.Fatalf(errstr, path, false, 13, r, f) 87 } 88 89 path = "raw/13" 90 r, f, err = resourcePostMode(path) 91 if err != nil { 92 t.Fatal(err) 93 } else if !r || f == 0 { 94 t.Fatalf(errstr, path, true, 13, r, f) 95 } 96 97 path = "foo/13" 98 r, f, err = resourcePostMode(path) 99 if err == nil { 100 t.Fatal("resourcePostMode for 'foo/13' should fail, returned error nil") 101 } 102 } 103 104 func serverFunc(api *api.API) testutil.TestServer { 105 return NewServer(api, "") 106 } 107 108 func newTestSigner() (*mru.GenericSigner, error) { 109 privKey, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") 110 if err != nil { 111 return nil, err 112 } 113 return mru.NewGenericSigner(privKey), nil 114 } 115 116 // 117 // 118 // 119 // 120 // 121 func TestBzzResourceMultihash(t *testing.T) { 122 123 signer, _ := newTestSigner() 124 125 srv := testutil.NewTestSwarmServer(t, serverFunc) 126 defer srv.Close() 127 128 // 129 databytes := "bar" 130 url := fmt.Sprintf("%s/bzz:/", srv.URL) 131 resp, err := http.Post(url, "text/plain", bytes.NewReader([]byte(databytes))) 132 if err != nil { 133 t.Fatal(err) 134 } 135 defer resp.Body.Close() 136 if resp.StatusCode != http.StatusOK { 137 t.Fatalf("err %s", resp.Status) 138 } 139 b, err := ioutil.ReadAll(resp.Body) 140 141 if err != nil { 142 t.Fatal(err) 143 } 144 s := common.FromHex(string(b)) 145 mh := multihash.ToMultihash(s) 146 147 log.Info("added data", "manifest", string(b), "data", common.ToHex(mh)) 148 149 // 150 keybytes := "foo.eth" 151 152 updateRequest, err := mru.NewCreateUpdateRequest(&mru.ResourceMetadata{ 153 Name: keybytes, 154 Frequency: 13, 155 StartTime: srv.GetCurrentTime(), 156 Owner: signer.Address(), 157 }) 158 if err != nil { 159 t.Fatal(err) 160 } 161 updateRequest.SetData(mh, true) 162 163 if err := updateRequest.Sign(signer); err != nil { 164 t.Fatal(err) 165 } 166 log.Info("added data", "manifest", string(b), "data", common.ToHex(mh)) 167 168 body, err := updateRequest.MarshalJSON() 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 // 174 url = fmt.Sprintf("%s/bzz-resource:/", srv.URL) 175 resp, err = http.Post(url, "application/json", bytes.NewReader(body)) 176 if err != nil { 177 t.Fatal(err) 178 } 179 defer resp.Body.Close() 180 if resp.StatusCode != http.StatusOK { 181 t.Fatalf("err %s", resp.Status) 182 } 183 b, err = ioutil.ReadAll(resp.Body) 184 if err != nil { 185 t.Fatal(err) 186 } 187 rsrcResp := &storage.Address{} 188 err = json.Unmarshal(b, rsrcResp) 189 if err != nil { 190 t.Fatalf("data %s could not be unmarshaled: %v", b, err) 191 } 192 193 correctManifestAddrHex := "6d3bc4664c97d8b821cb74bcae43f592494fb46d2d9cd31e69f3c7c802bbbd8e" 194 if rsrcResp.Hex() != correctManifestAddrHex { 195 t.Fatalf("Response resource key mismatch, expected '%s', got '%s'", correctManifestAddrHex, rsrcResp.Hex()) 196 } 197 198 // 199 url = fmt.Sprintf("%s/bzz:/%s", srv.URL, rsrcResp) 200 resp, err = http.Get(url) 201 if err != nil { 202 t.Fatal(err) 203 } 204 defer resp.Body.Close() 205 if resp.StatusCode != http.StatusOK { 206 t.Fatalf("err %s", resp.Status) 207 } 208 b, err = ioutil.ReadAll(resp.Body) 209 if err != nil { 210 t.Fatal(err) 211 } 212 if !bytes.Equal(b, []byte(databytes)) { 213 t.Fatalf("retrieved data mismatch, expected %x, got %x", databytes, b) 214 } 215 } 216 217 // 218 func TestBzzResource(t *testing.T) { 219 srv := testutil.NewTestSwarmServer(t, serverFunc) 220 signer, _ := newTestSigner() 221 222 defer srv.Close() 223 224 // 225 keybytes := "foo.eth" 226 227 // 228 databytes := make([]byte, 666) 229 _, err := rand.Read(databytes) 230 if err != nil { 231 t.Fatal(err) 232 } 233 234 updateRequest, err := mru.NewCreateUpdateRequest(&mru.ResourceMetadata{ 235 Name: keybytes, 236 Frequency: 13, 237 StartTime: srv.GetCurrentTime(), 238 Owner: signer.Address(), 239 }) 240 if err != nil { 241 t.Fatal(err) 242 } 243 updateRequest.SetData(databytes, false) 244 245 if err := updateRequest.Sign(signer); err != nil { 246 t.Fatal(err) 247 } 248 249 body, err := updateRequest.MarshalJSON() 250 if err != nil { 251 t.Fatal(err) 252 } 253 254 // 255 url := fmt.Sprintf("%s/bzz-resource:/", srv.URL) 256 resp, err := http.Post(url, "application/json", bytes.NewReader(body)) 257 if err != nil { 258 t.Fatal(err) 259 } 260 defer resp.Body.Close() 261 if resp.StatusCode != http.StatusOK { 262 t.Fatalf("err %s", resp.Status) 263 } 264 b, err := ioutil.ReadAll(resp.Body) 265 if err != nil { 266 t.Fatal(err) 267 } 268 rsrcResp := &storage.Address{} 269 err = json.Unmarshal(b, rsrcResp) 270 if err != nil { 271 t.Fatalf("data %s could not be unmarshaled: %v", b, err) 272 } 273 274 correctManifestAddrHex := "6d3bc4664c97d8b821cb74bcae43f592494fb46d2d9cd31e69f3c7c802bbbd8e" 275 if rsrcResp.Hex() != correctManifestAddrHex { 276 t.Fatalf("Response resource key mismatch, expected '%s', got '%s'", correctManifestAddrHex, rsrcResp.Hex()) 277 } 278 279 // 280 url = fmt.Sprintf("%s/bzz-raw:/%s", srv.URL, rsrcResp) 281 resp, err = http.Get(url) 282 if err != nil { 283 t.Fatal(err) 284 } 285 defer resp.Body.Close() 286 if resp.StatusCode != http.StatusOK { 287 t.Fatalf("err %s", resp.Status) 288 } 289 b, err = ioutil.ReadAll(resp.Body) 290 if err != nil { 291 t.Fatal(err) 292 } 293 manifest := &api.Manifest{} 294 err = json.Unmarshal(b, manifest) 295 if err != nil { 296 t.Fatal(err) 297 } 298 if len(manifest.Entries) != 1 { 299 t.Fatalf("Manifest has %d entries", len(manifest.Entries)) 300 } 301 correctRootKeyHex := "68f7ba07ac8867a4c841a4d4320e3cdc549df23702dc7285fcb6acf65df48562" 302 if manifest.Entries[0].Hash != correctRootKeyHex { 303 t.Fatalf("Expected manifest path '%s', got '%s'", correctRootKeyHex, manifest.Entries[0].Hash) 304 } 305 306 // 307 url = fmt.Sprintf("%s/bzz:/%s", srv.URL, rsrcResp) 308 resp, err = http.Get(url) 309 if err != nil { 310 t.Fatal(err) 311 } 312 defer resp.Body.Close() 313 if resp.StatusCode != http.StatusOK { 314 t.Fatalf("err %s", resp.Status) 315 } 316 b, err = ioutil.ReadAll(resp.Body) 317 if err != nil { 318 t.Fatal(err) 319 } 320 321 // 322 url = fmt.Sprintf("%s/bzz-resource:/bar", srv.URL) 323 resp, err = http.Get(url) 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 if resp.StatusCode != http.StatusNotFound { 329 t.Fatalf("Expected get non-existent resource to fail with StatusNotFound (404), got %d", resp.StatusCode) 330 } 331 332 resp.Body.Close() 333 334 // 335 log.Info("get update latest = 1.1", "addr", correctManifestAddrHex) 336 url = fmt.Sprintf("%s/bzz-resource:/%s", srv.URL, correctManifestAddrHex) 337 resp, err = http.Get(url) 338 if err != nil { 339 t.Fatal(err) 340 } 341 defer resp.Body.Close() 342 if resp.StatusCode != http.StatusOK { 343 t.Fatalf("err %s", resp.Status) 344 } 345 b, err = ioutil.ReadAll(resp.Body) 346 if err != nil { 347 t.Fatal(err) 348 } 349 if !bytes.Equal(databytes, b) { 350 t.Fatalf("Expected body '%x', got '%x'", databytes, b) 351 } 352 353 // 354 log.Info("update 2") 355 356 // 357 url = fmt.Sprintf("%s/bzz-resource:/%s/", srv.URL, correctManifestAddrHex) 358 resp, err = http.Get(url + "meta") 359 if err != nil { 360 t.Fatal(err) 361 } 362 defer resp.Body.Close() 363 if resp.StatusCode != http.StatusOK { 364 t.Fatalf("Get resource metadata returned %s", resp.Status) 365 } 366 b, err = ioutil.ReadAll(resp.Body) 367 if err != nil { 368 t.Fatal(err) 369 } 370 updateRequest = &mru.Request{} 371 if err = updateRequest.UnmarshalJSON(b); err != nil { 372 t.Fatalf("Error decoding resource metadata: %s", err) 373 } 374 data := []byte("foo") 375 updateRequest.SetData(data, false) 376 if err = updateRequest.Sign(signer); err != nil { 377 t.Fatal(err) 378 } 379 body, err = updateRequest.MarshalJSON() 380 if err != nil { 381 t.Fatal(err) 382 } 383 384 resp, err = http.Post(url, "application/json", bytes.NewReader(body)) 385 if err != nil { 386 t.Fatal(err) 387 } 388 defer resp.Body.Close() 389 if resp.StatusCode != http.StatusOK { 390 t.Fatalf("Update returned %s", resp.Status) 391 } 392 393 // 394 log.Info("get update 1.2") 395 url = fmt.Sprintf("%s/bzz-resource:/%s", srv.URL, correctManifestAddrHex) 396 resp, err = http.Get(url) 397 if err != nil { 398 t.Fatal(err) 399 } 400 defer resp.Body.Close() 401 if resp.StatusCode != http.StatusOK { 402 t.Fatalf("err %s", resp.Status) 403 } 404 b, err = ioutil.ReadAll(resp.Body) 405 if err != nil { 406 t.Fatal(err) 407 } 408 if !bytes.Equal(data, b) { 409 t.Fatalf("Expected body '%x', got '%x'", data, b) 410 } 411 412 // 413 log.Info("get update latest = 1.2") 414 url = fmt.Sprintf("%s/bzz-resource:/%s/1", srv.URL, correctManifestAddrHex) 415 resp, err = http.Get(url) 416 if err != nil { 417 t.Fatal(err) 418 } 419 defer resp.Body.Close() 420 if resp.StatusCode != http.StatusOK { 421 t.Fatalf("err %s", resp.Status) 422 } 423 b, err = ioutil.ReadAll(resp.Body) 424 if err != nil { 425 t.Fatal(err) 426 } 427 if !bytes.Equal(data, b) { 428 t.Fatalf("Expected body '%x', got '%x'", data, b) 429 } 430 431 // 432 log.Info("get first update 1.1") 433 url = fmt.Sprintf("%s/bzz-resource:/%s/1/1", srv.URL, correctManifestAddrHex) 434 resp, err = http.Get(url) 435 if err != nil { 436 t.Fatal(err) 437 } 438 defer resp.Body.Close() 439 if resp.StatusCode != http.StatusOK { 440 t.Fatalf("err %s", resp.Status) 441 } 442 b, err = ioutil.ReadAll(resp.Body) 443 if err != nil { 444 t.Fatal(err) 445 } 446 if !bytes.Equal(databytes, b) { 447 t.Fatalf("Expected body '%x', got '%x'", databytes, b) 448 } 449 } 450 451 func TestBzzGetPath(t *testing.T) { 452 testBzzGetPath(false, t) 453 testBzzGetPath(true, t) 454 } 455 456 func testBzzGetPath(encrypted bool, t *testing.T) { 457 var err error 458 459 testmanifest := []string{ 460 `{"entries":[{"path":"b","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"c","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0}]}`, 461 `{"entries":[{"path":"a","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"b/","hash":"<key0>","contentType":"application/bzz-manifest+json","status":0}]}`, 462 `{"entries":[{"path":"a/","hash":"<key1>","contentType":"application/bzz-manifest+json","status":0}]}`, 463 } 464 465 testrequests := make(map[string]int) 466 testrequests["/"] = 2 467 testrequests["/a/"] = 1 468 testrequests["/a/b/"] = 0 469 testrequests["/x"] = 0 470 testrequests[""] = 0 471 472 expectedfailrequests := []string{"", "/x"} 473 474 reader := [3]*bytes.Reader{} 475 476 addr := [3]storage.Address{} 477 478 srv := testutil.NewTestSwarmServer(t, serverFunc) 479 defer srv.Close() 480 481 for i, mf := range testmanifest { 482 reader[i] = bytes.NewReader([]byte(mf)) 483 var wait func(context.Context) error 484 ctx := context.TODO() 485 addr[i], wait, err = srv.FileStore.Store(ctx, reader[i], int64(len(mf)), encrypted) 486 for j := i + 1; j < len(testmanifest); j++ { 487 testmanifest[j] = strings.Replace(testmanifest[j], fmt.Sprintf("<key%v>", i), addr[i].Hex(), -1) 488 } 489 if err != nil { 490 t.Fatal(err) 491 } 492 err = wait(ctx) 493 if err != nil { 494 t.Fatal(err) 495 } 496 } 497 498 rootRef := addr[2].Hex() 499 500 _, err = http.Get(srv.URL + "/bzz-raw:/" + rootRef + "/a") 501 if err != nil { 502 t.Fatalf("Failed to connect to proxy: %v", err) 503 } 504 505 for k, v := range testrequests { 506 var resp *http.Response 507 var respbody []byte 508 509 url := srv.URL + "/bzz-raw:/" 510 if k[:] != "" { 511 url += rootRef + "/" + k[1:] + "?content_type=text/plain" 512 } 513 resp, err = http.Get(url) 514 if err != nil { 515 t.Fatalf("Request failed: %v", err) 516 } 517 defer resp.Body.Close() 518 respbody, err = ioutil.ReadAll(resp.Body) 519 520 if string(respbody) != testmanifest[v] { 521 isexpectedfailrequest := false 522 523 for _, r := range expectedfailrequests { 524 if k[:] == r { 525 isexpectedfailrequest = true 526 } 527 } 528 if !isexpectedfailrequest { 529 t.Fatalf("Response body does not match, expected: %v, got %v", testmanifest[v], string(respbody)) 530 } 531 } 532 } 533 534 for k, v := range testrequests { 535 var resp *http.Response 536 var respbody []byte 537 538 url := srv.URL + "/bzz-hash:/" 539 if k[:] != "" { 540 url += rootRef + "/" + k[1:] 541 } 542 resp, err = http.Get(url) 543 if err != nil { 544 t.Fatalf("Request failed: %v", err) 545 } 546 defer resp.Body.Close() 547 respbody, err = ioutil.ReadAll(resp.Body) 548 if err != nil { 549 t.Fatalf("Read request body: %v", err) 550 } 551 552 if string(respbody) != addr[v].Hex() { 553 isexpectedfailrequest := false 554 555 for _, r := range expectedfailrequests { 556 if k[:] == r { 557 isexpectedfailrequest = true 558 } 559 } 560 if !isexpectedfailrequest { 561 t.Fatalf("Response body does not match, expected: %v, got %v", addr[v], string(respbody)) 562 } 563 } 564 } 565 566 ref := addr[2].Hex() 567 568 for _, c := range []struct { 569 path string 570 json string 571 pageFragments []string 572 }{ 573 { 574 path: "/", 575 json: `{"common_prefixes":["a/"]}`, 576 pageFragments: []string{ 577 fmt.Sprintf("Swarm index of bzz:/%s/", ref), 578 `<a class="normal-link" href="a/">a/</a>`, 579 }, 580 }, 581 { 582 path: "/a/", 583 json: `{"common_prefixes":["a/b/"],"entries":[{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/a","mod_time":"0001-01-01T00:00:00Z"}]}`, 584 pageFragments: []string{ 585 fmt.Sprintf("Swarm index of bzz:/%s/a/", ref), 586 `<a class="normal-link" href="b/">b/</a>`, 587 fmt.Sprintf(`<a class="normal-link" href="/bzz:/%s/a/a">a</a>`, ref), 588 }, 589 }, 590 { 591 path: "/a/b/", 592 json: `{"entries":[{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/b/b","mod_time":"0001-01-01T00:00:00Z"},{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/b/c","mod_time":"0001-01-01T00:00:00Z"}]}`, 593 pageFragments: []string{ 594 fmt.Sprintf("Swarm index of bzz:/%s/a/b/", ref), 595 fmt.Sprintf(`<a class="normal-link" href="/bzz:/%s/a/b/b">b</a>`, ref), 596 fmt.Sprintf(`<a class="normal-link" href="/bzz:/%s/a/b/c">c</a>`, ref), 597 }, 598 }, 599 { 600 path: "/x", 601 }, 602 { 603 path: "", 604 }, 605 } { 606 k := c.path 607 url := srv.URL + "/bzz-list:/" 608 if k[:] != "" { 609 url += rootRef + "/" + k[1:] 610 } 611 t.Run("json list "+c.path, func(t *testing.T) { 612 resp, err := http.Get(url) 613 if err != nil { 614 t.Fatalf("HTTP request: %v", err) 615 } 616 defer resp.Body.Close() 617 respbody, err := ioutil.ReadAll(resp.Body) 618 if err != nil { 619 t.Fatalf("Read response body: %v", err) 620 } 621 622 body := strings.TrimSpace(string(respbody)) 623 if body != c.json { 624 isexpectedfailrequest := false 625 626 for _, r := range expectedfailrequests { 627 if k[:] == r { 628 isexpectedfailrequest = true 629 } 630 } 631 if !isexpectedfailrequest { 632 t.Errorf("Response list body %q does not match, expected: %v, got %v", k, c.json, body) 633 } 634 } 635 }) 636 t.Run("html list "+c.path, func(t *testing.T) { 637 req, err := http.NewRequest(http.MethodGet, url, nil) 638 if err != nil { 639 t.Fatalf("New request: %v", err) 640 } 641 req.Header.Set("Accept", "text/html") 642 resp, err := http.DefaultClient.Do(req) 643 if err != nil { 644 t.Fatalf("HTTP request: %v", err) 645 } 646 defer resp.Body.Close() 647 b, err := ioutil.ReadAll(resp.Body) 648 if err != nil { 649 t.Fatalf("Read response body: %v", err) 650 } 651 652 body := string(b) 653 654 for _, f := range c.pageFragments { 655 if !strings.Contains(body, f) { 656 isexpectedfailrequest := false 657 658 for _, r := range expectedfailrequests { 659 if k[:] == r { 660 isexpectedfailrequest = true 661 } 662 } 663 if !isexpectedfailrequest { 664 t.Errorf("Response list body %q does not contain %q: body %q", k, f, body) 665 } 666 } 667 } 668 }) 669 } 670 671 nonhashtests := []string{ 672 srv.URL + "/bzz:/name", 673 srv.URL + "/bzz-immutable:/nonhash", 674 srv.URL + "/bzz-raw:/nonhash", 675 srv.URL + "/bzz-list:/nonhash", 676 srv.URL + "/bzz-hash:/nonhash", 677 } 678 679 nonhashresponses := []string{ 680 `cannot resolve name: no DNS to resolve name: "name"`, 681 `cannot resolve nonhash: immutable address not a content hash: "nonhash"`, 682 `cannot resolve nonhash: no DNS to resolve name: "nonhash"`, 683 `cannot resolve nonhash: no DNS to resolve name: "nonhash"`, 684 `cannot resolve nonhash: no DNS to resolve name: "nonhash"`, 685 } 686 687 for i, url := range nonhashtests { 688 var resp *http.Response 689 var respbody []byte 690 691 resp, err = http.Get(url) 692 693 if err != nil { 694 t.Fatalf("Request failed: %v", err) 695 } 696 defer resp.Body.Close() 697 respbody, err = ioutil.ReadAll(resp.Body) 698 if err != nil { 699 t.Fatalf("ReadAll failed: %v", err) 700 } 701 if !strings.Contains(string(respbody), nonhashresponses[i]) { 702 t.Fatalf("Non-Hash response body does not match, expected: %v, got: %v", nonhashresponses[i], string(respbody)) 703 } 704 } 705 } 706 707 func TestBzzTar(t *testing.T) { 708 testBzzTar(false, t) 709 testBzzTar(true, t) 710 } 711 712 func testBzzTar(encrypted bool, t *testing.T) { 713 srv := testutil.NewTestSwarmServer(t, serverFunc) 714 defer srv.Close() 715 fileNames := []string{"tmp1.txt", "tmp2.lock", "tmp3.rtf"} 716 fileContents := []string{"tmp1textfilevalue", "tmp2lockfilelocked", "tmp3isjustaplaintextfile"} 717 718 buf := &bytes.Buffer{} 719 tw := tar.NewWriter(buf) 720 defer tw.Close() 721 722 for i, v := range fileNames { 723 size := int64(len(fileContents[i])) 724 hdr := &tar.Header{ 725 Name: v, 726 Mode: 0644, 727 Size: size, 728 ModTime: time.Now(), 729 Xattrs: map[string]string{ 730 "user.swarm.content-type": "text/plain", 731 }, 732 } 733 if err := tw.WriteHeader(hdr); err != nil { 734 t.Fatal(err) 735 } 736 737 // 738 n, err := io.Copy(tw, bytes.NewBufferString(fileContents[i])) 739 if err != nil { 740 t.Fatal(err) 741 } else if n != size { 742 t.Fatal("size mismatch") 743 } 744 } 745 746 // 747 url := srv.URL + "/bzz:/" 748 if encrypted { 749 url = url + "encrypt" 750 } 751 req, err := http.NewRequest("POST", url, buf) 752 if err != nil { 753 t.Fatal(err) 754 } 755 req.Header.Add("Content-Type", "application/x-tar") 756 client := &http.Client{} 757 resp2, err := client.Do(req) 758 if err != nil { 759 t.Fatal(err) 760 } 761 if resp2.StatusCode != http.StatusOK { 762 t.Fatalf("err %s", resp2.Status) 763 } 764 swarmHash, err := ioutil.ReadAll(resp2.Body) 765 resp2.Body.Close() 766 if err != nil { 767 t.Fatal(err) 768 } 769 770 // 771 req, err = http.NewRequest("GET", fmt.Sprintf(srv.URL+"/bzz:/%s", string(swarmHash)), nil) 772 if err != nil { 773 t.Fatal(err) 774 } 775 req.Header.Add("Accept", "application/x-tar") 776 resp2, err = client.Do(req) 777 if err != nil { 778 t.Fatal(err) 779 } 780 defer resp2.Body.Close() 781 782 file, err := ioutil.TempFile("", "swarm-downloaded-tarball") 783 if err != nil { 784 t.Fatal(err) 785 } 786 defer os.Remove(file.Name()) 787 _, err = io.Copy(file, resp2.Body) 788 if err != nil { 789 t.Fatalf("error getting tarball: %v", err) 790 } 791 file.Sync() 792 file.Close() 793 794 tarFileHandle, err := os.Open(file.Name()) 795 if err != nil { 796 t.Fatal(err) 797 } 798 tr := tar.NewReader(tarFileHandle) 799 800 for { 801 hdr, err := tr.Next() 802 if err == io.EOF { 803 break 804 } else if err != nil { 805 t.Fatalf("error reading tar stream: %s", err) 806 } 807 bb := make([]byte, hdr.Size) 808 _, err = tr.Read(bb) 809 if err != nil && err != io.EOF { 810 t.Fatal(err) 811 } 812 passed := false 813 for i, v := range fileNames { 814 if v == hdr.Name { 815 if string(bb) == fileContents[i] { 816 passed = true 817 break 818 } 819 } 820 } 821 if !passed { 822 t.Fatalf("file %s did not pass content assertion", hdr.Name) 823 } 824 } 825 } 826 827 // 828 // 829 // 830 func TestBzzRootRedirect(t *testing.T) { 831 testBzzRootRedirect(false, t) 832 } 833 func TestBzzRootRedirectEncrypted(t *testing.T) { 834 testBzzRootRedirect(true, t) 835 } 836 837 func testBzzRootRedirect(toEncrypt bool, t *testing.T) { 838 srv := testutil.NewTestSwarmServer(t, serverFunc) 839 defer srv.Close() 840 841 // 842 client := swarm.NewClient(srv.URL) 843 data := []byte("data") 844 file := &swarm.File{ 845 ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), 846 ManifestEntry: api.ManifestEntry{ 847 Path: "", 848 ContentType: "text/plain", 849 Size: int64(len(data)), 850 }, 851 } 852 hash, err := client.Upload(file, "", toEncrypt) 853 if err != nil { 854 t.Fatal(err) 855 } 856 857 // 858 // 859 redirected := false 860 httpClient := http.Client{ 861 CheckRedirect: func(req *http.Request, via []*http.Request) error { 862 if redirected { 863 return errors.New("too many redirects") 864 } 865 redirected = true 866 expectedPath := "/bzz:/" + hash + "/" 867 if req.URL.Path != expectedPath { 868 return fmt.Errorf("expected redirect to %q, got %q", expectedPath, req.URL.Path) 869 } 870 return nil 871 }, 872 } 873 874 // 875 res, err := httpClient.Get(srv.URL + "/bzz:/" + hash) 876 if err != nil { 877 t.Fatal(err) 878 } 879 defer res.Body.Close() 880 if !redirected { 881 t.Fatal("expected GET /bzz:/<hash> to redirect to /bzz:/<hash>/ but it didn't") 882 } 883 gotData, err := ioutil.ReadAll(res.Body) 884 if err != nil { 885 t.Fatal(err) 886 } 887 if !bytes.Equal(gotData, data) { 888 t.Fatalf("expected response to equal %q, got %q", data, gotData) 889 } 890 } 891 892 func TestMethodsNotAllowed(t *testing.T) { 893 srv := testutil.NewTestSwarmServer(t, serverFunc) 894 defer srv.Close() 895 databytes := "bar" 896 for _, c := range []struct { 897 url string 898 code int 899 }{ 900 { 901 url: fmt.Sprintf("%s/bzz-list:/", srv.URL), 902 code: 405, 903 }, { 904 url: fmt.Sprintf("%s/bzz-hash:/", srv.URL), 905 code: 405, 906 }, 907 { 908 url: fmt.Sprintf("%s/bzz-immutable:/", srv.URL), 909 code: 405, 910 }, 911 } { 912 res, _ := http.Post(c.url, "text/plain", bytes.NewReader([]byte(databytes))) 913 if res.StatusCode != c.code { 914 t.Fatalf("should have failed. requested url: %s, expected code %d, got %d", c.url, c.code, res.StatusCode) 915 } 916 } 917 918 } 919 920 func httpDo(httpMethod string, url string, reqBody io.Reader, headers map[string]string, verbose bool, t *testing.T) (*http.Response, string) { 921 // 922 req, err := http.NewRequest(httpMethod, url, reqBody) 923 if err != nil { 924 t.Fatal(err) 925 } 926 for key, value := range headers { 927 req.Header.Set(key, value) 928 } 929 if verbose { 930 t.Log(req.Method, req.URL, req.Header, req.Body) 931 } 932 933 // 934 httpClient := &http.Client{} 935 res, err := httpClient.Do(req) 936 if err != nil { 937 t.Fatal(err) 938 } 939 940 // 941 buffer, err := ioutil.ReadAll(res.Body) 942 if err != nil { 943 t.Fatal(err) 944 } 945 defer res.Body.Close() 946 body := string(buffer) 947 948 return res, body 949 } 950 951 func TestGet(t *testing.T) { 952 srv := testutil.NewTestSwarmServer(t, serverFunc) 953 defer srv.Close() 954 955 for _, testCase := range []struct { 956 uri string 957 method string 958 headers map[string]string 959 expectedStatusCode int 960 assertResponseBody string 961 verbose bool 962 }{ 963 { 964 uri: fmt.Sprintf("%s/", srv.URL), 965 method: "GET", 966 headers: map[string]string{"Accept": "text/html"}, 967 expectedStatusCode: 200, 968 assertResponseBody: "Swarm: Serverless Hosting Incentivised Peer-To-Peer Storage And Content Distribution", 969 verbose: false, 970 }, 971 { 972 uri: fmt.Sprintf("%s/", srv.URL), 973 method: "GET", 974 headers: map[string]string{"Accept": "application/json"}, 975 expectedStatusCode: 200, 976 assertResponseBody: "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 977 verbose: false, 978 }, 979 { 980 uri: fmt.Sprintf("%s/robots.txt", srv.URL), 981 method: "GET", 982 headers: map[string]string{"Accept": "text/html"}, 983 expectedStatusCode: 200, 984 assertResponseBody: "User-agent: *\nDisallow: /", 985 verbose: false, 986 }, 987 { 988 uri: fmt.Sprintf("%s/nonexistent_path", srv.URL), 989 method: "GET", 990 headers: map[string]string{}, 991 expectedStatusCode: 404, 992 verbose: false, 993 }, 994 { 995 uri: fmt.Sprintf("%s/bzz:asdf/", srv.URL), 996 method: "GET", 997 headers: map[string]string{}, 998 expectedStatusCode: 404, 999 verbose: false, 1000 }, 1001 { 1002 uri: fmt.Sprintf("%s/tbz2/", srv.URL), 1003 method: "GET", 1004 headers: map[string]string{}, 1005 expectedStatusCode: 404, 1006 verbose: false, 1007 }, 1008 { 1009 uri: fmt.Sprintf("%s/bzz-rack:/", srv.URL), 1010 method: "GET", 1011 headers: map[string]string{}, 1012 expectedStatusCode: 404, 1013 verbose: false, 1014 }, 1015 { 1016 uri: fmt.Sprintf("%s/bzz-ls", srv.URL), 1017 method: "GET", 1018 headers: map[string]string{}, 1019 expectedStatusCode: 404, 1020 verbose: false, 1021 }, 1022 } { 1023 t.Run("GET "+testCase.uri, func(t *testing.T) { 1024 res, body := httpDo(testCase.method, testCase.uri, nil, testCase.headers, testCase.verbose, t) 1025 if res.StatusCode != testCase.expectedStatusCode { 1026 t.Fatalf("expected status code %d but got %d", testCase.expectedStatusCode, res.StatusCode) 1027 } 1028 if testCase.assertResponseBody != "" && !strings.Contains(body, testCase.assertResponseBody) { 1029 t.Fatalf("expected response to be: %s but got: %s", testCase.assertResponseBody, body) 1030 } 1031 }) 1032 } 1033 } 1034 1035 func TestModify(t *testing.T) { 1036 srv := testutil.NewTestSwarmServer(t, serverFunc) 1037 defer srv.Close() 1038 1039 swarmClient := swarm.NewClient(srv.URL) 1040 data := []byte("data") 1041 file := &swarm.File{ 1042 ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), 1043 ManifestEntry: api.ManifestEntry{ 1044 Path: "", 1045 ContentType: "text/plain", 1046 Size: int64(len(data)), 1047 }, 1048 } 1049 1050 hash, err := swarmClient.Upload(file, "", false) 1051 if err != nil { 1052 t.Fatal(err) 1053 } 1054 1055 for _, testCase := range []struct { 1056 uri string 1057 method string 1058 headers map[string]string 1059 requestBody []byte 1060 expectedStatusCode int 1061 assertResponseBody string 1062 assertResponseHeaders map[string]string 1063 verbose bool 1064 }{ 1065 { 1066 uri: fmt.Sprintf("%s/bzz:/%s", srv.URL, hash), 1067 method: "DELETE", 1068 headers: map[string]string{}, 1069 expectedStatusCode: 200, 1070 assertResponseBody: "8b634aea26eec353ac0ecbec20c94f44d6f8d11f38d4578a4c207a84c74ef731", 1071 verbose: false, 1072 }, 1073 { 1074 uri: fmt.Sprintf("%s/bzz:/%s", srv.URL, hash), 1075 method: "PUT", 1076 headers: map[string]string{}, 1077 expectedStatusCode: 405, 1078 verbose: false, 1079 }, 1080 { 1081 uri: fmt.Sprintf("%s/bzz-raw:/%s", srv.URL, hash), 1082 method: "PUT", 1083 headers: map[string]string{}, 1084 expectedStatusCode: 405, 1085 verbose: false, 1086 }, 1087 { 1088 uri: fmt.Sprintf("%s/bzz:/%s", srv.URL, hash), 1089 method: "PATCH", 1090 headers: map[string]string{}, 1091 expectedStatusCode: 405, 1092 verbose: false, 1093 }, 1094 { 1095 uri: fmt.Sprintf("%s/bzz-raw:/", srv.URL), 1096 method: "POST", 1097 headers: map[string]string{}, 1098 requestBody: []byte("POSTdata"), 1099 expectedStatusCode: 200, 1100 assertResponseHeaders: map[string]string{"Content-Length": "64"}, 1101 verbose: false, 1102 }, 1103 { 1104 uri: fmt.Sprintf("%s/bzz-raw:/encrypt", srv.URL), 1105 method: "POST", 1106 headers: map[string]string{}, 1107 requestBody: []byte("POSTdata"), 1108 expectedStatusCode: 200, 1109 assertResponseHeaders: map[string]string{"Content-Length": "128"}, 1110 verbose: false, 1111 }, 1112 } { 1113 t.Run(testCase.method+" "+testCase.uri, func(t *testing.T) { 1114 reqBody := bytes.NewReader(testCase.requestBody) 1115 res, body := httpDo(testCase.method, testCase.uri, reqBody, testCase.headers, testCase.verbose, t) 1116 1117 if res.StatusCode != testCase.expectedStatusCode { 1118 t.Fatalf("expected status code %d but got %d", testCase.expectedStatusCode, res.StatusCode) 1119 } 1120 if testCase.assertResponseBody != "" && !strings.Contains(body, testCase.assertResponseBody) { 1121 t.Log(body) 1122 t.Fatalf("expected response %s but got %s", testCase.assertResponseBody, body) 1123 } 1124 for key, value := range testCase.assertResponseHeaders { 1125 if res.Header.Get(key) != value { 1126 t.Logf("expected %s=%s in HTTP response header but got %s", key, value, res.Header.Get(key)) 1127 } 1128 } 1129 }) 1130 } 1131 } 1132 1133 func TestMultiPartUpload(t *testing.T) { 1134 // 1135 verbose := false 1136 // 1137 srv := testutil.NewTestSwarmServer(t, serverFunc) 1138 defer srv.Close() 1139 1140 url := fmt.Sprintf("%s/bzz:/", srv.URL) 1141 1142 buf := new(bytes.Buffer) 1143 form := multipart.NewWriter(buf) 1144 form.WriteField("name", "John Doe") 1145 file1, _ := form.CreateFormFile("cv", "cv.txt") 1146 file1.Write([]byte("John Doe's Credentials")) 1147 file2, _ := form.CreateFormFile("profile_picture", "profile.jpg") 1148 file2.Write([]byte("imaginethisisjpegdata")) 1149 form.Close() 1150 1151 headers := map[string]string{ 1152 "Content-Type": form.FormDataContentType(), 1153 "Content-Length": strconv.Itoa(buf.Len()), 1154 } 1155 res, body := httpDo("POST", url, buf, headers, verbose, t) 1156 1157 if res.StatusCode != 200 { 1158 t.Fatalf("expected POST multipart/form-data to return 200, but it returned %d", res.StatusCode) 1159 } 1160 if len(body) != 64 { 1161 t.Fatalf("expected POST multipart/form-data to return a 64 char manifest but the answer was %d chars long", len(body)) 1162 } 1163 }