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