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