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