github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/swarm/api/api.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-wtc 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-wtc 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 api 18 19 import ( 20 "fmt" 21 "io" 22 "net/http" 23 "regexp" 24 "strings" 25 "sync" 26 27 "bytes" 28 "mime" 29 "path/filepath" 30 "time" 31 32 "github.com/wtc/go-wtc/common" 33 "github.com/wtc/go-wtc/log" 34 "github.com/wtc/go-wtc/swarm/storage" 35 ) 36 37 var hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") 38 39 type Resolver interface { 40 Resolve(string) (common.Hash, error) 41 } 42 43 /* 44 Api implements webserver/file system related content storage and retrieval 45 on top of the dpa 46 it is the public interface of the dpa which is included in the wtc stack 47 */ 48 type Api struct { 49 dpa *storage.DPA 50 dns Resolver 51 } 52 53 //the api constructor initialises 54 func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { 55 self = &Api{ 56 dpa: dpa, 57 dns: dns, 58 } 59 return 60 } 61 62 // to be used only in TEST 63 func (self *Api) Upload(uploadDir, index string) (hash string, err error) { 64 fs := NewFileSystem(self) 65 hash, err = fs.Upload(uploadDir, index) 66 return hash, err 67 } 68 69 // DPA reader API 70 func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { 71 return self.dpa.Retrieve(key) 72 } 73 74 func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { 75 return self.dpa.Store(data, size, wg, nil) 76 } 77 78 type ErrResolve error 79 80 // DNS Resolver 81 func (self *Api) Resolve(uri *URI) (storage.Key, error) { 82 log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr)) 83 84 // if the URI is immutable, check if the address is a hash 85 isHash := hashMatcher.MatchString(uri.Addr) 86 if uri.Immutable() { 87 if !isHash { 88 return nil, fmt.Errorf("immutable address not a content hash: %q", uri.Addr) 89 } 90 return common.Hex2Bytes(uri.Addr), nil 91 } 92 93 // if DNS is not configured, check if the address is a hash 94 if self.dns == nil { 95 if !isHash { 96 return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr) 97 } 98 return common.Hex2Bytes(uri.Addr), nil 99 } 100 101 // try and resolve the address 102 resolved, err := self.dns.Resolve(uri.Addr) 103 if err == nil { 104 return resolved[:], nil 105 } else if !isHash { 106 return nil, err 107 } 108 return common.Hex2Bytes(uri.Addr), nil 109 } 110 111 // Put provides singleton manifest creation on top of dpa store 112 func (self *Api) Put(content, contentType string) (storage.Key, error) { 113 r := strings.NewReader(content) 114 wg := &sync.WaitGroup{} 115 key, err := self.dpa.Store(r, int64(len(content)), wg, nil) 116 if err != nil { 117 return nil, err 118 } 119 manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) 120 r = strings.NewReader(manifest) 121 key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) 122 if err != nil { 123 return nil, err 124 } 125 wg.Wait() 126 return key, nil 127 } 128 129 // Get uses iterative manifest retrieval and prefix matching 130 // to resolve basePath to content using dpa retrieve 131 // it returns a section reader, mimeType, status and an error 132 func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) { 133 trie, err := loadManifest(self.dpa, key, nil) 134 if err != nil { 135 status = http.StatusNotFound 136 log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err)) 137 return 138 } 139 140 log.Trace(fmt.Sprintf("getEntry(%s)", path)) 141 142 entry, _ := trie.getEntry(path) 143 144 if entry != nil { 145 key = common.Hex2Bytes(entry.Hash) 146 status = entry.Status 147 mimeType = entry.ContentType 148 log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType)) 149 reader = self.dpa.Retrieve(key) 150 } else { 151 status = http.StatusNotFound 152 err = fmt.Errorf("manifest entry for '%s' not found", path) 153 log.Warn(fmt.Sprintf("%v", err)) 154 } 155 return 156 } 157 158 func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) { 159 quitC := make(chan bool) 160 trie, err := loadManifest(self.dpa, key, quitC) 161 if err != nil { 162 return nil, err 163 } 164 if contentHash != "" { 165 entry := newManifestTrieEntry(&ManifestEntry{ 166 Path: path, 167 ContentType: contentType, 168 }, nil) 169 entry.Hash = contentHash 170 trie.addEntry(entry, quitC) 171 } else { 172 trie.deleteEntry(path, quitC) 173 } 174 175 if err := trie.recalcAndStore(); err != nil { 176 return nil, err 177 } 178 return trie.hash, nil 179 } 180 181 func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) { 182 183 uri, err := Parse("bzz:/" + mhash) 184 if err != nil { 185 return nil, "", err 186 } 187 mkey, err := self.Resolve(uri) 188 if err != nil { 189 return nil, "", err 190 } 191 192 // trim the root dir we added 193 if path[:1] == "/" { 194 path = path[1:] 195 } 196 197 entry := &ManifestEntry{ 198 Path: filepath.Join(path, fname), 199 ContentType: mime.TypeByExtension(filepath.Ext(fname)), 200 Mode: 0700, 201 Size: int64(len(content)), 202 ModTime: time.Now(), 203 } 204 205 mw, err := self.NewManifestWriter(mkey, nil) 206 if err != nil { 207 return nil, "", err 208 } 209 210 fkey, err := mw.AddEntry(bytes.NewReader(content), entry) 211 if err != nil { 212 return nil, "", err 213 } 214 215 newMkey, err := mw.Store() 216 if err != nil { 217 return nil, "", err 218 219 } 220 221 return fkey, newMkey.String(), nil 222 223 } 224 225 func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) { 226 227 uri, err := Parse("bzz:/" + mhash) 228 if err != nil { 229 return "", err 230 } 231 mkey, err := self.Resolve(uri) 232 if err != nil { 233 return "", err 234 } 235 236 // trim the root dir we added 237 if path[:1] == "/" { 238 path = path[1:] 239 } 240 241 mw, err := self.NewManifestWriter(mkey, nil) 242 if err != nil { 243 return "", err 244 } 245 246 err = mw.RemoveEntry(filepath.Join(path, fname)) 247 if err != nil { 248 return "", err 249 } 250 251 newMkey, err := mw.Store() 252 if err != nil { 253 return "", err 254 255 } 256 257 return newMkey.String(), nil 258 } 259 260 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) { 261 262 buffSize := offset + addSize 263 if buffSize < existingSize { 264 buffSize = existingSize 265 } 266 267 buf := make([]byte, buffSize) 268 269 oldReader := self.Retrieve(oldKey) 270 io.ReadAtLeast(oldReader, buf, int(offset)) 271 272 newReader := bytes.NewReader(content) 273 io.ReadAtLeast(newReader, buf[offset:], int(addSize)) 274 275 if buffSize < existingSize { 276 io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize)) 277 } 278 279 combinedReader := bytes.NewReader(buf) 280 totalSize := int64(len(buf)) 281 282 // TODO(jmozah): to append using pyramid chunker when it is ready 283 //oldReader := self.Retrieve(oldKey) 284 //newReader := bytes.NewReader(content) 285 //combinedReader := io.MultiReader(oldReader, newReader) 286 287 uri, err := Parse("bzz:/" + mhash) 288 if err != nil { 289 return nil, "", err 290 } 291 mkey, err := self.Resolve(uri) 292 if err != nil { 293 return nil, "", err 294 } 295 296 // trim the root dir we added 297 if path[:1] == "/" { 298 path = path[1:] 299 } 300 301 mw, err := self.NewManifestWriter(mkey, nil) 302 if err != nil { 303 return nil, "", err 304 } 305 306 err = mw.RemoveEntry(filepath.Join(path, fname)) 307 if err != nil { 308 return nil, "", err 309 } 310 311 entry := &ManifestEntry{ 312 Path: filepath.Join(path, fname), 313 ContentType: mime.TypeByExtension(filepath.Ext(fname)), 314 Mode: 0700, 315 Size: totalSize, 316 ModTime: time.Now(), 317 } 318 319 fkey, err := mw.AddEntry(io.Reader(combinedReader), entry) 320 if err != nil { 321 return nil, "", err 322 } 323 324 newMkey, err := mw.Store() 325 if err != nil { 326 return nil, "", err 327 328 } 329 330 return fkey, newMkey.String(), nil 331 332 } 333 334 func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) { 335 uri, err := Parse("bzz:/" + mhash) 336 if err != nil { 337 return nil, nil, err 338 } 339 key, err = self.Resolve(uri) 340 if err != nil { 341 return nil, nil, err 342 } 343 344 quitC := make(chan bool) 345 rootTrie, err := loadManifest(self.dpa, key, quitC) 346 if err != nil { 347 return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err) 348 } 349 350 manifestEntryMap = map[string]*manifestTrieEntry{} 351 err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) { 352 manifestEntryMap[suffix] = entry 353 }) 354 355 if err != nil { 356 return nil, nil, fmt.Errorf("list with prefix failed %v: %v", key.String(), err) 357 } 358 return key, manifestEntryMap, nil 359 }