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