Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/network"
"gopkg.in/urfave/cli.v1"
)

Expand Down Expand Up @@ -76,7 +75,6 @@ var (
SwarmNetworkIdFlag = cli.IntFlag{
Name: "bzznetworkid",
Usage: "Network identifier (integer, default 3=swarm testnet)",
Value: network.NetworkId,
}
SwarmConfigPathFlag = cli.StringFlag{
Name: "bzzconfig",
Expand Down Expand Up @@ -242,13 +240,15 @@ func bzzd(ctx *cli.Context) error {
}

func registerBzzService(ctx *cli.Context, stack *node.Node) {

prvkey := getAccount(ctx, stack)

chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
if bzzdir == "" {
bzzdir = stack.InstanceDir()
}

bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
if err != nil {
utils.Fatalf("unable to configure swarm: %v", err)
Expand Down Expand Up @@ -280,6 +280,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {

func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
keyid := ctx.GlobalString(SwarmAccountFlag.Name)

if keyid == "" {
utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
}
Expand Down
13 changes: 10 additions & 3 deletions swarm/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package api
import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
"sync"
Expand Down Expand Up @@ -71,6 +72,7 @@ type ErrResolve error

// DNS Resolver
func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) {
glog.V(logger.Detail).Infof("Resolving : %v", hostPort)
if hashMatcher.MatchString(hostPort) || self.dns == nil {
glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort)
return storage.Key(common.Hex2Bytes(hostPort)), nil
Expand All @@ -86,8 +88,10 @@ func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error
glog.V(logger.Detail).Infof("host lookup: %v -> %v", err)
return contentHash[:], err
}

func parse(uri string) (hostPort, path string) {
func Parse(uri string) (hostPort, path string) {
if uri == "" {
return
}
parts := slashes.Split(uri, 3)
var i int
if len(parts) == 0 {
Expand All @@ -111,7 +115,7 @@ func parse(uri string) (hostPort, path string) {
}

func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) {
hostPort, path = parse(uri)
hostPort, path = Parse(uri)
//resolving host and port
contentHash, err := self.Resolve(hostPort, nameresolver)
glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path)
Expand Down Expand Up @@ -153,14 +157,17 @@ func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionR
}

glog.V(logger.Detail).Infof("getEntry(%s)", path)

entry, _ := trie.getEntry(path)

if entry != nil {
key = common.Hex2Bytes(entry.Hash)
status = entry.Status
mimeType = entry.ContentType
glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType)
reader = self.dpa.Retrieve(key)
} else {
status = http.StatusNotFound
err = fmt.Errorf("manifest entry for '%s' not found", path)
glog.V(logger.Warn).Infof("%v", err)
}
Expand Down
14 changes: 14 additions & 0 deletions swarm/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n
NetworkId: networkId,
}
data, err = ioutil.ReadFile(confpath)

// if not set in function param, then set default for swarm network, will be overwritten by config file if present
if networkId == 0 {
self.NetworkId = network.NetworkId
}

if err != nil {
if !os.IsNotExist(err) {
return
}

// file does not exist
// write out config file
err = self.Save()
Expand All @@ -97,6 +104,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n
}
return
}

// file exists, deserialise
err = json.Unmarshal(data, self)
if err != nil {
Expand All @@ -109,6 +117,12 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n
if keyhex != self.BzzKey {
return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey)
}

// if set in function param, replace id set from config file
if networkId != 0 {
self.NetworkId = networkId
}

self.Swap.SetKey(prvKey)

if (self.EnsRoot == common.Address{}) {
Expand Down
32 changes: 25 additions & 7 deletions swarm/api/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/storage"
"github.com/rs/cors"
)

Expand Down Expand Up @@ -194,17 +195,34 @@ func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
}
case r.Method == "GET" || r.Method == "HEAD":
path = trailingSlashes.ReplaceAllString(path, "")
if path == "" {
http.Error(w, "Empty path not allowed", http.StatusBadRequest)
return
}
if raw {
// resolving host
key, err := a.Resolve(path, nameresolver)
if err != nil {
glog.V(logger.Error).Infof("%v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
var reader storage.LazySectionReader
parsedurl, _ := api.Parse(path)

if parsedurl == path {
key, err := a.Resolve(parsedurl, nameresolver)
if err != nil {
glog.V(logger.Error).Infof("%v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
reader = a.Retrieve(key)
} else {
var status int
readertmp, _, status, err := a.Get(path, nameresolver)
if err != nil {
http.Error(w, err.Error(), status)
return
}
reader = readertmp
}

// retrieving content
reader := a.Retrieve(key)

quitC := make(chan bool)
size, err := reader.Size(quitC)
if err != nil {
Expand Down
133 changes: 133 additions & 0 deletions swarm/api/http/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package http
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright header is missing.


import (
"bytes"
"io/ioutil"
"net/http"
"sync"
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/storage"
)

func TestBzzrGetPath(t *testing.T) {

var err error

maxproxyattempts := 3

testmanifest := []string{
`{"entries":[{"path":"a/","hash":"674af7073604ebfc0282a4ab21e5ef1a3c22913866879ebc0816f8a89896b2ed","contentType":"application/bzz-manifest+json","status":0}]}`,
`{"entries":[{"path":"a","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"b/","hash":"0a87b1c3e4bf013686cdf107ec58590f2004610ee58cc2240f26939f691215f5","contentType":"application/bzz-manifest+json","status":0}]}`,
`{"entries":[{"path":"b","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"c","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0}]}`,
}

testrequests := make(map[string]int)
testrequests["/"] = 0
testrequests["/a"] = 1
testrequests["/a/b"] = 2
testrequests["/x"] = 0
testrequests[""] = 0

expectedfailrequests := []string{"", "/x"}

reader := [3]*bytes.Reader{}

key := [3]storage.Key{}

dir, _ := ioutil.TempDir("", "bzz-storage-test")

storeparams := &storage.StoreParams{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go vet complains on travis about anonymous fields for imported struct types. You need to explicitly specify the field names here.

ChunkDbPath: dir,
DbCapacity: 5000000,
CacheCapacity: 5000,
Radius: 0,
}

localStore, err := storage.NewLocalStore(storage.MakeHashFunc("SHA3"), storeparams)
if err != nil {
t.Fatal(err)
}
chunker := storage.NewTreeChunker(storage.NewChunkerParams())
dpa := &storage.DPA{
Chunker: chunker,
ChunkStore: localStore,
}
dpa.Start()
defer dpa.Stop()

wg := &sync.WaitGroup{}

for i, mf := range testmanifest {
reader[i] = bytes.NewReader([]byte(mf))
key[i], err = dpa.Store(reader[i], int64(len(mf)), wg, nil)
if err != nil {
t.Fatal(err)
}
wg.Wait()
}

a := api.NewApi(dpa, nil)

/// \todo iterate port numbers up if fail
StartHttpServer(a, &Server{Addr: "127.0.0.1:8504", CorsString: ""})
// how to wait for ListenAndServe to have initialized? This is pretty cruuuude
// if we fix it we don't need maxproxyattempts anymore either
time.Sleep(1000 * time.Millisecond)
for i := 0; i <= maxproxyattempts; i++ {
_, err := http.Get("http://127.0.0.1:8504/bzzr:/" + common.ToHex(key[0])[2:] + "/a")
if i == maxproxyattempts {
t.Fatalf("Failed to connect to proxy after %v attempts: %v", i, err)
} else if err != nil {
time.Sleep(100 * time.Millisecond)
continue
}
break
}

for k, v := range testrequests {
var resp *http.Response
var respbody []byte

url := "http://127.0.0.1:8504/bzzr:/"
if k[:] != "" {
url += common.ToHex(key[0])[2:] + "/" + k[1:] + "?content_type=text/plain"
}
resp, err = http.Get(url)
defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body)

if string(respbody) != testmanifest[v] {
isexpectedfailrequest := false

for _, r := range expectedfailrequests {
if k[:] == r {
isexpectedfailrequest = true
}
}
if isexpectedfailrequest == false {
t.Fatalf("Response body does not match, expected: %v, got %v", testmanifest[v], string(respbody))
}
}
}

}
5 changes: 2 additions & 3 deletions swarm/api/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man
if (len(path) >= epl) && (path[:epl] == entry.Path) {
glog.V(logger.Detail).Infof("entry.ContentType = %v", entry.ContentType)
if entry.ContentType == manifestType {
if self.loadSubTrie(entry, quitC) != nil {
err := self.loadSubTrie(entry, quitC)
if err != nil {
return nil, 0
}
entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC)
Expand All @@ -312,8 +313,6 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man
} else {
pos = epl
}
} else {
entry = nil
}
return
}
Expand Down