server_test.go 31 KB

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