diff --git a/cmd/swarm/global-store/explorer.go b/cmd/swarm/global-store/explorer.go new file mode 100644 index 0000000000..634ff1ebb6 --- /dev/null +++ b/cmd/swarm/global-store/explorer.go @@ -0,0 +1,66 @@ +// Copyright 2019 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 ( + "context" + "fmt" + "net" + "net/http" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/swarm/storage/mock" + "github.com/ethereum/go-ethereum/swarm/storage/mock/explorer" + cli "gopkg.in/urfave/cli.v1" +) + +// serveChunkExplorer starts an http server in background with chunk explorer handler +// using the provided global store. Server is started if the returned shutdown function +// is not nil. +func serveChunkExplorer(ctx *cli.Context, globalStore mock.GlobalStorer) (shutdown func(), err error) { + if !ctx.IsSet("explorer-address") { + return nil, nil + } + + corsOrigins := ctx.StringSlice("explorer-cors-origin") + server := &http.Server{ + Handler: explorer.NewHandler(globalStore, corsOrigins), + IdleTimeout: 30 * time.Minute, + ReadTimeout: 2 * time.Minute, + WriteTimeout: 2 * time.Minute, + } + listener, err := net.Listen("tcp", ctx.String("explorer-address")) + if err != nil { + return nil, fmt.Errorf("explorer: %v", err) + } + log.Info("chunk explorer http", "address", listener.Addr().String(), "origins", corsOrigins) + + go func() { + if err := server.Serve(listener); err != nil { + log.Error("chunk explorer", "err", err) + } + }() + + return func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(ctx); err != nil { + log.Error("chunk explorer: shutdown", "err", err) + } + }, nil +} diff --git a/cmd/swarm/global-store/global_store.go b/cmd/swarm/global-store/global_store.go index a55756e1c4..f93b464dbc 100644 --- a/cmd/swarm/global-store/global_store.go +++ b/cmd/swarm/global-store/global_store.go @@ -17,6 +17,7 @@ package main import ( + "io" "net" "net/http" "os" @@ -66,7 +67,7 @@ func startWS(ctx *cli.Context) (err error) { return http.Serve(listener, server.WebsocketHandler(origins)) } -// newServer creates a global store and returns its RPC server. +// newServer creates a global store and starts a chunk explorer server if configured. // Returned cleanup function should be called only if err is nil. func newServer(ctx *cli.Context) (server *rpc.Server, cleanup func(), err error) { log.PrintOrigins(true) @@ -81,7 +82,9 @@ func newServer(ctx *cli.Context) (server *rpc.Server, cleanup func(), err error) return nil, nil, err } cleanup = func() { - dbStore.Close() + if err := dbStore.Close(); err != nil { + log.Error("global store: close", "err", err) + } } globalStore = dbStore log.Info("database global store", "dir", dir) @@ -96,5 +99,22 @@ func newServer(ctx *cli.Context) (server *rpc.Server, cleanup func(), err error) return nil, nil, err } + shutdown, err := serveChunkExplorer(ctx, globalStore) + if err != nil { + cleanup() + return nil, nil, err + } + if shutdown != nil { + cleanup = func() { + shutdown() + + if c, ok := globalStore.(io.Closer); ok { + if err := c.Close(); err != nil { + log.Error("global store: close", "err", err) + } + } + } + } + return server, cleanup, nil } diff --git a/cmd/swarm/global-store/main.go b/cmd/swarm/global-store/main.go index 51df0099ac..52fafc8f6a 100644 --- a/cmd/swarm/global-store/main.go +++ b/cmd/swarm/global-store/main.go @@ -19,12 +19,14 @@ package main import ( "os" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/log" cli "gopkg.in/urfave/cli.v1" ) -var gitCommit string // Git SHA1 commit hash of the release (set via linker flags) +var ( + version = "0.1" + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) +) func main() { err := newApp().Run(os.Args) @@ -37,16 +39,30 @@ func main() { // newApp construct a new instance of Swarm Global Store. // Method Run is called on it in the main function and in tests. func newApp() (app *cli.App) { - app = utils.NewApp(gitCommit, "Swarm Global Store") - + app = cli.NewApp() app.Name = "global-store" + app.Version = version + if len(gitCommit) >= 8 { + app.Version += "-" + gitCommit[:8] + } + app.Usage = "Swarm Global Store" // app flags (for all commands) app.Flags = []cli.Flag{ cli.IntFlag{ Name: "verbosity", Value: 3, - Usage: "verbosity level", + Usage: "Verbosity level.", + }, + cli.StringFlag{ + Name: "explorer-address", + Value: "", + Usage: "Chunk explorer HTTP listener address.", + }, + cli.StringSliceFlag{ + Name: "explorer-cors-origin", + Value: nil, + Usage: "Chunk explorer CORS origin (can be specified multiple times).", }, } @@ -54,7 +70,7 @@ func newApp() (app *cli.App) { { Name: "http", Aliases: []string{"h"}, - Usage: "start swarm global store with http server", + Usage: "Start swarm global store with HTTP server.", Action: startHTTP, // Flags only for "start" command. // Allow app flags to be specified after the @@ -63,19 +79,19 @@ func newApp() (app *cli.App) { cli.StringFlag{ Name: "dir", Value: "", - Usage: "data directory", + Usage: "Data directory.", }, cli.StringFlag{ Name: "addr", Value: "0.0.0.0:3033", - Usage: "address to listen for http connection", + Usage: "Address to listen for HTTP connections.", }, ), }, { Name: "websocket", Aliases: []string{"ws"}, - Usage: "start swarm global store with websocket server", + Usage: "Start swarm global store with WebSocket server.", Action: startWS, // Flags only for "start" command. // Allow app flags to be specified after the @@ -84,17 +100,17 @@ func newApp() (app *cli.App) { cli.StringFlag{ Name: "dir", Value: "", - Usage: "data directory", + Usage: "Data directory.", }, cli.StringFlag{ Name: "addr", Value: "0.0.0.0:3033", - Usage: "address to listen for websocket connection", + Usage: "Address to listen for WebSocket connections.", }, cli.StringSliceFlag{ - Name: "origins", - Value: &cli.StringSlice{"*"}, - Usage: "websocket origins", + Name: "origin", + Value: nil, + Usage: "WebSocket CORS origin (can be specified multiple times).", }, ), },