api_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. // Copyright 2016 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 api
  17. import (
  18. "bytes"
  19. "context"
  20. crand "crypto/rand"
  21. "errors"
  22. "flag"
  23. "fmt"
  24. "io"
  25. "io/ioutil"
  26. "math/big"
  27. "os"
  28. "strings"
  29. "testing"
  30. "github.com/ethereum/go-ethereum/common"
  31. "github.com/ethereum/go-ethereum/core/types"
  32. "github.com/ethereum/go-ethereum/log"
  33. "github.com/ethereum/go-ethereum/swarm/chunk"
  34. "github.com/ethereum/go-ethereum/swarm/sctx"
  35. "github.com/ethereum/go-ethereum/swarm/storage"
  36. "github.com/ethereum/go-ethereum/swarm/testutil"
  37. )
  38. func init() {
  39. loglevel := flag.Int("loglevel", 2, "loglevel")
  40. flag.Parse()
  41. log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))
  42. }
  43. func testAPI(t *testing.T, f func(*API, *chunk.Tags, bool)) {
  44. for _, v := range []bool{true, false} {
  45. datadir, err := ioutil.TempDir("", "bzz-test")
  46. if err != nil {
  47. t.Fatalf("unable to create temp dir: %v", err)
  48. }
  49. defer os.RemoveAll(datadir)
  50. tags := chunk.NewTags()
  51. fileStore, err := storage.NewLocalFileStore(datadir, make([]byte, 32), tags)
  52. if err != nil {
  53. return
  54. }
  55. api := NewAPI(fileStore, nil, nil, nil, tags)
  56. f(api, tags, v)
  57. }
  58. }
  59. type testResponse struct {
  60. reader storage.LazySectionReader
  61. *Response
  62. }
  63. type Response struct {
  64. MimeType string
  65. Status int
  66. Size int64
  67. Content string
  68. }
  69. func checkResponse(t *testing.T, resp *testResponse, exp *Response) {
  70. if resp.MimeType != exp.MimeType {
  71. t.Errorf("incorrect mimeType. expected '%s', got '%s'", exp.MimeType, resp.MimeType)
  72. }
  73. if resp.Status != exp.Status {
  74. t.Errorf("incorrect status. expected '%d', got '%d'", exp.Status, resp.Status)
  75. }
  76. if resp.Size != exp.Size {
  77. t.Errorf("incorrect size. expected '%d', got '%d'", exp.Size, resp.Size)
  78. }
  79. if resp.reader != nil {
  80. content := make([]byte, resp.Size)
  81. read, _ := resp.reader.Read(content)
  82. if int64(read) != exp.Size {
  83. t.Errorf("incorrect content length. expected '%d...', got '%d...'", read, exp.Size)
  84. }
  85. resp.Content = string(content)
  86. }
  87. if resp.Content != exp.Content {
  88. // if !bytes.Equal(resp.Content, exp.Content)
  89. t.Errorf("incorrect content. expected '%s...', got '%s...'", string(exp.Content), string(resp.Content))
  90. }
  91. }
  92. // func expResponse(content []byte, mimeType string, status int) *Response {
  93. func expResponse(content string, mimeType string, status int) *Response {
  94. log.Trace(fmt.Sprintf("expected content (%v): %v ", len(content), content))
  95. return &Response{mimeType, status, int64(len(content)), content}
  96. }
  97. func testGet(t *testing.T, api *API, bzzhash, path string) *testResponse {
  98. addr := storage.Address(common.Hex2Bytes(bzzhash))
  99. reader, mimeType, status, _, err := api.Get(context.TODO(), NOOPDecrypt, addr, path)
  100. if err != nil {
  101. t.Fatalf("unexpected error: %v", err)
  102. }
  103. quitC := make(chan bool)
  104. size, err := reader.Size(context.TODO(), quitC)
  105. if err != nil {
  106. t.Fatalf("unexpected error: %v", err)
  107. }
  108. log.Trace(fmt.Sprintf("reader size: %v ", size))
  109. s := make([]byte, size)
  110. _, err = reader.Read(s)
  111. if err != io.EOF {
  112. t.Fatalf("unexpected error: %v", err)
  113. }
  114. reader.Seek(0, 0)
  115. return &testResponse{reader, &Response{mimeType, status, size, string(s)}}
  116. }
  117. func TestApiPut(t *testing.T) {
  118. testAPI(t, func(api *API, tags *chunk.Tags, toEncrypt bool) {
  119. content := "hello"
  120. exp := expResponse(content, "text/plain", 0)
  121. ctx := context.TODO()
  122. addr, wait, err := putString(ctx, api, content, exp.MimeType, toEncrypt)
  123. if err != nil {
  124. t.Fatalf("unexpected error: %v", err)
  125. }
  126. err = wait(ctx)
  127. if err != nil {
  128. t.Fatalf("unexpected error: %v", err)
  129. }
  130. resp := testGet(t, api, addr.Hex(), "")
  131. checkResponse(t, resp, exp)
  132. tag := tags.All()[0]
  133. testutil.CheckTag(t, tag, 2, 2, 0, 2) //1 chunk data, 1 chunk manifest
  134. })
  135. }
  136. // TestApiTagLarge tests that the the number of chunks counted is larger for a larger input
  137. func TestApiTagLarge(t *testing.T) {
  138. const contentLength = 4096 * 4095
  139. testAPI(t, func(api *API, tags *chunk.Tags, toEncrypt bool) {
  140. randomContentReader := io.LimitReader(crand.Reader, int64(contentLength))
  141. tag, err := api.Tags.New("unnamed-tag", 0)
  142. if err != nil {
  143. t.Fatal(err)
  144. }
  145. ctx := sctx.SetTag(context.Background(), tag.Uid)
  146. key, waitContent, err := api.Store(ctx, randomContentReader, int64(contentLength), toEncrypt)
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. err = waitContent(ctx)
  151. if err != nil {
  152. t.Fatal(err)
  153. }
  154. tag.DoneSplit(key)
  155. if toEncrypt {
  156. tag := tags.All()[0]
  157. expect := int64(4095 + 64 + 1)
  158. testutil.CheckTag(t, tag, expect, expect, 0, expect)
  159. } else {
  160. tag := tags.All()[0]
  161. expect := int64(4095 + 32 + 1)
  162. testutil.CheckTag(t, tag, expect, expect, 0, expect)
  163. }
  164. })
  165. }
  166. // testResolver implements the Resolver interface and either returns the given
  167. // hash if it is set, or returns a "name not found" error
  168. type testResolveValidator struct {
  169. hash *common.Hash
  170. }
  171. func newTestResolveValidator(addr string) *testResolveValidator {
  172. r := &testResolveValidator{}
  173. if addr != "" {
  174. hash := common.HexToHash(addr)
  175. r.hash = &hash
  176. }
  177. return r
  178. }
  179. func (t *testResolveValidator) Resolve(addr string) (common.Hash, error) {
  180. if t.hash == nil {
  181. return common.Hash{}, fmt.Errorf("DNS name not found: %q", addr)
  182. }
  183. return *t.hash, nil
  184. }
  185. func (t *testResolveValidator) Owner(node [32]byte) (addr common.Address, err error) {
  186. return
  187. }
  188. func (t *testResolveValidator) HeaderByNumber(context.Context, *big.Int) (header *types.Header, err error) {
  189. return
  190. }
  191. // TestAPIResolve tests resolving URIs which can either contain content hashes
  192. // or ENS names
  193. func TestAPIResolve(t *testing.T) {
  194. ensAddr := "swarm.eth"
  195. hashAddr := "1111111111111111111111111111111111111111111111111111111111111111"
  196. resolvedAddr := "2222222222222222222222222222222222222222222222222222222222222222"
  197. doesResolve := newTestResolveValidator(resolvedAddr)
  198. doesntResolve := newTestResolveValidator("")
  199. type test struct {
  200. desc string
  201. dns Resolver
  202. addr string
  203. immutable bool
  204. result string
  205. expectErr error
  206. }
  207. tests := []*test{
  208. {
  209. desc: "DNS not configured, hash address, returns hash address",
  210. dns: nil,
  211. addr: hashAddr,
  212. result: hashAddr,
  213. },
  214. {
  215. desc: "DNS not configured, ENS address, returns error",
  216. dns: nil,
  217. addr: ensAddr,
  218. expectErr: errors.New(`no DNS to resolve name: "swarm.eth"`),
  219. },
  220. {
  221. desc: "DNS configured, hash address, hash resolves, returns resolved address",
  222. dns: doesResolve,
  223. addr: hashAddr,
  224. result: resolvedAddr,
  225. },
  226. {
  227. desc: "DNS configured, immutable hash address, hash resolves, returns hash address",
  228. dns: doesResolve,
  229. addr: hashAddr,
  230. immutable: true,
  231. result: hashAddr,
  232. },
  233. {
  234. desc: "DNS configured, hash address, hash doesn't resolve, returns hash address",
  235. dns: doesntResolve,
  236. addr: hashAddr,
  237. result: hashAddr,
  238. },
  239. {
  240. desc: "DNS configured, ENS address, name resolves, returns resolved address",
  241. dns: doesResolve,
  242. addr: ensAddr,
  243. result: resolvedAddr,
  244. },
  245. {
  246. desc: "DNS configured, immutable ENS address, name resolves, returns error",
  247. dns: doesResolve,
  248. addr: ensAddr,
  249. immutable: true,
  250. expectErr: errors.New(`immutable address not a content hash: "swarm.eth"`),
  251. },
  252. {
  253. desc: "DNS configured, ENS address, name doesn't resolve, returns error",
  254. dns: doesntResolve,
  255. addr: ensAddr,
  256. expectErr: errors.New(`DNS name not found: "swarm.eth"`),
  257. },
  258. }
  259. for _, x := range tests {
  260. t.Run(x.desc, func(t *testing.T) {
  261. api := &API{dns: x.dns}
  262. uri := &URI{Addr: x.addr, Scheme: "bzz"}
  263. if x.immutable {
  264. uri.Scheme = "bzz-immutable"
  265. }
  266. res, err := api.ResolveURI(context.TODO(), uri, "")
  267. if err == nil {
  268. if x.expectErr != nil {
  269. t.Fatalf("expected error %q, got result %q", x.expectErr, res)
  270. }
  271. if res.String() != x.result {
  272. t.Fatalf("expected result %q, got %q", x.result, res)
  273. }
  274. } else {
  275. if x.expectErr == nil {
  276. t.Fatalf("expected no error, got %q", err)
  277. }
  278. if err.Error() != x.expectErr.Error() {
  279. t.Fatalf("expected error %q, got %q", x.expectErr, err)
  280. }
  281. }
  282. })
  283. }
  284. }
  285. func TestMultiResolver(t *testing.T) {
  286. doesntResolve := newTestResolveValidator("")
  287. ethAddr := "swarm.eth"
  288. ethHash := "0x2222222222222222222222222222222222222222222222222222222222222222"
  289. ethResolve := newTestResolveValidator(ethHash)
  290. testAddr := "swarm.test"
  291. testHash := "0x1111111111111111111111111111111111111111111111111111111111111111"
  292. testResolve := newTestResolveValidator(testHash)
  293. tests := []struct {
  294. desc string
  295. r Resolver
  296. addr string
  297. result string
  298. err error
  299. }{
  300. {
  301. desc: "No resolvers, returns error",
  302. r: NewMultiResolver(),
  303. err: NewNoResolverError(""),
  304. },
  305. {
  306. desc: "One default resolver, returns resolved address",
  307. r: NewMultiResolver(MultiResolverOptionWithResolver(ethResolve, "")),
  308. addr: ethAddr,
  309. result: ethHash,
  310. },
  311. {
  312. desc: "Two default resolvers, returns resolved address",
  313. r: NewMultiResolver(
  314. MultiResolverOptionWithResolver(ethResolve, ""),
  315. MultiResolverOptionWithResolver(ethResolve, ""),
  316. ),
  317. addr: ethAddr,
  318. result: ethHash,
  319. },
  320. {
  321. desc: "Two default resolvers, first doesn't resolve, returns resolved address",
  322. r: NewMultiResolver(
  323. MultiResolverOptionWithResolver(doesntResolve, ""),
  324. MultiResolverOptionWithResolver(ethResolve, ""),
  325. ),
  326. addr: ethAddr,
  327. result: ethHash,
  328. },
  329. {
  330. desc: "Default resolver doesn't resolve, tld resolver resolve, returns resolved address",
  331. r: NewMultiResolver(
  332. MultiResolverOptionWithResolver(doesntResolve, ""),
  333. MultiResolverOptionWithResolver(ethResolve, "eth"),
  334. ),
  335. addr: ethAddr,
  336. result: ethHash,
  337. },
  338. {
  339. desc: "Three TLD resolvers, third resolves, returns resolved address",
  340. r: NewMultiResolver(
  341. MultiResolverOptionWithResolver(doesntResolve, "eth"),
  342. MultiResolverOptionWithResolver(doesntResolve, "eth"),
  343. MultiResolverOptionWithResolver(ethResolve, "eth"),
  344. ),
  345. addr: ethAddr,
  346. result: ethHash,
  347. },
  348. {
  349. desc: "One TLD resolver doesn't resolve, returns error",
  350. r: NewMultiResolver(
  351. MultiResolverOptionWithResolver(doesntResolve, ""),
  352. MultiResolverOptionWithResolver(ethResolve, "eth"),
  353. ),
  354. addr: ethAddr,
  355. result: ethHash,
  356. },
  357. {
  358. desc: "One defautl and one TLD resolver, all doesn't resolve, returns error",
  359. r: NewMultiResolver(
  360. MultiResolverOptionWithResolver(doesntResolve, ""),
  361. MultiResolverOptionWithResolver(doesntResolve, "eth"),
  362. ),
  363. addr: ethAddr,
  364. result: ethHash,
  365. err: errors.New(`DNS name not found: "swarm.eth"`),
  366. },
  367. {
  368. desc: "Two TLD resolvers, both resolve, returns resolved address",
  369. r: NewMultiResolver(
  370. MultiResolverOptionWithResolver(ethResolve, "eth"),
  371. MultiResolverOptionWithResolver(testResolve, "test"),
  372. ),
  373. addr: testAddr,
  374. result: testHash,
  375. },
  376. {
  377. desc: "One TLD resolver, no default resolver, returns error for different TLD",
  378. r: NewMultiResolver(
  379. MultiResolverOptionWithResolver(ethResolve, "eth"),
  380. ),
  381. addr: testAddr,
  382. err: NewNoResolverError("test"),
  383. },
  384. }
  385. for _, x := range tests {
  386. t.Run(x.desc, func(t *testing.T) {
  387. res, err := x.r.Resolve(x.addr)
  388. if err == nil {
  389. if x.err != nil {
  390. t.Fatalf("expected error %q, got result %q", x.err, res.Hex())
  391. }
  392. if res.Hex() != x.result {
  393. t.Fatalf("expected result %q, got %q", x.result, res.Hex())
  394. }
  395. } else {
  396. if x.err == nil {
  397. t.Fatalf("expected no error, got %q", err)
  398. }
  399. if err.Error() != x.err.Error() {
  400. t.Fatalf("expected error %q, got %q", x.err, err)
  401. }
  402. }
  403. })
  404. }
  405. }
  406. func TestDecryptOriginForbidden(t *testing.T) {
  407. ctx := context.TODO()
  408. ctx = sctx.SetHost(ctx, "swarm-gateways.net")
  409. me := &ManifestEntry{
  410. Access: &AccessEntry{Type: AccessTypePass},
  411. }
  412. api := NewAPI(nil, nil, nil, nil, chunk.NewTags())
  413. f := api.Decryptor(ctx, "")
  414. err := f(me)
  415. if err != ErrDecryptDomainForbidden {
  416. t.Fatalf("should fail with ErrDecryptDomainForbidden, got %v", err)
  417. }
  418. }
  419. func TestDecryptOrigin(t *testing.T) {
  420. for _, v := range []struct {
  421. host string
  422. expectError error
  423. }{
  424. {
  425. host: "localhost",
  426. expectError: ErrDecrypt,
  427. },
  428. {
  429. host: "127.0.0.1",
  430. expectError: ErrDecrypt,
  431. },
  432. {
  433. host: "swarm-gateways.net",
  434. expectError: ErrDecryptDomainForbidden,
  435. },
  436. } {
  437. ctx := context.TODO()
  438. ctx = sctx.SetHost(ctx, v.host)
  439. me := &ManifestEntry{
  440. Access: &AccessEntry{Type: AccessTypePass},
  441. }
  442. api := NewAPI(nil, nil, nil, nil, chunk.NewTags())
  443. f := api.Decryptor(ctx, "")
  444. err := f(me)
  445. if err != v.expectError {
  446. t.Fatalf("should fail with %v, got %v", v.expectError, err)
  447. }
  448. }
  449. }
  450. func TestDetectContentType(t *testing.T) {
  451. for _, tc := range []struct {
  452. file string
  453. content string
  454. expectedContentType string
  455. }{
  456. {
  457. file: "file-with-correct-css.css",
  458. content: "body {background-color: orange}",
  459. expectedContentType: "text/css; charset=utf-8",
  460. },
  461. {
  462. file: "empty-file.css",
  463. content: "",
  464. expectedContentType: "text/css; charset=utf-8",
  465. },
  466. {
  467. file: "empty-file.pdf",
  468. content: "",
  469. expectedContentType: "application/pdf",
  470. },
  471. {
  472. file: "empty-file.md",
  473. content: "",
  474. expectedContentType: "text/markdown; charset=utf-8",
  475. },
  476. {
  477. file: "empty-file-with-unknown-content.strangeext",
  478. content: "",
  479. expectedContentType: "text/plain; charset=utf-8",
  480. },
  481. {
  482. file: "file-with-unknown-extension-and-content.strangeext",
  483. content: "Lorem Ipsum",
  484. expectedContentType: "text/plain; charset=utf-8",
  485. },
  486. {
  487. file: "file-no-extension",
  488. content: "Lorem Ipsum",
  489. expectedContentType: "text/plain; charset=utf-8",
  490. },
  491. {
  492. file: "file-no-extension-no-content",
  493. content: "",
  494. expectedContentType: "text/plain; charset=utf-8",
  495. },
  496. {
  497. file: "css-file-with-html-inside.css",
  498. content: "<!doctype html><html><head></head><body></body></html>",
  499. expectedContentType: "text/css; charset=utf-8",
  500. },
  501. } {
  502. t.Run(tc.file, func(t *testing.T) {
  503. detected, err := DetectContentType(tc.file, bytes.NewReader([]byte(tc.content)))
  504. if err != nil {
  505. t.Fatal(err)
  506. }
  507. if detected != tc.expectedContentType {
  508. t.Fatalf("File: %s, Expected mime type %s, got %s", tc.file, tc.expectedContentType, detected)
  509. }
  510. })
  511. }
  512. }
  513. // putString provides singleton manifest creation on top of api.API
  514. func putString(ctx context.Context, a *API, content string, contentType string, toEncrypt bool) (k storage.Address, wait func(context.Context) error, err error) {
  515. r := strings.NewReader(content)
  516. tag, err := a.Tags.New("unnamed-tag", 0)
  517. log.Trace("created new tag", "uid", tag.Uid)
  518. cCtx := sctx.SetTag(ctx, tag.Uid)
  519. key, waitContent, err := a.Store(cCtx, r, int64(len(content)), toEncrypt)
  520. if err != nil {
  521. return nil, nil, err
  522. }
  523. manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType)
  524. r = strings.NewReader(manifest)
  525. key, waitManifest, err := a.Store(cCtx, r, int64(len(manifest)), toEncrypt)
  526. if err != nil {
  527. return nil, nil, err
  528. }
  529. tag.DoneSplit(key)
  530. return key, func(ctx context.Context) error {
  531. err := waitContent(ctx)
  532. if err != nil {
  533. return err
  534. }
  535. return waitManifest(ctx)
  536. }, nil
  537. }