From f087633efdf42f23ada99a5750af30320a7905a8 Mon Sep 17 00:00:00 2001 From: Maran Date: Thu, 5 Jan 2017 11:57:41 +0100 Subject: [PATCH] swarm/api/http: add support for CORS headers (#3388) --- cmd/swarm/main.go | 26 ++++++++++++++++---------- swarm/api/http/server.go | 26 +++++++++++++++++++++++--- swarm/swarm.go | 12 ++++++++++-- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 04930760ee..954ad3b13b 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -82,15 +82,15 @@ var ( Name: "bzzconfig", Usage: "Swarm config file path (datadir/bzz)", } - SwarmSwapEnabled = cli.BoolFlag{ + SwarmSwapEnabledFlag = cli.BoolFlag{ Name: "swap", Usage: "Swarm SWAP enabled (default false)", } - SwarmSyncEnabled = cli.BoolTFlag{ + SwarmSyncEnabledFlag = cli.BoolTFlag{ Name: "sync", Usage: "Swarm Syncing enabled (default true)", } - EthAPI = cli.StringFlag{ + EthAPIFlag = cli.StringFlag{ Name: "ethapi", Usage: "URL of the Ethereum API provider", Value: node.DefaultIPCEndpoint("geth"), @@ -112,6 +112,10 @@ var ( Name: "defaultpath", Usage: "path to file served for empty url path (none)", } + CorsStringFlag = cli.StringFlag{ + Name: "corsdomain", + Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied seperated by a ',')", + } ) func init() { @@ -171,10 +175,11 @@ Prints the swarm hash of file or directory. utils.IPCApiFlag, utils.IPCPathFlag, // bzzd-specific flags - EthAPI, + CorsStringFlag, + EthAPIFlag, SwarmConfigPathFlag, - SwarmSwapEnabled, - SwarmSyncEnabled, + SwarmSwapEnabledFlag, + SwarmSyncEnabledFlag, SwarmPortFlag, SwarmAccountFlag, SwarmNetworkIdFlag, @@ -252,10 +257,11 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { if len(bzzport) > 0 { bzzconfig.Port = bzzport } - swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name) - syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name) + swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name) + syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name) - ethapi := ctx.GlobalString(EthAPI.Name) + ethapi := ctx.GlobalString(EthAPIFlag.Name) + cors := ctx.GlobalString(CorsStringFlag.Name) boot := func(ctx *node.ServiceContext) (node.Service, error) { var client *ethclient.Client @@ -265,7 +271,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { utils.Fatalf("Can't connect: %v", err) } } - return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) + return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors) } if err := stack.Register(boot); err != nil { utils.Fatalf("Failed to register the Swarm service: %v", err) diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go index f82775f254..c8e79ab4ec 100644 --- a/swarm/api/http/server.go +++ b/swarm/api/http/server.go @@ -24,6 +24,7 @@ import ( "io" "net/http" "regexp" + "strings" "sync" "time" @@ -31,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/rs/cors" ) const ( @@ -53,19 +55,37 @@ type sequentialReader struct { lock sync.Mutex } +// Server is the basic configuration needs for the HTTP server and also +// includes CORS settings. +type Server struct { + Addr string + CorsString string +} + // browser API for registering bzz url scheme handlers: // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers // electron (chromium) api for registering bzz url scheme handlers: // https://github.com/atom/electron/blob/master/docs/api/protocol.md // starts up http server -func StartHttpServer(api *api.Api, port string) { +func StartHttpServer(api *api.Api, server *Server) { serveMux := http.NewServeMux() serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { handler(w, r, api) }) - go http.ListenAndServe(":"+port, serveMux) - glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", port) + var allowedOrigins []string + for _, domain := range strings.Split(server.CorsString, ",") { + allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) + } + c := cors.New(cors.Options{ + AllowedOrigins: allowedOrigins, + AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"}, + MaxAge: 600, + }) + hdlr := c.Handler(serveMux) + + go http.ListenAndServe(server.Addr, hdlr) + glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", server.Addr) } func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { diff --git a/swarm/swarm.go b/swarm/swarm.go index 7e38944de0..4b3621affc 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -52,6 +52,7 @@ type Swarm struct { hive *network.Hive // the logistic manager backend chequebook.Backend // simple blockchain Backend privateKey *ecdsa.PrivateKey + corsString string swapEnabled bool } @@ -71,7 +72,7 @@ func (self *Swarm) API() *SwarmAPI { // creates a new swarm service instance // implements node.Service -func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool) (self *Swarm, err error) { +func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool, cors string) (self *Swarm, err error) { if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { return nil, fmt.Errorf("empty public key") } @@ -84,6 +85,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api. swapEnabled: swapEnabled, backend: backend, privateKey: config.Swap.PrivateKey(), + corsString: cors, } glog.V(logger.Debug).Infof("Setting up Swarm service components") @@ -188,10 +190,16 @@ func (self *Swarm) Start(net *p2p.Server) error { // start swarm http proxy server if self.config.Port != "" { - go httpapi.StartHttpServer(self.api, self.config.Port) + addr := ":" + self.config.Port + go httpapi.StartHttpServer(self.api, &httpapi.Server{Addr: addr, CorsString: self.corsString}) } + glog.V(logger.Debug).Infof("Swarm http proxy started on port: %v", self.config.Port) + if self.corsString != "" { + glog.V(logger.Debug).Infof("Swarm http proxy started with corsdomain:", self.corsString) + } + return nil }