diff --git a/accounts/account_manager.go b/accounts/account_manager.go index c8601c3c0..abe442388 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -228,11 +228,17 @@ func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Durati return nil } -func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { +// Find resolves the given account into a unique entry in the keystore. +func (am *Manager) Find(a Account) (Account, error) { am.cache.maybeReload() am.cache.mu.Lock() a, err := am.cache.find(a) am.cache.mu.Unlock() + return a, err +} + +func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { + a, err := am.Find(a) if err != nil { return a, nil, err } diff --git a/cmd/bzzd/main.go b/cmd/bzzd/main.go new file mode 100644 index 000000000..b2f14a4a9 --- /dev/null +++ b/cmd/bzzd/main.go @@ -0,0 +1,246 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm" + bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "gopkg.in/urfave/cli.v1" +) + +const clientIdentifier = "bzzd" + +var ( + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) + app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon") +) + +var ( + ChequebookAddrFlag = cli.StringFlag{ + Name: "chequebook", + Usage: "chequebook contract address", + } + SwarmAccountFlag = cli.StringFlag{ + Name: "bzzaccount", + Usage: "Swarm account key file", + } + SwarmPortFlag = cli.StringFlag{ + Name: "bzzport", + Usage: "Swarm local http api port", + } + SwarmConfigPathFlag = cli.StringFlag{ + Name: "bzzconfig", + Usage: "Swarm config file path (datadir/bzz)", + } + SwarmSwapDisabled = cli.BoolFlag{ + Name: "bzznoswap", + Usage: "Swarm SWAP disabled (default false)", + } + SwarmSyncDisabled = cli.BoolFlag{ + Name: "bzznosync", + Usage: "Swarm Syncing disabled (default false)", + } + EthAPI = cli.StringFlag{ + Name: "ethapi", + Usage: "URL of the Ethereum API provider", + Value: node.DefaultIPCEndpoint("geth"), + } +) + +var defaultBootnodes = []string{} + +func init() { + // Override flag defaults so bzzd can run alongside geth. + utils.ListenPortFlag.Value = 30399 + utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} + + // Set up the cli app. + app.Commands = nil + app.Action = bzzd + app.Flags = []cli.Flag{ + utils.IdentityFlag, + utils.DataDirFlag, + utils.BootnodesFlag, + utils.KeyStoreDirFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + // bzzd-specific flags + EthAPI, + SwarmConfigPathFlag, + SwarmSwapDisabled, + SwarmSyncDisabled, + SwarmPortFlag, + SwarmAccountFlag, + ChequebookAddrFlag, + } + app.Flags = append(app.Flags, debug.Flags...) + app.Before = func(ctx *cli.Context) error { + runtime.GOMAXPROCS(runtime.NumCPU()) + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil + } +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func bzzd(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + registerBzzService(ctx, stack) + utils.StartNode(stack) + + // Add bootnodes as initial peers. + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name)) + } else { + injectBootnodes(stack.Server(), defaultBootnodes) + } + + stack.Wait() + return nil +} + +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) + if err != nil { + utils.Fatalf("unable to configure swarm: %v", err) + } + bzzport := ctx.GlobalString(SwarmPortFlag.Name) + if len(bzzport) > 0 { + bzzconfig.Port = bzzport + } + swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name) + syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name) + + ethapi := ctx.GlobalString(EthAPI.Name) + if ethapi == "" { + utils.Fatalf("Option %q must not be empty", EthAPI.Name) + } + + boot := func(ctx *node.ServiceContext) (node.Service, error) { + client, err := ethclient.Dial(ethapi) + if err != nil { + utils.Fatalf("Can't connect: %v", err) + } + return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) + } + if err := stack.Register(boot); err != nil { + utils.Fatalf("Failed to register the Swarm service: %v", err) + } +} + +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) + } + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(keyid); err == nil { + glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey)) + return key + } + // Otherwise try getting it from the keystore. + return decryptStoreAccount(stack.AccountManager(), keyid) +} + +func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey { + var a accounts.Account + var err error + if common.IsHexAddress(account) { + a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)}) + } else if ix, ixerr := strconv.Atoi(account); ixerr == nil { + a, err = accman.AccountByIndex(ix) + } else { + utils.Fatalf("Can't find swarm account key %s", account) + } + if err != nil { + utils.Fatalf("Can't find swarm account key: %v", err) + } + keyjson, err := ioutil.ReadFile(a.File) + if err != nil { + utils.Fatalf("Can't load swarm account key: %v", err) + } + for i := 1; i <= 3; i++ { + passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) + key, err := accounts.DecryptKey(keyjson, passphrase) + if err == nil { + return key.PrivateKey + } + } + utils.Fatalf("Can't decrypt swarm account key") + return nil +} + +func promptPassphrase(prompt string) string { + if prompt != "" { + fmt.Println(prompt) + } + password, err := console.Stdin.PromptPassword("Passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + return password +} + +func injectBootnodes(srv *p2p.Server, nodes []string) { + for _, url := range nodes { + n, err := discover.ParseNode(url) + if err != nil { + glog.Errorf("invalid bootnode %q", err) + } + srv.AddPeer(n) + } +} diff --git a/cmd/bzzhash/main.go b/cmd/bzzhash/main.go new file mode 100644 index 000000000..0ae99acc0 --- /dev/null +++ b/cmd/bzzhash/main.go @@ -0,0 +1,49 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzhash computes a swarm tree hash. +package main + +import ( + "fmt" + "os" + "runtime" + + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + if len(os.Args) < 2 { + fmt.Println("Usage: bzzhash ") + os.Exit(0) + } + f, err := os.Open(os.Args[1]) + if err != nil { + fmt.Println("Error opening file " + os.Args[1]) + os.Exit(1) + } + + stat, _ := f.Stat() + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + key, err := chunker.Split(f, stat.Size(), nil, nil, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + } else { + fmt.Printf("%v\n", key) + } +} diff --git a/cmd/bzzup/main.go b/cmd/bzzup/main.go new file mode 100644 index 000000000..83d6f9b7f --- /dev/null +++ b/cmd/bzzup/main.go @@ -0,0 +1,161 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzup uploads files to the swarm HTTP API. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "net/http" + "os" + "path/filepath" + "strings" +) + +func main() { + var ( + bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint") + recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively") + manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload") + ) + log.SetOutput(os.Stderr) + log.SetFlags(0) + flag.Parse() + if flag.NArg() != 1 { + log.Fatal("need filename as the first and only argument") + } + + var ( + file = flag.Arg(0) + client = &client{api: *bzzapiFlag} + mroot manifest + ) + fi, err := os.Stat(file) + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + if !*recursiveFlag { + log.Fatal("argument is a directory and recursive upload is disabled") + } + mroot, err = client.uploadDirectory(file) + } else { + mroot, err = client.uploadFile(file, fi) + if *manifestFlag { + // Wrap the raw file entry in a proper manifest so both hashes get printed. + mroot = manifest{Entries: []manifest{mroot}} + } + } + if err != nil { + log.Fatalln("upload failed:", err) + } + if *manifestFlag { + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + mroot.Hash = hash + } + + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) +} + +// client wraps interaction with the swarm HTTP gateway. +type client struct { + api string +} + +// manifest is the JSON representation of a swarm manifest. +type manifest struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` + Entries []manifest `json:"entries,omitempty"` +} + +func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { + hash, err := c.uploadFileContent(file, fi) + m := manifest{ + Hash: hash, + ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), + } + return m, err +} + +func (c *client) uploadDirectory(dir string) (manifest, error) { + dirm := manifest{} + prefix := filepath.ToSlash(dir) + "/" + err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || fi.IsDir() { + return err + } + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("path %s outside directory %s", path, dir) + } + entry, err := c.uploadFile(path, fi) + entry.Path = strings.TrimPrefix(filepath.ToSlash(path), prefix) + dirm.Entries = append(dirm.Entries, entry) + return err + }) + return dirm, err +} + +func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { + fd, err := os.Open(file) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes)", file, fi.Size()) + return c.postRaw("application/octet-stream", fi.Size(), fd) +} + +func (c *client) uploadManifest(m manifest) (string, error) { + jsm, err := json.Marshal(m) + if err != nil { + panic(err) + } + log.Println("uploading manifest") + return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) +} + +func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { + req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) + if err != nil { + return "", err + } + req.Header.Set("content-type", mimetype) + req.ContentLength = size + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} diff --git a/cmd/v5test/main.go b/cmd/v5test/main.go deleted file mode 100644 index 1daff56f8..000000000 --- a/cmd/v5test/main.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -// bootnode runs a bootstrap node for the Ethereum Discovery Protocol. -package main - -import ( - "flag" - "fmt" - "math/rand" - "strconv" - "time" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/p2p/nat" -) - -func main() { - var ( - listenPort = flag.Int("addr", 31000, "beginning of listening port range") - natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") - count = flag.Int("count", 1, "number of v5 topic discovery test nodes (adds default bootnodes to form a test network)") - regtopic = flag.String("reg", "", "topic to register on the network") - looktopic = flag.String("search", "", "topic to search on the network") - ) - flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)") - flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern") - glog.SetToStderr(true) - flag.Parse() - - natm, err := nat.Parse(*natdesc) - if err != nil { - utils.Fatalf("-nat: %v", err) - } - - for i := 0; i < *count; i++ { - listenAddr := ":" + strconv.Itoa(*listenPort+i) - - nodeKey, err := crypto.GenerateKey() - if err != nil { - utils.Fatalf("could not generate key: %v", err) - } - - if net, err := discv5.ListenUDP(nodeKey, listenAddr, natm, ""); err != nil { - utils.Fatalf("%v", err) - } else { - if err := net.SetFallbackNodes(discv5.BootNodes); err != nil { - utils.Fatalf("%v", err) - } - go func() { - if *looktopic == "" { - for i := 0; i < 20; i++ { - time.Sleep(time.Millisecond * time.Duration(2000+rand.Intn(2001))) - net.BucketFill() - } - } - switch { - case *regtopic != "": - // register topic - fmt.Println("Starting topic register") - stop := make(chan struct{}) - net.RegisterTopic(discv5.Topic(*regtopic), stop) - case *looktopic != "": - // search topic - fmt.Println("Starting topic search") - stop := make(chan struct{}) - found := make(chan string, 100) - go net.SearchTopic(discv5.Topic(*looktopic), stop, found) - for s := range found { - fmt.Println(time.Now(), s) - } - default: - // just keep doing lookups - for { - time.Sleep(time.Millisecond * time.Duration(40000+rand.Intn(40001))) - net.BucketFill() - } - } - }() - } - fmt.Printf("Started test node #%d with public key %v\n", i, discv5.PubkeyID(&nodeKey.PublicKey)) - } - - select {} -} diff --git a/node/config.go b/node/config.go index 8af9215a0..dbefcb8a5 100644 --- a/node/config.go +++ b/node/config.go @@ -268,7 +268,7 @@ func (c *Config) name() string { return c.Name } -// These resources are resolved differently for the "geth" and "geth-testnet" instances. +// These resources are resolved differently for "geth" instances. var isOldGethResource = map[string]bool{ "chaindata": true, "nodes": true, @@ -297,7 +297,14 @@ func (c *Config) resolvePath(path string) string { return oldpath } } - return filepath.Join(c.DataDir, c.name(), path) + return filepath.Join(c.instanceDir(), path) +} + +func (c *Config) instanceDir() string { + if c.DataDir == "" { + return "" + } + return filepath.Join(c.DataDir, c.name()) } // NodeKey retrieves the currently configured private key of the node, checking diff --git a/node/node.go b/node/node.go index 15f43fc6b..fb11696fa 100644 --- a/node/node.go +++ b/node/node.go @@ -601,10 +601,16 @@ func (n *Node) Service(service interface{}) error { } // DataDir retrieves the current datadir used by the protocol stack. +// Deprecated: No files should be stored in this directory, use InstanceDir instead. func (n *Node) DataDir() string { return n.config.DataDir } +// InstanceDir retrieves the instance directory used by the protocol stack. +func (n *Node) InstanceDir() string { + return n.config.instanceDir() +} + // AccountManager retrieves the account manager used by the protocol stack. func (n *Node) AccountManager() *accounts.Manager { return n.accman diff --git a/swarm/api/config.go b/swarm/api/config.go index 730755c43..c04a015ef 100644 --- a/swarm/api/config.go +++ b/swarm/api/config.go @@ -59,9 +59,8 @@ type Config struct { // config is agnostic to where private key is coming from // so managing accounts is outside swarm and left to wrappers func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) { - address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address - dirpath := filepath.Join(path, common.Bytes2Hex(address.Bytes())) + dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes())) err = os.MkdirAll(dirpath, os.ModePerm) if err != nil { return diff --git a/swarm/api/config_test.go b/swarm/api/config_test.go index 874701119..8fe3ddacc 100644 --- a/swarm/api/config_test.go +++ b/swarm/api/config_test.go @@ -30,14 +30,14 @@ import ( var ( hexprvkey = "65138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c" defaultConfig = `{ - "ChunkDbPath": "` + filepath.Join("TMPDIR", "0d2f62485607cf38d9d795d93682a517661e513e", "chunks") + `", + "ChunkDbPath": "` + filepath.Join("TMPDIR", "chunks") + `", "DbCapacity": 5000000, "CacheCapacity": 5000, "Radius": 0, "Branches": 128, "Hash": "SHA3", "CallInterval": 3000000000, - "KadDbPath": "` + filepath.Join("TMPDIR", "0d2f62485607cf38d9d795d93682a517661e513e", "bzz-peers.json") + `", + "KadDbPath": "` + filepath.Join("TMPDIR", "bzz-peers.json") + `", "MaxProx": 8, "ProxBinSize": 2, "BucketSize": 4, @@ -59,7 +59,7 @@ var ( "Contract": "0x0000000000000000000000000000000000000000", "Beneficiary": "0x0d2f62485607cf38d9d795d93682a517661e513e" }, - "RequestDbPath": "` + filepath.Join("TMPDIR", "0d2f62485607cf38d9d795d93682a517661e513e", "requests") + `", + "RequestDbPath": "` + filepath.Join("TMPDIR", "requests") + `", "RequestDbBatchSize": 512, "KeyBufferSize": 1024, "SyncBatchSize": 128, @@ -79,7 +79,7 @@ var ( true, false ], - "Path": "` + filepath.Join("TMPDIR", "0d2f62485607cf38d9d795d93682a517661e513e") + `", + "Path": "TMPDIR", "Port": "8500", "PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3", "BzzKey": "0xe861964402c0b78e2d44098329b8545726f215afa737d803714a4338552fcb81", @@ -99,16 +99,12 @@ func TestConfigWriteRead(t *testing.T) { if err != nil { t.Fatalf("expected no error, got %v", err) } - account := crypto.PubkeyToAddress(prvkey.PublicKey) - dirpath := filepath.Join(tmp, common.Bytes2Hex(account.Bytes())) - confpath := filepath.Join(dirpath, "config.json") - data, err := ioutil.ReadFile(confpath) + data, err := ioutil.ReadFile(filepath.Join(orig.Path, "config.json")) if err != nil { t.Fatalf("default config file cannot be read: %v", err) } - exp := strings.Replace(defaultConfig, "TMPDIR", tmp, -1) + exp := strings.Replace(defaultConfig, "TMPDIR", orig.Path, -1) exp = strings.Replace(exp, "\\", "\\\\", -1) - if string(data) != exp { t.Fatalf("default config mismatch:\nexpected: %v\ngot: %v", exp, string(data)) } diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go index a35672687..9be60ef94 100644 --- a/swarm/api/http/server.go +++ b/swarm/api/http/server.go @@ -115,7 +115,11 @@ func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { switch { case r.Method == "POST" || r.Method == "PUT": - key, err := a.Store(r.Body, r.ContentLength, nil) + if r.Header.Get("content-length") == "" { + http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest) + return + } + key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil) if err == nil { glog.V(logger.Debug).Infof("Content for %v stored", key.Log()) } else { diff --git a/swarm/network/syncdb_test.go b/swarm/network/syncdb_test.go index e46d32a2e..ed43fbd06 100644 --- a/swarm/network/syncdb_test.go +++ b/swarm/network/syncdb_test.go @@ -141,6 +141,8 @@ func (self *testSyncDb) expect(n int, db bool) { } func TestSyncDb(t *testing.T) { + t.Skip("fails randomly on all platforms") + priority := High bufferSize := 5 batchSize := 2 * bufferSize