github.com/anthdm/go-ethereum@v1.8.4-0.20180412101906-60516c83b011/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  	"path"
    24  	"regexp"
    25  	"strings"
    26  	"sync"
    27  
    28  	"bytes"
    29  	"mime"
    30  	"path/filepath"
    31  	"time"
    32  
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/ethereum/go-ethereum/metrics"
    36  	"github.com/ethereum/go-ethereum/swarm/storage"
    37  )
    38  
    39  var hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
    40  
    41  //setup metrics
    42  var (
    43  	apiResolveCount    = metrics.NewRegisteredCounter("api.resolve.count", nil)
    44  	apiResolveFail     = metrics.NewRegisteredCounter("api.resolve.fail", nil)
    45  	apiPutCount        = metrics.NewRegisteredCounter("api.put.count", nil)
    46  	apiPutFail         = metrics.NewRegisteredCounter("api.put.fail", nil)
    47  	apiGetCount        = metrics.NewRegisteredCounter("api.get.count", nil)
    48  	apiGetNotFound     = metrics.NewRegisteredCounter("api.get.notfound", nil)
    49  	apiGetHttp300      = metrics.NewRegisteredCounter("api.get.http.300", nil)
    50  	apiModifyCount     = metrics.NewRegisteredCounter("api.modify.count", nil)
    51  	apiModifyFail      = metrics.NewRegisteredCounter("api.modify.fail", nil)
    52  	apiAddFileCount    = metrics.NewRegisteredCounter("api.addfile.count", nil)
    53  	apiAddFileFail     = metrics.NewRegisteredCounter("api.addfile.fail", nil)
    54  	apiRmFileCount     = metrics.NewRegisteredCounter("api.removefile.count", nil)
    55  	apiRmFileFail      = metrics.NewRegisteredCounter("api.removefile.fail", nil)
    56  	apiAppendFileCount = metrics.NewRegisteredCounter("api.appendfile.count", nil)
    57  	apiAppendFileFail  = metrics.NewRegisteredCounter("api.appendfile.fail", nil)
    58  )
    59  
    60  type Resolver interface {
    61  	Resolve(string) (common.Hash, error)
    62  }
    63  
    64  // NoResolverError is returned by MultiResolver.Resolve if no resolver
    65  // can be found for the address.
    66  type NoResolverError struct {
    67  	TLD string
    68  }
    69  
    70  func NewNoResolverError(tld string) *NoResolverError {
    71  	return &NoResolverError{TLD: tld}
    72  }
    73  
    74  func (e *NoResolverError) Error() string {
    75  	if e.TLD == "" {
    76  		return "no ENS resolver"
    77  	}
    78  	return fmt.Sprintf("no ENS endpoint configured to resolve .%s TLD names", e.TLD)
    79  }
    80  
    81  // MultiResolver is used to resolve URL addresses based on their TLDs.
    82  // Each TLD can have multiple resolvers, and the resoluton from the
    83  // first one in the sequence will be returned.
    84  type MultiResolver struct {
    85  	resolvers map[string][]Resolver
    86  }
    87  
    88  // MultiResolverOption sets options for MultiResolver and is used as
    89  // arguments for its constructor.
    90  type MultiResolverOption func(*MultiResolver)
    91  
    92  // MultiResolverOptionWithResolver adds a Resolver to a list of resolvers
    93  // for a specific TLD. If TLD is an empty string, the resolver will be added
    94  // to the list of default resolver, the ones that will be used for resolution
    95  // of addresses which do not have their TLD resolver specified.
    96  func MultiResolverOptionWithResolver(r Resolver, tld string) MultiResolverOption {
    97  	return func(m *MultiResolver) {
    98  		m.resolvers[tld] = append(m.resolvers[tld], r)
    99  	}
   100  }
   101  
   102  // NewMultiResolver creates a new instance of MultiResolver.
   103  func NewMultiResolver(opts ...MultiResolverOption) (m *MultiResolver) {
   104  	m = &MultiResolver{
   105  		resolvers: make(map[string][]Resolver),
   106  	}
   107  	for _, o := range opts {
   108  		o(m)
   109  	}
   110  	return m
   111  }
   112  
   113  // Resolve resolves address by choosing a Resolver by TLD.
   114  // If there are more default Resolvers, or for a specific TLD,
   115  // the Hash from the the first one which does not return error
   116  // will be returned.
   117  func (m MultiResolver) Resolve(addr string) (h common.Hash, err error) {
   118  	rs := m.resolvers[""]
   119  	tld := path.Ext(addr)
   120  	if tld != "" {
   121  		tld = tld[1:]
   122  		rstld, ok := m.resolvers[tld]
   123  		if ok {
   124  			rs = rstld
   125  		}
   126  	}
   127  	if rs == nil {
   128  		return h, NewNoResolverError(tld)
   129  	}
   130  	for _, r := range rs {
   131  		h, err = r.Resolve(addr)
   132  		if err == nil {
   133  			return
   134  		}
   135  	}
   136  	return
   137  }
   138  
   139  /*
   140  Api implements webserver/file system related content storage and retrieval
   141  on top of the dpa
   142  it is the public interface of the dpa which is included in the ethereum stack
   143  */
   144  type Api struct {
   145  	dpa *storage.DPA
   146  	dns Resolver
   147  }
   148  
   149  //the api constructor initialises
   150  func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
   151  	self = &Api{
   152  		dpa: dpa,
   153  		dns: dns,
   154  	}
   155  	return
   156  }
   157  
   158  // to be used only in TEST
   159  func (self *Api) Upload(uploadDir, index string) (hash string, err error) {
   160  	fs := NewFileSystem(self)
   161  	hash, err = fs.Upload(uploadDir, index)
   162  	return hash, err
   163  }
   164  
   165  // DPA reader API
   166  func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader {
   167  	return self.dpa.Retrieve(key)
   168  }
   169  
   170  func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) {
   171  	return self.dpa.Store(data, size, wg, nil)
   172  }
   173  
   174  type ErrResolve error
   175  
   176  // DNS Resolver
   177  func (self *Api) Resolve(uri *URI) (storage.Key, error) {
   178  	apiResolveCount.Inc(1)
   179  	log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr))
   180  
   181  	// if the URI is immutable, check if the address is a hash
   182  	isHash := hashMatcher.MatchString(uri.Addr)
   183  	if uri.Immutable() || uri.DeprecatedImmutable() {
   184  		if !isHash {
   185  			return nil, fmt.Errorf("immutable address not a content hash: %q", uri.Addr)
   186  		}
   187  		return common.Hex2Bytes(uri.Addr), nil
   188  	}
   189  
   190  	// if DNS is not configured, check if the address is a hash
   191  	if self.dns == nil {
   192  		if !isHash {
   193  			apiResolveFail.Inc(1)
   194  			return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr)
   195  		}
   196  		return common.Hex2Bytes(uri.Addr), nil
   197  	}
   198  
   199  	// try and resolve the address
   200  	resolved, err := self.dns.Resolve(uri.Addr)
   201  	if err == nil {
   202  		return resolved[:], nil
   203  	} else if !isHash {
   204  		apiResolveFail.Inc(1)
   205  		return nil, err
   206  	}
   207  	return common.Hex2Bytes(uri.Addr), nil
   208  }
   209  
   210  // Put provides singleton manifest creation on top of dpa store
   211  func (self *Api) Put(content, contentType string) (storage.Key, error) {
   212  	apiPutCount.Inc(1)
   213  	r := strings.NewReader(content)
   214  	wg := &sync.WaitGroup{}
   215  	key, err := self.dpa.Store(r, int64(len(content)), wg, nil)
   216  	if err != nil {
   217  		apiPutFail.Inc(1)
   218  		return nil, err
   219  	}
   220  	manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType)
   221  	r = strings.NewReader(manifest)
   222  	key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil)
   223  	if err != nil {
   224  		apiPutFail.Inc(1)
   225  		return nil, err
   226  	}
   227  	wg.Wait()
   228  	return key, nil
   229  }
   230  
   231  // Get uses iterative manifest retrieval and prefix matching
   232  // to resolve basePath to content using dpa retrieve
   233  // it returns a section reader, mimeType, status and an error
   234  func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
   235  	apiGetCount.Inc(1)
   236  	trie, err := loadManifest(self.dpa, key, nil)
   237  	if err != nil {
   238  		apiGetNotFound.Inc(1)
   239  		status = http.StatusNotFound
   240  		log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err))
   241  		return
   242  	}
   243  
   244  	log.Trace(fmt.Sprintf("getEntry(%s)", path))
   245  
   246  	entry, _ := trie.getEntry(path)
   247  
   248  	if entry != nil {
   249  		key = common.Hex2Bytes(entry.Hash)
   250  		status = entry.Status
   251  		if status == http.StatusMultipleChoices {
   252  			apiGetHttp300.Inc(1)
   253  			return
   254  		} else {
   255  			mimeType = entry.ContentType
   256  			log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
   257  			reader = self.dpa.Retrieve(key)
   258  		}
   259  	} else {
   260  		status = http.StatusNotFound
   261  		apiGetNotFound.Inc(1)
   262  		err = fmt.Errorf("manifest entry for '%s' not found", path)
   263  		log.Warn(fmt.Sprintf("%v", err))
   264  	}
   265  	return
   266  }
   267  
   268  func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) {
   269  	apiModifyCount.Inc(1)
   270  	quitC := make(chan bool)
   271  	trie, err := loadManifest(self.dpa, key, quitC)
   272  	if err != nil {
   273  		apiModifyFail.Inc(1)
   274  		return nil, err
   275  	}
   276  	if contentHash != "" {
   277  		entry := newManifestTrieEntry(&ManifestEntry{
   278  			Path:        path,
   279  			ContentType: contentType,
   280  		}, nil)
   281  		entry.Hash = contentHash
   282  		trie.addEntry(entry, quitC)
   283  	} else {
   284  		trie.deleteEntry(path, quitC)
   285  	}
   286  
   287  	if err := trie.recalcAndStore(); err != nil {
   288  		apiModifyFail.Inc(1)
   289  		return nil, err
   290  	}
   291  	return trie.hash, nil
   292  }
   293  
   294  func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) {
   295  	apiAddFileCount.Inc(1)
   296  
   297  	uri, err := Parse("bzz:/" + mhash)
   298  	if err != nil {
   299  		apiAddFileFail.Inc(1)
   300  		return nil, "", err
   301  	}
   302  	mkey, err := self.Resolve(uri)
   303  	if err != nil {
   304  		apiAddFileFail.Inc(1)
   305  		return nil, "", err
   306  	}
   307  
   308  	// trim the root dir we added
   309  	if path[:1] == "/" {
   310  		path = path[1:]
   311  	}
   312  
   313  	entry := &ManifestEntry{
   314  		Path:        filepath.Join(path, fname),
   315  		ContentType: mime.TypeByExtension(filepath.Ext(fname)),
   316  		Mode:        0700,
   317  		Size:        int64(len(content)),
   318  		ModTime:     time.Now(),
   319  	}
   320  
   321  	mw, err := self.NewManifestWriter(mkey, nil)
   322  	if err != nil {
   323  		apiAddFileFail.Inc(1)
   324  		return nil, "", err
   325  	}
   326  
   327  	fkey, err := mw.AddEntry(bytes.NewReader(content), entry)
   328  	if err != nil {
   329  		apiAddFileFail.Inc(1)
   330  		return nil, "", err
   331  	}
   332  
   333  	newMkey, err := mw.Store()
   334  	if err != nil {
   335  		apiAddFileFail.Inc(1)
   336  		return nil, "", err
   337  
   338  	}
   339  
   340  	return fkey, newMkey.String(), nil
   341  
   342  }
   343  
   344  func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) {
   345  	apiRmFileCount.Inc(1)
   346  
   347  	uri, err := Parse("bzz:/" + mhash)
   348  	if err != nil {
   349  		apiRmFileFail.Inc(1)
   350  		return "", err
   351  	}
   352  	mkey, err := self.Resolve(uri)
   353  	if err != nil {
   354  		apiRmFileFail.Inc(1)
   355  		return "", err
   356  	}
   357  
   358  	// trim the root dir we added
   359  	if path[:1] == "/" {
   360  		path = path[1:]
   361  	}
   362  
   363  	mw, err := self.NewManifestWriter(mkey, nil)
   364  	if err != nil {
   365  		apiRmFileFail.Inc(1)
   366  		return "", err
   367  	}
   368  
   369  	err = mw.RemoveEntry(filepath.Join(path, fname))
   370  	if err != nil {
   371  		apiRmFileFail.Inc(1)
   372  		return "", err
   373  	}
   374  
   375  	newMkey, err := mw.Store()
   376  	if err != nil {
   377  		apiRmFileFail.Inc(1)
   378  		return "", err
   379  
   380  	}
   381  
   382  	return newMkey.String(), nil
   383  }
   384  
   385  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) {
   386  	apiAppendFileCount.Inc(1)
   387  
   388  	buffSize := offset + addSize
   389  	if buffSize < existingSize {
   390  		buffSize = existingSize
   391  	}
   392  
   393  	buf := make([]byte, buffSize)
   394  
   395  	oldReader := self.Retrieve(oldKey)
   396  	io.ReadAtLeast(oldReader, buf, int(offset))
   397  
   398  	newReader := bytes.NewReader(content)
   399  	io.ReadAtLeast(newReader, buf[offset:], int(addSize))
   400  
   401  	if buffSize < existingSize {
   402  		io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize))
   403  	}
   404  
   405  	combinedReader := bytes.NewReader(buf)
   406  	totalSize := int64(len(buf))
   407  
   408  	// TODO(jmozah): to append using pyramid chunker when it is ready
   409  	//oldReader := self.Retrieve(oldKey)
   410  	//newReader := bytes.NewReader(content)
   411  	//combinedReader := io.MultiReader(oldReader, newReader)
   412  
   413  	uri, err := Parse("bzz:/" + mhash)
   414  	if err != nil {
   415  		apiAppendFileFail.Inc(1)
   416  		return nil, "", err
   417  	}
   418  	mkey, err := self.Resolve(uri)
   419  	if err != nil {
   420  		apiAppendFileFail.Inc(1)
   421  		return nil, "", err
   422  	}
   423  
   424  	// trim the root dir we added
   425  	if path[:1] == "/" {
   426  		path = path[1:]
   427  	}
   428  
   429  	mw, err := self.NewManifestWriter(mkey, nil)
   430  	if err != nil {
   431  		apiAppendFileFail.Inc(1)
   432  		return nil, "", err
   433  	}
   434  
   435  	err = mw.RemoveEntry(filepath.Join(path, fname))
   436  	if err != nil {
   437  		apiAppendFileFail.Inc(1)
   438  		return nil, "", err
   439  	}
   440  
   441  	entry := &ManifestEntry{
   442  		Path:        filepath.Join(path, fname),
   443  		ContentType: mime.TypeByExtension(filepath.Ext(fname)),
   444  		Mode:        0700,
   445  		Size:        totalSize,
   446  		ModTime:     time.Now(),
   447  	}
   448  
   449  	fkey, err := mw.AddEntry(io.Reader(combinedReader), entry)
   450  	if err != nil {
   451  		apiAppendFileFail.Inc(1)
   452  		return nil, "", err
   453  	}
   454  
   455  	newMkey, err := mw.Store()
   456  	if err != nil {
   457  		apiAppendFileFail.Inc(1)
   458  		return nil, "", err
   459  
   460  	}
   461  
   462  	return fkey, newMkey.String(), nil
   463  
   464  }
   465  
   466  func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) {
   467  
   468  	uri, err := Parse("bzz:/" + mhash)
   469  	if err != nil {
   470  		return nil, nil, err
   471  	}
   472  	key, err = self.Resolve(uri)
   473  	if err != nil {
   474  		return nil, nil, err
   475  	}
   476  
   477  	quitC := make(chan bool)
   478  	rootTrie, err := loadManifest(self.dpa, key, quitC)
   479  	if err != nil {
   480  		return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err)
   481  	}
   482  
   483  	manifestEntryMap = map[string]*manifestTrieEntry{}
   484  	err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) {
   485  		manifestEntryMap[suffix] = entry
   486  	})
   487  
   488  	if err != nil {
   489  		return nil, nil, fmt.Errorf("list with prefix failed %v: %v", key.String(), err)
   490  	}
   491  	return key, manifestEntryMap, nil
   492  }