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  }