github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/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 "regexp" 23 "strings" 24 "sync" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/logger" 28 "github.com/ethereum/go-ethereum/logger/glog" 29 "github.com/ethereum/go-ethereum/swarm/storage" 30 ) 31 32 var ( 33 hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") 34 slashes = regexp.MustCompile("/+") 35 domainAndVersion = regexp.MustCompile("[@:;,]+") 36 ) 37 38 type Resolver interface { 39 Resolve(string) (common.Hash, error) 40 } 41 42 /* 43 Api implements webserver/file system related content storage and retrieval 44 on top of the dpa 45 it is the public interface of the dpa which is included in the ethereum stack 46 */ 47 type Api struct { 48 dpa *storage.DPA 49 dns Resolver 50 } 51 52 //the api constructor initialises 53 func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { 54 self = &Api{ 55 dpa: dpa, 56 dns: dns, 57 } 58 return 59 } 60 61 // DPA reader API 62 func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { 63 return self.dpa.Retrieve(key) 64 } 65 66 func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { 67 return self.dpa.Store(data, size, wg, nil) 68 } 69 70 type ErrResolve error 71 72 // DNS Resolver 73 func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { 74 if hashMatcher.MatchString(hostPort) || self.dns == nil { 75 glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort) 76 return storage.Key(common.Hex2Bytes(hostPort)), nil 77 } 78 if !nameresolver { 79 return nil, fmt.Errorf("'%s' is not a content hash value.", hostPort) 80 } 81 contentHash, err := self.dns.Resolve(hostPort) 82 if err != nil { 83 err = ErrResolve(err) 84 glog.V(logger.Warn).Infof("DNS error : %v", err) 85 } 86 glog.V(logger.Detail).Infof("host lookup: %v -> %v", err) 87 return contentHash[:], err 88 } 89 90 func parse(uri string) (hostPort, path string) { 91 parts := slashes.Split(uri, 3) 92 var i int 93 if len(parts) == 0 { 94 return 95 } 96 // beginning with slash is now optional 97 for len(parts[i]) == 0 { 98 i++ 99 } 100 hostPort = parts[i] 101 for i < len(parts)-1 { 102 i++ 103 if len(path) > 0 { 104 path = path + "/" + parts[i] 105 } else { 106 path = parts[i] 107 } 108 } 109 glog.V(logger.Debug).Infof("host: '%s', path '%s' requested.", hostPort, path) 110 return 111 } 112 113 func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { 114 hostPort, path = parse(uri) 115 //resolving host and port 116 contentHash, err := self.Resolve(hostPort, nameresolver) 117 glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path) 118 return contentHash[:], hostPort, path, err 119 } 120 121 // Put provides singleton manifest creation on top of dpa store 122 func (self *Api) Put(content, contentType string) (string, error) { 123 r := strings.NewReader(content) 124 wg := &sync.WaitGroup{} 125 key, err := self.dpa.Store(r, int64(len(content)), wg, nil) 126 if err != nil { 127 return "", err 128 } 129 manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) 130 r = strings.NewReader(manifest) 131 key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) 132 if err != nil { 133 return "", err 134 } 135 wg.Wait() 136 return key.String(), nil 137 } 138 139 // Get uses iterative manifest retrieval and prefix matching 140 // to resolve path to content using dpa retrieve 141 // it returns a section reader, mimeType, status and an error 142 func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionReader, mimeType string, status int, err error) { 143 key, _, path, err := self.parseAndResolve(uri, nameresolver) 144 if err != nil { 145 return nil, "", 500, fmt.Errorf("can't resolve: %v", err) 146 } 147 148 quitC := make(chan bool) 149 trie, err := loadManifest(self.dpa, key, quitC) 150 if err != nil { 151 glog.V(logger.Warn).Infof("loadManifestTrie error: %v", err) 152 return 153 } 154 155 glog.V(logger.Detail).Infof("getEntry(%s)", path) 156 entry, _ := trie.getEntry(path) 157 if entry != nil { 158 key = common.Hex2Bytes(entry.Hash) 159 status = entry.Status 160 mimeType = entry.ContentType 161 glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType) 162 reader = self.dpa.Retrieve(key) 163 } else { 164 err = fmt.Errorf("manifest entry for '%s' not found", path) 165 glog.V(logger.Warn).Infof("%v", err) 166 } 167 return 168 } 169 170 func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) { 171 root, _, path, err := self.parseAndResolve(uri, nameresolver) 172 if err != nil { 173 return "", fmt.Errorf("can't resolve: %v", err) 174 } 175 176 quitC := make(chan bool) 177 trie, err := loadManifest(self.dpa, root, quitC) 178 if err != nil { 179 return 180 } 181 182 if contentHash != "" { 183 entry := &manifestTrieEntry{ 184 Path: path, 185 Hash: contentHash, 186 ContentType: contentType, 187 } 188 trie.addEntry(entry, quitC) 189 } else { 190 trie.deleteEntry(path, quitC) 191 } 192 193 err = trie.recalcAndStore() 194 if err != nil { 195 return 196 } 197 return trie.hash.String(), nil 198 }