diff options
-rw-r--r-- | cmd/geth/main.go | 5 | ||||
-rw-r--r-- | cmd/gethrpctest/main.go | 37 | ||||
-rw-r--r-- | cmd/utils/flags.go | 48 | ||||
-rw-r--r-- | common/defaults.go | 48 | ||||
-rw-r--r-- | common/path.go | 22 | ||||
-rw-r--r-- | node/api.go | 46 | ||||
-rw-r--r-- | node/config.go | 37 | ||||
-rw-r--r-- | node/node.go | 182 | ||||
-rw-r--r-- | rpc/http.go | 61 | ||||
-rw-r--r-- | rpc/utils.go | 3 |
10 files changed, 274 insertions, 215 deletions
diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fa456a7ac..a321181a1 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -503,11 +503,6 @@ func startNode(ctx *cli.Context, stack *node.Node) { } } // Start auxiliary services if enabled - if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { - if err := utils.StartRPC(stack, ctx); err != nil { - utils.Fatalf("Failed to start RPC: %v", err) - } - } if ctx.GlobalBool(utils.WSEnabledFlag.Name) { if err := utils.StartWS(stack, ctx); err != nil { utils.Fatalf("Failed to start WS: %v", err) diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go index becd09f5a..b0907f8c5 100644 --- a/cmd/gethrpctest/main.go +++ b/cmd/gethrpctest/main.go @@ -18,7 +18,6 @@ package main import ( - "errors" "flag" "io/ioutil" "log" @@ -26,10 +25,10 @@ import ( "os/signal" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/tests" @@ -84,12 +83,6 @@ func main() { } log.Println("Initial test suite passed...") - // Start the RPC interface and wait until terminated - if err := StartRPC(stack); err != nil { - log.Fatalf("Failed to start RPC interface: %v", err) - } - log.Println("RPC Interface started, accepting requests...") - quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit @@ -99,7 +92,13 @@ func main() { // keystore path and initial pre-state. func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node.Node, error) { // Create a networkless protocol stack - stack, err := node.New(&node.Config{IpcPath: node.DefaultIpcEndpoint(), NoDiscovery: true}) + stack, err := node.New(&node.Config{ + IpcPath: node.DefaultIpcEndpoint(), + HttpHost: common.DefaultHttpHost, + HttpPort: common.DefaultHttpPort, + HttpModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"}, + NoDiscovery: true, + }) if err != nil { return nil, err } @@ -164,23 +163,3 @@ func RunTest(stack *node.Node, test *tests.BlockTest) error { } return nil } - -// StartRPC initializes an RPC interface to the given protocol stack. -func StartRPC(stack *node.Node) error { - /* - web3 := NewPublicWeb3API(stack) - server.RegisterName("web3", web3) - net := NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) - server.RegisterName("net", net) - */ - - for _, api := range stack.APIs() { - if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok { - _, err := adminApi.StartRPC("127.0.0.1", 8545, "", "admin,db,eth,debug,miner,net,shh,txpool,personal,web3") - return err - } - } - - glog.V(logger.Error).Infof("Unable to start RPC-HTTP interface, could not find admin API") - return errors.New("Unable to start RPC-HTTP interface") -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5d56ba7d0..40ea29d78 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -233,12 +233,12 @@ var ( RPCListenAddrFlag = cli.StringFlag{ Name: "rpcaddr", Usage: "HTTP-RPC server listening interface", - Value: "127.0.0.1", + Value: common.DefaultHttpHost, } RPCPortFlag = cli.IntFlag{ Name: "rpcport", Usage: "HTTP-RPC server listening port", - Value: 8545, + Value: common.DefaultHttpPort, } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", @@ -262,7 +262,7 @@ var ( IPCPathFlag = DirectoryFlag{ Name: "ipcpath", Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", - Value: DirectoryString{common.DefaultIpcSocket()}, + Value: DirectoryString{common.DefaultIpcSocket}, } WSEnabledFlag = cli.BoolFlag{ Name: "ws", @@ -271,12 +271,12 @@ var ( WSListenAddrFlag = cli.StringFlag{ Name: "wsaddr", Usage: "WS-RPC server listening interface", - Value: "127.0.0.1", + Value: common.DefaultWsHost, } WSPortFlag = cli.IntFlag{ Name: "wsport", Usage: "WS-RPC server listening port", - Value: 8546, + Value: common.DefaultWsPort, } WSApiFlag = cli.StringFlag{ Name: "wsapi", @@ -284,7 +284,7 @@ var ( Value: rpc.DefaultHttpRpcApis, } WSAllowedDomainsFlag = cli.StringFlag{ - Name: "wsdomains", + Name: "wscors", Usage: "Domains from which to accept websockets requests", Value: "", } @@ -482,6 +482,15 @@ func MakeNAT(ctx *cli.Context) nat.Interface { return natif } +// MakeHttpRpcHost creates the HTTP RPC listener interface string from the set +// command line flags, returning empty if the HTTP endpoint is disabled. +func MakeHttpRpcHost(ctx *cli.Context) string { + if !ctx.GlobalBool(RPCEnabledFlag.Name) { + return "" + } + return ctx.GlobalString(RPCListenAddrFlag.Name) +} + // MakeGenesisBlock loads up a genesis block from an input file specified in the // command line, or returns the empty string if none set. func MakeGenesisBlock(ctx *cli.Context) string { @@ -591,7 +600,6 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. // Configure the node's service container stackConf := &node.Config{ DataDir: MustMakeDataDir(ctx), - IpcPath: MakeIpcPath(ctx), PrivateKey: MakeNodeKey(ctx), Name: MakeNodeName(name, version, ctx), NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name), @@ -600,6 +608,11 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. NAT: MakeNAT(ctx), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), + IpcPath: MakeIpcPath(ctx), + HttpHost: MakeHttpRpcHost(ctx), + HttpPort: ctx.GlobalInt(RPCPortFlag.Name), + HttpCors: ctx.GlobalString(RPCCORSDomainFlag.Name), + HttpModules: strings.Split(ctx.GlobalString(RPCApiFlag.Name), ","), } // Configure the Ethereum service accman := MakeAccountManager(ctx) @@ -744,27 +757,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database return chain, chainDb } -// StartRPC starts a HTTP JSON-RPC API server. -func StartRPC(stack *node.Node, ctx *cli.Context) error { - for _, api := range stack.APIs() { - if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok { - address := ctx.GlobalString(RPCListenAddrFlag.Name) - port := ctx.GlobalInt(RPCPortFlag.Name) - cors := ctx.GlobalString(RPCCORSDomainFlag.Name) - apiStr := "" - if ctx.GlobalIsSet(RPCApiFlag.Name) { - apiStr = ctx.GlobalString(RPCApiFlag.Name) - } - - _, err := adminApi.StartRPC(address, port, cors, apiStr) - return err - } - } - - glog.V(logger.Error).Infof("Unable to start RPC-HTTP interface, could not find admin API") - return errors.New("Unable to start RPC-HTTP interface") -} - // StartWS starts a websocket JSON-RPC API server. func StartWS(stack *node.Node, ctx *cli.Context) error { for _, api := range stack.APIs() { diff --git a/common/defaults.go b/common/defaults.go new file mode 100644 index 000000000..c5a88d7a3 --- /dev/null +++ b/common/defaults.go @@ -0,0 +1,48 @@ +// Copyright 2016 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 common + +import ( + "path/filepath" + "runtime" +) + +const ( + DefaultIpcSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket + DefaultHttpHost = "localhost" // Default host interface for the HTTP RPC server + DefaultHttpPort = 8545 // Default TCP port for the HTTP RPC server + DefaultWsHost = "localhost" // Default host interface for the websocket RPC server + DefaultWsPort = 8546 // Default TCP port for the websocket RPC server +) + +// DefaultDataDir is the default data directory to use for the databases and other +// persistence requirements. +func DefaultDataDir() string { + // Try to place the data folder in the user's home dir + home := HomeDir() + if home != "" { + if runtime.GOOS == "darwin" { + return filepath.Join(home, "Library", "Ethereum") + } else if runtime.GOOS == "windows" { + return filepath.Join(home, "AppData", "Roaming", "Ethereum") + } else { + return filepath.Join(home, ".ethereum") + } + } + // As we cannot guess a stable location, return empty and handle later + return "" +} diff --git a/common/path.go b/common/path.go index 38c213a12..75a8c1a3e 100644 --- a/common/path.go +++ b/common/path.go @@ -72,25 +72,3 @@ func HomeDir() string { } return "" } - -func DefaultDataDir() string { - // Try to place the data folder in the user's home dir - home := HomeDir() - if home != "" { - if runtime.GOOS == "darwin" { - return filepath.Join(home, "Library", "Ethereum") - } else if runtime.GOOS == "windows" { - return filepath.Join(home, "AppData", "Roaming", "Ethereum") - } else { - return filepath.Join(home, ".ethereum") - } - } - // As we cannot guess a stable location, return empty and handle later - return "" -} - -// DefaultIpcSocket returns the relative name of the default IPC socket. The path -// resolution is done by a node with other contextual infos. -func DefaultIpcSocket() string { - return "geth.ipc" -} diff --git a/node/api.go b/node/api.go index bc1795407..1b185c6f1 100644 --- a/node/api.go +++ b/node/api.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/rpc" "github.com/rcrowley/go-metrics" - "gopkg.in/fatih/set.v0" ) @@ -61,42 +60,29 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) { } // StartRPC starts the HTTP RPC API server. -func (api *PrivateAdminAPI) StartRPC(address string, port int, cors string, apis string) (bool, error) { - var offeredAPIs []rpc.API - if len(apis) > 0 { - namespaces := set.New() - for _, a := range strings.Split(apis, ",") { - namespaces.Add(strings.TrimSpace(a)) - } - for _, api := range api.node.APIs() { - if namespaces.Has(api.Namespace) { - offeredAPIs = append(offeredAPIs, api) - } - } - } else { // use by default all public API's - for _, api := range api.node.APIs() { - if api.Public { - offeredAPIs = append(offeredAPIs, api) - } - } - } +func (api *PrivateAdminAPI) StartRPC(host string, port int, cors string, apis string) (bool, error) { + api.node.lock.Lock() + defer api.node.lock.Unlock() - if address == "" { - address = "127.0.0.1" + if api.node.httpHandler != nil { + return false, fmt.Errorf("HTTP RPC already running on %s", api.node.httpEndpoint) } - if port == 0 { - port = 8545 + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", host, port), api.node.rpcAPIs, strings.Split(apis, ","), cors); err != nil { + return false, err } - - corsDomains := strings.Split(cors, " ") - err := rpc.StartHTTP(address, port, corsDomains, offeredAPIs) - return err == nil, err + return true, nil } // StopRPC terminates an already running HTTP RPC API endpoint. func (api *PrivateAdminAPI) StopRPC() (bool, error) { - err := rpc.StopHTTP() - return err == nil, err + api.node.lock.Lock() + defer api.node.lock.Unlock() + + if api.node.httpHandler == nil { + return false, fmt.Errorf("HTTP RPC not running") + } + api.node.stopHTTP() + return true, nil } // StartWS starts the websocket RPC API server. diff --git a/node/config.go b/node/config.go index d3eb1c78b..94c6e2e56 100644 --- a/node/config.go +++ b/node/config.go @@ -19,6 +19,7 @@ package node import ( "crypto/ecdsa" "encoding/json" + "fmt" "io/ioutil" "net" "os" @@ -97,6 +98,25 @@ type Config struct { // handshake phase, counted separately for inbound and outbound connections. // Zero defaults to preset values. MaxPendingPeers int + + // HttpHost is the host interface on which to start the HTTP RPC server. If this + // field is empty, no HTTP API endpoint will be started. + HttpHost string + + // HttpPort is the TCP port number on which to start the HTTP RPC server. The + // default zero value is/ valid and will pick a port number randomly (useful + // for ephemeral nodes). + HttpPort int + + // HttpCors is the Cross-Origin Resource Sharing header to send to requesting + // clients. Please be aware that CORS is a browser enforced security, it's fully + // useless for custom HTTP clients. + HttpCors string + + // HttpModules is a list of API modules to expose via the HTTP RPC interface. + // If the module list is empty, all RPC API endpoints designated public will be + // exposed. + HttpModules []string } // IpcEndpoint resolves an IPC endpoint based on a configured value, taking into @@ -126,10 +146,25 @@ func (c *Config) IpcEndpoint() string { // DefaultIpcEndpoint returns the IPC path used by default. func DefaultIpcEndpoint() string { - config := &Config{DataDir: common.DefaultDataDir(), IpcPath: common.DefaultIpcSocket()} + config := &Config{DataDir: common.DefaultDataDir(), IpcPath: common.DefaultIpcSocket} return config.IpcEndpoint() } +// HttpEndpoint resolves an HTTP endpoint based on the configured host interface +// and port parameters. +func (c *Config) HttpEndpoint() string { + if c.HttpHost == "" { + return "" + } + return fmt.Sprintf("%s:%d", c.HttpHost, c.HttpPort) +} + +// DefaultHttpEndpoint returns the HTTP endpoint used by default. +func DefaultHttpEndpoint() string { + config := &Config{HttpHost: common.DefaultHttpHost, HttpPort: common.DefaultHttpPort} + return config.HttpEndpoint() +} + // NodeKey retrieves the currently configured private key of the node, checking // first any manually set key, falling back to the one found in the configured // data folder. If no key can be found, a new one is generated. diff --git a/node/node.go b/node/node.go index e3fc03360..44c88d378 100644 --- a/node/node.go +++ b/node/node.go @@ -55,10 +55,17 @@ type Node struct { serviceFuncs []ServiceConstructor // Service constructors (in dependency order) services map[reflect.Type]Service // Currently running services + rpcAPIs []rpc.API // List of APIs currently provided by the node ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled) ipcListener net.Listener // IPC RPC listener socket to serve API requests ipcHandler *rpc.Server // IPC RPC request handler to process the API requests + httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) + httpWhitelist []string // HTTP RPC modules to allow through this endpoint + httpCors string // HTTP RPC Cross-Origin Resource Sharing header + httpListener net.Listener // HTTP RPC listener socket to server API requests + httpHandler *rpc.Server // HTTP RPC request handler to process the API requests + stop chan struct{} // Channel to wait for termination notifications lock sync.RWMutex } @@ -93,9 +100,12 @@ func New(conf *Config) (*Node, error) { MaxPeers: conf.MaxPeers, MaxPendingPeers: conf.MaxPendingPeers, }, - serviceFuncs: []ServiceConstructor{}, - ipcEndpoint: conf.IpcEndpoint(), - eventmux: new(event.TypeMux), + serviceFuncs: []ServiceConstructor{}, + ipcEndpoint: conf.IpcEndpoint(), + httpEndpoint: conf.HttpEndpoint(), + httpWhitelist: conf.HttpModules, + httpCors: conf.HttpCors, + eventmux: new(event.TypeMux), }, nil } @@ -188,58 +198,146 @@ func (n *Node) Start() error { return nil } -// startRPC initializes and starts the IPC RPC endpoints. +// startRPC is a helper method to start all the various RPC endpoint during node +// startup. It's not meant to be called at any time afterwards as it makes certain +// assumptions about the state of the node. func (n *Node) startRPC(services map[reflect.Type]Service) error { - // Gather and register all the APIs exposed by the services + // Gather all the possible APIs to surface apis := n.apis() for _, service := range services { apis = append(apis, service.APIs()...) } - ipcHandler := rpc.NewServer() + // Start the various API endpoints, terminating all in case of errors + if err := n.startIPC(apis); err != nil { + return err + } + if err := n.startHTTP(n.httpEndpoint, apis, n.httpWhitelist, n.httpCors); err != nil { + n.stopIPC() + return err + } + // All API endpoints started successfully + n.rpcAPIs = apis + return nil +} + +// startIPC initializes and starts the IPC RPC endpoint. +func (n *Node) startIPC(apis []rpc.API) error { + // Short circuit if the IPC endpoint isn't being exposed + if n.ipcEndpoint == "" { + return nil + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() for _, api := range apis { - if err := ipcHandler.RegisterName(api.Namespace, api.Service); err != nil { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return err } - glog.V(logger.Debug).Infof("Register %T under namespace '%s'", api.Service, api.Namespace) + glog.V(logger.Debug).Infof("IPC registered %T under '%s'", api.Service, api.Namespace) } - // All APIs registered, start the IPC and HTTP listeners + // All APIs registered, start the IPC listener var ( - ipcListener net.Listener - err error + listener net.Listener + err error ) - if n.ipcEndpoint != "" { - if ipcListener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil { - return err - } - go func() { - glog.V(logger.Info).Infof("IPC endpoint opened: %s", n.ipcEndpoint) - defer glog.V(logger.Info).Infof("IPC endpoint closed: %s", n.ipcEndpoint) - - for { - conn, err := ipcListener.Accept() - if err != nil { - // Terminate if the listener was closed - n.lock.RLock() - closed := n.ipcListener == nil - n.lock.RUnlock() - if closed { - return - } - // Not closed, just some error; report and continue - glog.V(logger.Error).Infof("IPC accept failed: %v", err) - continue + if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil { + return err + } + go func() { + glog.V(logger.Info).Infof("IPC endpoint opened: %s", n.ipcEndpoint) + + for { + conn, err := listener.Accept() + if err != nil { + // Terminate if the listener was closed + n.lock.RLock() + closed := n.ipcListener == nil + n.lock.RUnlock() + if closed { + return } - go ipcHandler.ServeCodec(rpc.NewJSONCodec(conn)) + // Not closed, just some error; report and continue + glog.V(logger.Error).Infof("IPC accept failed: %v", err) + continue + } + go handler.ServeCodec(rpc.NewJSONCodec(conn)) + } + }() + // All listeners booted successfully + n.ipcListener = listener + n.ipcHandler = handler + + return nil +} + +// stopIPC terminates the IPC RPC endpoint. +func (n *Node) stopIPC() { + if n.ipcListener != nil { + n.ipcListener.Close() + n.ipcListener = nil + + glog.V(logger.Info).Infof("IPC endpoint closed: %s", n.ipcEndpoint) + } + if n.ipcHandler != nil { + n.ipcHandler.Stop() + n.ipcHandler = nil + } +} + +// startHTTP initializes and starts the HTTP RPC endpoint. +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors string) error { + // Short circuit if the IPC endpoint isn't being exposed + if endpoint == "" { + return nil + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return err } - }() + glog.V(logger.Debug).Infof("HTTP registered %T under '%s'", api.Service, api.Namespace) + } } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return err + } + go rpc.NewHTTPServer(cors, handler).Serve(listener) + glog.V(logger.Info).Infof("HTTP endpoint opened: http://%s", endpoint) + // All listeners booted successfully - n.ipcListener = ipcListener - n.ipcHandler = ipcHandler + n.httpEndpoint = endpoint + n.httpListener = listener + n.httpHandler = handler + n.httpCors = cors return nil } +// stopHTTP terminates the HTTP RPC endpoint. +func (n *Node) stopHTTP() { + if n.httpListener != nil { + n.httpListener.Close() + n.httpListener = nil + + glog.V(logger.Info).Infof("HTTP endpoint closed: http://%s", n.httpEndpoint) + } + if n.httpHandler != nil { + n.httpHandler.Stop() + n.httpHandler = nil + } +} + // Stop terminates a running node along with all it's services. In the node was // not started, an error is returned. func (n *Node) Stop() error { @@ -251,14 +349,10 @@ func (n *Node) Stop() error { return ErrNodeStopped } // Otherwise terminate the API, all services and the P2P server too - if n.ipcListener != nil { - n.ipcListener.Close() - n.ipcListener = nil - } - if n.ipcHandler != nil { - n.ipcHandler.Stop() - n.ipcHandler = nil - } + n.stopIPC() + n.stopHTTP() + n.rpcAPIs = nil + failure := &StopError{ Services: make(map[reflect.Type]error), } diff --git a/rpc/http.go b/rpc/http.go index c5eb41af1..e58a88c08 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -20,7 +20,6 @@ import ( "bufio" "bytes" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -29,7 +28,6 @@ import ( "net/url" "strconv" "strings" - "sync" "time" "github.com/ethereum/go-ethereum/logger" @@ -41,12 +39,6 @@ const ( httpReadDeadLine = 60 * time.Second // wait max httpReadDeadeline for next request ) -var ( - httpServerMu sync.Mutex // prevent concurrent access to the httpListener and httpServer - httpListener net.Listener // listener for the http server - httpRPCServer *Server // the node can only start 1 HTTP RPC server instance -) - // httpMessageStream is the glue between a HTTP connection which is message based // and the RPC codecs that expect json requests to be read from a stream. It will // parse HTTP messages and offer the bodies of these requests as a stream through @@ -249,53 +241,14 @@ func (h *httpConnHijacker) ServeHTTP(w http.ResponseWriter, req *http.Request) { go h.rpcServer.ServeCodec(codec) } -// StartHTTP will start the JSONRPC HTTP RPC interface when its not yet running. -func StartHTTP(address string, port int, corsdomains []string, apis []API) error { - httpServerMu.Lock() - defer httpServerMu.Unlock() - - if httpRPCServer != nil { - return fmt.Errorf("HTTP RPC interface already started on %s", httpListener.Addr()) - } - - rpcServer := NewServer() - - for _, api := range apis { - if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - } - - listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) - if err != nil { - return err +// NewHTTPServer creates a new HTTP RPC server around an API provider. +func NewHTTPServer(cors string, handler *Server) *http.Server { + return &http.Server{ + Handler: &httpConnHijacker{ + corsdomains: strings.Split(cors, ","), + rpcServer: handler, + }, } - - httpServer := http.Server{Handler: &httpConnHijacker{corsdomains, rpcServer}} - go httpServer.Serve(listener) - - httpListener = listener - httpRPCServer = rpcServer - - return nil -} - -// StopHTTP will stop the running HTTP interface. If it is not running an error will be returned. -func StopHTTP() error { - httpServerMu.Lock() - defer httpServerMu.Unlock() - - if httpRPCServer == nil { - return errors.New("HTTP RPC interface not started") - } - - httpListener.Close() - httpRPCServer.Stop() - - httpRPCServer = nil - httpListener = nil - - return nil } // httpClient connects to a geth RPC server over HTTP. diff --git a/rpc/utils.go b/rpc/utils.go index 39acf8196..fa114284d 100644 --- a/rpc/utils.go +++ b/rpc/utils.go @@ -20,13 +20,12 @@ import ( "crypto/rand" "encoding/hex" "errors" + "fmt" "math/big" "reflect" "unicode" "unicode/utf8" - "fmt" - "golang.org/x/net/context" ) |