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  }