aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2016-02-05 19:45:36 +0800
committerPéter Szilágyi <peterke@gmail.com>2016-02-05 19:45:36 +0800
commita13bc9d7a1bc96fab93ace40045c0f0fea4da836 (patch)
treec6bd5be2e3e1ce06450a8611b2f1253b2ac18351
parentba7c125153ce1be30985784a18edf38645406d03 (diff)
downloadgo-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar.gz
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar.bz2
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar.lz
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar.xz
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.tar.zst
go-tangerine-a13bc9d7a1bc96fab93ace40045c0f0fea4da836.zip
cmd, common, node, rpc: move HTTP RPC into node, drop singletone aspect
-rw-r--r--cmd/geth/main.go5
-rw-r--r--cmd/gethrpctest/main.go37
-rw-r--r--cmd/utils/flags.go48
-rw-r--r--common/defaults.go48
-rw-r--r--common/path.go22
-rw-r--r--node/api.go46
-rw-r--r--node/config.go37
-rw-r--r--node/node.go182
-rw-r--r--rpc/http.go61
-rw-r--r--rpc/utils.go3
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"
)