api.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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. "fmt"
  19. "io"
  20. "net/http"
  21. "path"
  22. "regexp"
  23. "strings"
  24. "sync"
  25. "bytes"
  26. "mime"
  27. "path/filepath"
  28. "time"
  29. "github.com/ethereum/go-ethereum/common"
  30. "github.com/ethereum/go-ethereum/log"
  31. "github.com/ethereum/go-ethereum/metrics"
  32. "github.com/ethereum/go-ethereum/swarm/storage"
  33. )
  34. var hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
  35. //setup metrics
  36. var (
  37. apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
  38. apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
  39. apiPutCount = metrics.NewRegisteredCounter("api.put.count", nil)
  40. apiPutFail = metrics.NewRegisteredCounter("api.put.fail", nil)
  41. apiGetCount = metrics.NewRegisteredCounter("api.get.count", nil)
  42. apiGetNotFound = metrics.NewRegisteredCounter("api.get.notfound", nil)
  43. apiGetHttp300 = metrics.NewRegisteredCounter("api.get.http.300", nil)
  44. apiModifyCount = metrics.NewRegisteredCounter("api.modify.count", nil)
  45. apiModifyFail = metrics.NewRegisteredCounter("api.modify.fail", nil)
  46. apiAddFileCount = metrics.NewRegisteredCounter("api.addfile.count", nil)
  47. apiAddFileFail = metrics.NewRegisteredCounter("api.addfile.fail", nil)
  48. apiRmFileCount = metrics.NewRegisteredCounter("api.removefile.count", nil)
  49. apiRmFileFail = metrics.NewRegisteredCounter("api.removefile.fail", nil)
  50. apiAppendFileCount = metrics.NewRegisteredCounter("api.appendfile.count", nil)
  51. apiAppendFileFail = metrics.NewRegisteredCounter("api.appendfile.fail", nil)
  52. )
  53. type Resolver interface {
  54. Resolve(string) (common.Hash, error)
  55. }
  56. // NoResolverError is returned by MultiResolver.Resolve if no resolver
  57. // can be found for the address.
  58. type NoResolverError struct {
  59. TLD string
  60. }
  61. func NewNoResolverError(tld string) *NoResolverError {
  62. return &NoResolverError{TLD: tld}
  63. }
  64. func (e *NoResolverError) Error() string {
  65. if e.TLD == "" {
  66. return "no ENS resolver"
  67. }
  68. return fmt.Sprintf("no ENS endpoint configured to resolve .%s TLD names", e.TLD)
  69. }
  70. // MultiResolver is used to resolve URL addresses based on their TLDs.
  71. // Each TLD can have multiple resolvers, and the resoluton from the
  72. // first one in the sequence will be returned.
  73. type MultiResolver struct {
  74. resolvers map[string][]Resolver
  75. }
  76. // MultiResolverOption sets options for MultiResolver and is used as
  77. // arguments for its constructor.
  78. type MultiResolverOption func(*MultiResolver)
  79. // MultiResolverOptionWithResolver adds a Resolver to a list of resolvers
  80. // for a specific TLD. If TLD is an empty string, the resolver will be added
  81. // to the list of default resolver, the ones that will be used for resolution
  82. // of addresses which do not have their TLD resolver specified.
  83. func MultiResolverOptionWithResolver(r Resolver, tld string) MultiResolverOption {
  84. return func(m *MultiResolver) {
  85. m.resolvers[tld] = append(m.resolvers[tld], r)
  86. }
  87. }
  88. // NewMultiResolver creates a new instance of MultiResolver.
  89. func NewMultiResolver(opts ...MultiResolverOption) (m *MultiResolver) {
  90. m = &MultiResolver{
  91. resolvers: make(map[string][]Resolver),
  92. }
  93. for _, o := range opts {
  94. o(m)
  95. }
  96. return m
  97. }
  98. // Resolve resolves address by choosing a Resolver by TLD.
  99. // If there are more default Resolvers, or for a specific TLD,
  100. // the Hash from the the first one which does not return error
  101. // will be returned.
  102. func (m MultiResolver) Resolve(addr string) (h common.Hash, err error) {
  103. rs := m.resolvers[""]
  104. tld := path.Ext(addr)
  105. if tld != "" {
  106. tld = tld[1:]
  107. rstld, ok := m.resolvers[tld]
  108. if ok {
  109. rs = rstld
  110. }
  111. }
  112. if rs == nil {
  113. return h, NewNoResolverError(tld)
  114. }
  115. for _, r := range rs {
  116. h, err = r.Resolve(addr)
  117. if err == nil {
  118. return
  119. }
  120. }
  121. return
  122. }
  123. /*
  124. Api implements webserver/file system related content storage and retrieval
  125. on top of the dpa
  126. it is the public interface of the dpa which is included in the ethereum stack
  127. */
  128. type Api struct {
  129. dpa *storage.DPA
  130. dns Resolver
  131. }
  132. //the api constructor initialises
  133. func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
  134. self = &Api{
  135. dpa: dpa,
  136. dns: dns,
  137. }
  138. return
  139. }
  140. // to be used only in TEST
  141. func (self *Api) Upload(uploadDir, index string) (hash string, err error) {
  142. fs := NewFileSystem(self)
  143. hash, err = fs.Upload(uploadDir, index)
  144. return hash, err
  145. }
  146. // DPA reader API
  147. func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader {
  148. return self.dpa.Retrieve(key)
  149. }
  150. func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) {
  151. return self.dpa.Store(data, size, wg, nil)
  152. }
  153. type ErrResolve error
  154. // DNS Resolver
  155. func (self *Api) Resolve(uri *URI) (storage.Key, error) {
  156. apiResolveCount.Inc(1)
  157. log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr))
  158. // if the URI is immutable, check if the address is a hash
  159. isHash := hashMatcher.MatchString(uri.Addr)
  160. if uri.Immutable() || uri.DeprecatedImmutable() {
  161. if !isHash {
  162. return nil, fmt.Errorf("immutable address not a content hash: %q", uri.Addr)
  163. }
  164. return common.Hex2Bytes(uri.Addr), nil
  165. }
  166. // if DNS is not configured, check if the address is a hash
  167. if self.dns == nil {
  168. if !isHash {
  169. apiResolveFail.Inc(1)
  170. return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr)
  171. }
  172. return common.Hex2Bytes(uri.Addr), nil
  173. }
  174. // try and resolve the address
  175. resolved, err := self.dns.Resolve(uri.Addr)
  176. if err == nil {
  177. return resolved[:], nil
  178. } else if !isHash {
  179. apiResolveFail.Inc(1)
  180. return nil, err
  181. }
  182. return common.Hex2Bytes(uri.Addr), nil
  183. }
  184. // Put provides singleton manifest creation on top of dpa store
  185. func (self *Api) Put(content, contentType string) (storage.Key, error) {
  186. apiPutCount.Inc(1)
  187. r := strings.NewReader(content)
  188. wg := &sync.WaitGroup{}
  189. key, err := self.dpa.Store(r, int64(len(content)), wg, nil)
  190. if err != nil {
  191. apiPutFail.Inc(1)
  192. return nil, err
  193. }
  194. manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType)
  195. r = strings.NewReader(manifest)
  196. key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil)
  197. if err != nil {
  198. apiPutFail.Inc(1)
  199. return nil, err
  200. }
  201. wg.Wait()
  202. return key, nil
  203. }
  204. // Get uses iterative manifest retrieval and prefix matching
  205. // to resolve basePath to content using dpa retrieve
  206. // it returns a section reader, mimeType, status and an error
  207. func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
  208. apiGetCount.Inc(1)
  209. trie, err := loadManifest(self.dpa, key, nil)
  210. if err != nil {
  211. apiGetNotFound.Inc(1)
  212. status = http.StatusNotFound
  213. log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err))
  214. return
  215. }
  216. log.Trace(fmt.Sprintf("getEntry(%s)", path))
  217. entry, _ := trie.getEntry(path)
  218. if entry != nil {
  219. key = common.Hex2Bytes(entry.Hash)
  220. status = entry.Status
  221. if status == http.StatusMultipleChoices {
  222. apiGetHttp300.Inc(1)
  223. return
  224. } else {
  225. mimeType = entry.ContentType
  226. log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
  227. reader = self.dpa.Retrieve(key)
  228. }
  229. } else {
  230. status = http.StatusNotFound
  231. apiGetNotFound.Inc(1)
  232. err = fmt.Errorf("manifest entry for '%s' not found", path)
  233. log.Warn(fmt.Sprintf("%v", err))
  234. }
  235. return
  236. }
  237. func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) {
  238. apiModifyCount.Inc(1)
  239. quitC := make(chan bool)
  240. trie, err := loadManifest(self.dpa, key, quitC)
  241. if err != nil {
  242. apiModifyFail.Inc(1)
  243. return nil, err
  244. }
  245. if contentHash != "" {
  246. entry := newManifestTrieEntry(&ManifestEntry{
  247. Path: path,
  248. ContentType: contentType,
  249. }, nil)
  250. entry.Hash = contentHash
  251. trie.addEntry(entry, quitC)
  252. } else {
  253. trie.deleteEntry(path, quitC)
  254. }
  255. if err := trie.recalcAndStore(); err != nil {
  256. apiModifyFail.Inc(1)
  257. return nil, err
  258. }
  259. return trie.hash, nil
  260. }
  261. func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) {
  262. apiAddFileCount.Inc(1)
  263. uri, err := Parse("bzz:/" + mhash)
  264. if err != nil {
  265. apiAddFileFail.Inc(1)
  266. return nil, "", err
  267. }
  268. mkey, err := self.Resolve(uri)
  269. if err != nil {
  270. apiAddFileFail.Inc(1)
  271. return nil, "", err
  272. }
  273. // trim the root dir we added
  274. if path[:1] == "/" {
  275. path = path[1:]
  276. }
  277. entry := &ManifestEntry{
  278. Path: filepath.Join(path, fname),
  279. ContentType: mime.TypeByExtension(filepath.Ext(fname)),
  280. Mode: 0700,
  281. Size: int64(len(content)),
  282. ModTime: time.Now(),
  283. }
  284. mw, err := self.NewManifestWriter(mkey, nil)
  285. if err != nil {
  286. apiAddFileFail.Inc(1)
  287. return nil, "", err
  288. }
  289. fkey, err := mw.AddEntry(bytes.NewReader(content), entry)
  290. if err != nil {
  291. apiAddFileFail.Inc(1)
  292. return nil, "", err
  293. }
  294. newMkey, err := mw.Store()
  295. if err != nil {
  296. apiAddFileFail.Inc(1)
  297. return nil, "", err
  298. }
  299. return fkey, newMkey.String(), nil
  300. }
  301. func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) {
  302. apiRmFileCount.Inc(1)
  303. uri, err := Parse("bzz:/" + mhash)
  304. if err != nil {
  305. apiRmFileFail.Inc(1)
  306. return "", err
  307. }
  308. mkey, err := self.Resolve(uri)
  309. if err != nil {
  310. apiRmFileFail.Inc(1)
  311. return "", err
  312. }
  313. // trim the root dir we added
  314. if path[:1] == "/" {
  315. path = path[1:]
  316. }
  317. mw, err := self.NewManifestWriter(mkey, nil)
  318. if err != nil {
  319. apiRmFileFail.Inc(1)
  320. return "", err
  321. }
  322. err = mw.RemoveEntry(filepath.Join(path, fname))
  323. if err != nil {
  324. apiRmFileFail.Inc(1)
  325. return "", err
  326. }
  327. newMkey, err := mw.Store()
  328. if err != nil {
  329. apiRmFileFail.Inc(1)
  330. return "", err
  331. }
  332. return newMkey.String(), nil
  333. }
  334. func (self *Api) AppendFile(mhash, path, fname string, existingSize int64, content []byte, oldKey storage.Key, offset int64, addSize int64, nameresolver bool) (storage.Key, string, error) {
  335. apiAppendFileCount.Inc(1)
  336. buffSize := offset + addSize
  337. if buffSize < existingSize {
  338. buffSize = existingSize
  339. }
  340. buf := make([]byte, buffSize)
  341. oldReader := self.Retrieve(oldKey)
  342. io.ReadAtLeast(oldReader, buf, int(offset))
  343. newReader := bytes.NewReader(content)
  344. io.ReadAtLeast(newReader, buf[offset:], int(addSize))
  345. if buffSize < existingSize {
  346. io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize))
  347. }
  348. combinedReader := bytes.NewReader(buf)
  349. totalSize := int64(len(buf))
  350. // TODO(jmozah): to append using pyramid chunker when it is ready
  351. //oldReader := self.Retrieve(oldKey)
  352. //newReader := bytes.NewReader(content)
  353. //combinedReader := io.MultiReader(oldReader, newReader)
  354. uri, err := Parse("bzz:/" + mhash)
  355. if err != nil {
  356. apiAppendFileFail.Inc(1)
  357. return nil, "", err
  358. }
  359. mkey, err := self.Resolve(uri)
  360. if err != nil {
  361. apiAppendFileFail.Inc(1)
  362. return nil, "", err
  363. }
  364. // trim the root dir we added
  365. if path[:1] == "/" {
  366. path = path[1:]
  367. }
  368. mw, err := self.NewManifestWriter(mkey, nil)
  369. if err != nil {
  370. apiAppendFileFail.Inc(1)
  371. return nil, "", err
  372. }
  373. err = mw.RemoveEntry(filepath.Join(path, fname))
  374. if err != nil {
  375. apiAppendFileFail.Inc(1)
  376. return nil, "", err
  377. }
  378. entry := &ManifestEntry{
  379. Path: filepath.Join(path, fname),
  380. ContentType: mime.TypeByExtension(filepath.Ext(fname)),
  381. Mode: 0700,
  382. Size: totalSize,
  383. ModTime: time.Now(),
  384. }
  385. fkey, err := mw.AddEntry(io.Reader(combinedReader), entry)
  386. if err != nil {
  387. apiAppendFileFail.Inc(1)
  388. return nil, "", err
  389. }
  390. newMkey, err := mw.Store()
  391. if err != nil {
  392. apiAppendFileFail.Inc(1)
  393. return nil, "", err
  394. }
  395. return fkey, newMkey.String(), nil
  396. }
  397. func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) {
  398. uri, err := Parse("bzz:/" + mhash)
  399. if err != nil {
  400. return nil, nil, err
  401. }
  402. key, err = self.Resolve(uri)
  403. if err != nil {
  404. return nil, nil, err
  405. }
  406. quitC := make(chan bool)
  407. rootTrie, err := loadManifest(self.dpa, key, quitC)
  408. if err != nil {
  409. return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err)
  410. }
  411. manifestEntryMap = map[string]*manifestTrieEntry{}
  412. err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) {
  413. manifestEntryMap[suffix] = entry
  414. })
  415. if err != nil {
  416. return nil, nil, fmt.Errorf("list with prefix failed %v: %v", key.String(), err)
  417. }
  418. return key, manifestEntryMap, nil
  419. }