api.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. "errors"
  19. "fmt"
  20. "io"
  21. "net/http"
  22. "regexp"
  23. "strings"
  24. "sync"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/log"
  27. "github.com/ethereum/go-ethereum/swarm/storage"
  28. )
  29. var (
  30. hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
  31. slashes = regexp.MustCompile("/+")
  32. domainAndVersion = regexp.MustCompile("[@:;,]+")
  33. )
  34. type Resolver interface {
  35. Resolve(string) (common.Hash, error)
  36. }
  37. /*
  38. Api implements webserver/file system related content storage and retrieval
  39. on top of the dpa
  40. it is the public interface of the dpa which is included in the ethereum stack
  41. */
  42. type Api struct {
  43. dpa *storage.DPA
  44. dns Resolver
  45. }
  46. //the api constructor initialises
  47. func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
  48. self = &Api{
  49. dpa: dpa,
  50. dns: dns,
  51. }
  52. return
  53. }
  54. // DPA reader API
  55. func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader {
  56. return self.dpa.Retrieve(key)
  57. }
  58. func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) {
  59. return self.dpa.Store(data, size, wg, nil)
  60. }
  61. type ErrResolve error
  62. // DNS Resolver
  63. func (self *Api) Resolve(uri *URI) (storage.Key, error) {
  64. log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr))
  65. if hashMatcher.MatchString(uri.Addr) {
  66. log.Trace(fmt.Sprintf("addr is a hash: %q", uri.Addr))
  67. return storage.Key(common.Hex2Bytes(uri.Addr)), nil
  68. }
  69. if uri.Immutable() {
  70. return nil, errors.New("refusing to resolve immutable address")
  71. }
  72. if self.dns == nil {
  73. return nil, fmt.Errorf("unable to resolve addr %q, resolver not configured", uri.Addr)
  74. }
  75. hash, err := self.dns.Resolve(uri.Addr)
  76. if err != nil {
  77. log.Warn(fmt.Sprintf("DNS error resolving addr %q: %s", uri.Addr, err))
  78. return nil, ErrResolve(err)
  79. }
  80. log.Trace(fmt.Sprintf("addr lookup: %v -> %v", uri.Addr, hash))
  81. return hash[:], nil
  82. }
  83. // Put provides singleton manifest creation on top of dpa store
  84. func (self *Api) Put(content, contentType string) (storage.Key, error) {
  85. r := strings.NewReader(content)
  86. wg := &sync.WaitGroup{}
  87. key, err := self.dpa.Store(r, int64(len(content)), wg, nil)
  88. if err != nil {
  89. return nil, err
  90. }
  91. manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType)
  92. r = strings.NewReader(manifest)
  93. key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil)
  94. if err != nil {
  95. return nil, err
  96. }
  97. wg.Wait()
  98. return key, nil
  99. }
  100. // Get uses iterative manifest retrieval and prefix matching
  101. // to resolve path to content using dpa retrieve
  102. // it returns a section reader, mimeType, status and an error
  103. func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
  104. trie, err := loadManifest(self.dpa, key, nil)
  105. if err != nil {
  106. log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err))
  107. return
  108. }
  109. log.Trace(fmt.Sprintf("getEntry(%s)", path))
  110. entry, _ := trie.getEntry(path)
  111. if entry != nil {
  112. key = common.Hex2Bytes(entry.Hash)
  113. status = entry.Status
  114. mimeType = entry.ContentType
  115. log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
  116. reader = self.dpa.Retrieve(key)
  117. } else {
  118. status = http.StatusNotFound
  119. err = fmt.Errorf("manifest entry for '%s' not found", path)
  120. log.Warn(fmt.Sprintf("%v", err))
  121. }
  122. return
  123. }
  124. func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) {
  125. quitC := make(chan bool)
  126. trie, err := loadManifest(self.dpa, key, quitC)
  127. if err != nil {
  128. return nil, err
  129. }
  130. if contentHash != "" {
  131. entry := newManifestTrieEntry(&ManifestEntry{
  132. Path: path,
  133. ContentType: contentType,
  134. }, nil)
  135. entry.Hash = contentHash
  136. trie.addEntry(entry, quitC)
  137. } else {
  138. trie.deleteEntry(path, quitC)
  139. }
  140. if err := trie.recalcAndStore(); err != nil {
  141. return nil, err
  142. }
  143. return trie.hash, nil
  144. }