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