diff --git a/cmd/clef/main.go b/cmd/clef/main.go index dde4ae853f..7b44975021 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -748,7 +748,7 @@ func signer(c *cli.Context) error { // start http server httpEndpoint := net.JoinHostPort(c.String(utils.HTTPListenAddrFlag.Name), fmt.Sprintf("%d", port)) - httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler) + httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, c.String(utils.HTTPListenProtocolFlag.Name), rpc.DefaultHTTPTimeouts, handler) if err != nil { utils.Fatalf("Could not start RPC api: %v", err) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 850e26d161..d7d02667ce 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -162,6 +162,7 @@ var ( rpcFlags = []cli.Flag{ utils.HTTPEnabledFlag, utils.HTTPListenAddrFlag, + utils.HTTPListenProtocolFlag, utils.HTTPPortFlag, utils.HTTPCORSDomainFlag, utils.AuthListenFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c41cf4ee40..e0da9d6bc0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -27,6 +27,7 @@ import ( "net" "net/http" "os" + "path" "path/filepath" godebug "runtime/debug" "strconv" @@ -741,6 +742,12 @@ var ( Value: node.DefaultHTTPHost, Category: flags.APICategory, } + HTTPListenProtocolFlag = &cli.StringFlag{ + Name: "http.proto", + Usage: "HTTP-RPC server listening protocol (tcp,unix)", + Value: node.DefaultHTTPProtocol, + Category: flags.APICategory, + } HTTPPortFlag = &cli.IntFlag{ Name: "http.port", Usage: "HTTP-RPC server listening port", @@ -1301,13 +1308,23 @@ func SplitAndTrim(input string) (ret []string) { func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.Bool(HTTPEnabledFlag.Name) { if cfg.HTTPHost == "" { - cfg.HTTPHost = "127.0.0.1" + if HTTPListenProtocolFlag.Name == "unix" { + cfg.HTTPHost = path.Join(os.TempDir(), "geth.http.sock") + } else { + cfg.HTTPHost = "127.0.0.1" + } } if ctx.IsSet(HTTPListenAddrFlag.Name) { cfg.HTTPHost = ctx.String(HTTPListenAddrFlag.Name) } } + if ctx.IsSet(HTTPListenProtocolFlag.Name) { + cfg.HTTPProto = ctx.String(HTTPListenProtocolFlag.Name) + } else { + cfg.HTTPProto = "tcp" + } + if ctx.IsSet(HTTPPortFlag.Name) { cfg.HTTPPort = ctx.Int(HTTPPortFlag.Name) } diff --git a/node/api.go b/node/api.go index e5dda5ac4d..2fe43c17cb 100644 --- a/node/api.go +++ b/node/api.go @@ -157,7 +157,7 @@ func (api *adminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) } // StartHTTP starts the HTTP RPC API server. -func (api *adminAPI) StartHTTP(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { +func (api *adminAPI) StartHTTP(proto *string, host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { api.node.lock.Lock() defer api.node.lock.Unlock() @@ -202,7 +202,7 @@ func (api *adminAPI) StartHTTP(host *string, port *int, cors *string, apis *stri } } - if err := api.node.http.setListenAddr(*host, *port); err != nil { + if err := api.node.http.setListenAddr(*proto, *host, *port); err != nil { return false, err } if err := api.node.http.enableRPC(api.node.rpcAPIs, config); err != nil { @@ -216,9 +216,9 @@ func (api *adminAPI) StartHTTP(host *string, port *int, cors *string, apis *stri // StartRPC starts the HTTP RPC API server. // Deprecated: use StartHTTP instead. -func (api *adminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { +func (api *adminAPI) StartRPC(proto *string, host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP") - return api.StartHTTP(host, port, cors, apis, vhosts) + return api.StartHTTP(proto, host, port, cors, apis, vhosts) } // StopHTTP shuts down the HTTP server. @@ -235,7 +235,7 @@ func (api *adminAPI) StopRPC() (bool, error) { } // StartWS starts the websocket RPC API server. -func (api *adminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) { +func (api *adminAPI) StartWS(proto *string, host *string, port *int, allowedOrigins *string, apis *string) (bool, error) { api.node.lock.Lock() defer api.node.lock.Unlock() @@ -276,7 +276,7 @@ func (api *adminAPI) StartWS(host *string, port *int, allowedOrigins *string, ap // Enable WebSocket on the server. server := api.node.wsServerForPort(*port, false) - if err := server.setListenAddr(*host, *port); err != nil { + if err := server.setListenAddr(*proto, *host, *port); err != nil { return false, err } openApis, _ := api.node.getAPIs() diff --git a/node/api_test.go b/node/api_test.go index 4033c85871..e068ee270a 100644 --- a/node/api_test.go +++ b/node/api_test.go @@ -69,7 +69,7 @@ func TestStartRPC(t *testing.T) { name: "rpc enabled through API", cfg: Config{}, fn: func(t *testing.T, n *Node, api *adminAPI) { - _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) + _, err := api.StartHTTP(sp("tcp"), sp("127.0.0.1"), ip(0), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -90,14 +90,14 @@ func TestStartRPC(t *testing.T) { port := listener.Addr().(*net.TCPAddr).Port // Now try to start RPC on that port. This should fail. - _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) + _, err = api.StartHTTP(sp("tcp"), sp("127.0.0.1"), ip(port), nil, nil, nil) if err == nil { t.Fatal("StartHTTP should have failed on port", port) } // Try again after unblocking the port. It should work this time. listener.Close() - _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) + _, err = api.StartHTTP(sp("tcp"), sp("127.0.0.1"), ip(port), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -144,7 +144,7 @@ func TestStartRPC(t *testing.T) { name: "ws enabled through API", cfg: Config{}, fn: func(t *testing.T, n *Node, api *adminAPI) { - _, err := api.StartWS(sp("127.0.0.1"), ip(0), nil, nil) + _, err := api.StartWS(sp("tcp"), sp("127.0.0.1"), ip(0), nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -184,7 +184,7 @@ func TestStartRPC(t *testing.T) { cfg: Config{HTTPHost: "127.0.0.1"}, fn: func(t *testing.T, n *Node, api *adminAPI) { wsport := n.http.port - _, err := api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) + _, err := api.StartWS(sp("tcp"), sp("127.0.0.1"), ip(wsport), nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -197,7 +197,7 @@ func TestStartRPC(t *testing.T) { cfg: Config{HTTPHost: "127.0.0.1"}, fn: func(t *testing.T, n *Node, api *adminAPI) { wsport := n.http.port - _, err := api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) + _, err := api.StartWS(sp("tcp"), sp("127.0.0.1"), ip(wsport), nil, nil) assert.NoError(t, err) _, err = api.StopWS() @@ -211,11 +211,11 @@ func TestStartRPC(t *testing.T) { { name: "rpc stopped with ws enabled", fn: func(t *testing.T, n *Node, api *adminAPI) { - _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) + _, err := api.StartHTTP(sp("tcp"), sp("127.0.0.1"), ip(0), nil, nil, nil) assert.NoError(t, err) wsport := n.http.port - _, err = api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) + _, err = api.StartWS(sp("tcp"), sp("127.0.0.1"), ip(wsport), nil, nil) assert.NoError(t, err) _, err = api.StopHTTP() @@ -229,11 +229,11 @@ func TestStartRPC(t *testing.T) { { name: "rpc enabled after ws", fn: func(t *testing.T, n *Node, api *adminAPI) { - _, err := api.StartWS(sp("127.0.0.1"), ip(0), nil, nil) + _, err := api.StartWS(sp("tcp"), sp("127.0.0.1"), ip(0), nil, nil) assert.NoError(t, err) wsport := n.http.port - _, err = api.StartHTTP(sp("127.0.0.1"), ip(wsport), nil, nil, nil) + _, err = api.StartHTTP(sp("tcp"), sp("127.0.0.1"), ip(wsport), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, diff --git a/node/config.go b/node/config.go index 255b0f0aa9..a93af76ece 100644 --- a/node/config.go +++ b/node/config.go @@ -106,6 +106,10 @@ type Config struct { // field is empty, no HTTP API endpoint will be started. HTTPHost string + // HTTPProto is the protocol on which the HTTP RPC server will bind. + // Supported values are tcp, unix. HTTPPort is ignored if HTTPProto == unix + HTTPProto 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). diff --git a/node/defaults.go b/node/defaults.go index 3410fa2ae5..d888f1e98f 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -29,12 +29,13 @@ import ( ) const ( - 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 - DefaultAuthHost = "localhost" // Default host interface for the authenticated apis - DefaultAuthPort = 8551 // Default port for the authenticated apis + DefaultHTTPProtocol = "tcp" // Default protocol for the HTTP RPC SERVER + 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 + DefaultAuthHost = "localhost" // Default host interface for the authenticated apis + DefaultAuthPort = 8551 // Default port for the authenticated apis ) const ( diff --git a/node/endpoints.go b/node/endpoints.go index 14c12fd1f1..ea1b001b48 100644 --- a/node/endpoints.go +++ b/node/endpoints.go @@ -26,13 +26,13 @@ import ( ) // StartHTTPEndpoint starts the HTTP RPC endpoint. -func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) { +func StartHTTPEndpoint(endpoint string, protocol string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) { // start the HTTP listener var ( listener net.Listener err error ) - if listener, err = net.Listen("tcp", endpoint); err != nil { + if listener, err = net.Listen(protocol, endpoint); err != nil { return nil, nil, err } // make sure timeout values are meaningful diff --git a/node/node.go b/node/node.go index 56ecd7d522..4984d39124 100644 --- a/node/node.go +++ b/node/node.go @@ -90,6 +90,10 @@ func New(conf *Config) (*Node, error) { conf.Logger = log.New() } + if conf.HTTPProto == "" { + conf.HTTPProto = "tcp" + } + // Ensure that the instance name doesn't cause weird conflicts with // other files in the data directory. if strings.ContainsAny(conf.Name, `/\`) { @@ -395,7 +399,7 @@ func (n *Node) startRPC() error { } initHttp := func(server *httpServer, port int) error { - if err := server.setListenAddr(n.config.HTTPHost, port); err != nil { + if err := server.setListenAddr(n.config.HTTPProto, n.config.HTTPHost, port); err != nil { return err } if err := server.enableRPC(openAPIs, httpConfig{ @@ -413,7 +417,7 @@ func (n *Node) startRPC() error { initWS := func(port int) error { server := n.wsServerForPort(port, false) - if err := server.setListenAddr(n.config.WSHost, port); err != nil { + if err := server.setListenAddr(n.config.HTTPProto, n.config.WSHost, port); err != nil { return err } if err := server.enableWS(openAPIs, wsConfig{ @@ -431,7 +435,7 @@ func (n *Node) startRPC() error { initAuth := func(port int, secret []byte) error { // Enable auth via HTTP server := n.httpAuth - if err := server.setListenAddr(n.config.AuthAddr, port); err != nil { + if err := server.setListenAddr(n.config.HTTPProto, n.config.AuthAddr, port); err != nil { return err } sharedConfig := rpcEndpointConfig{ @@ -454,7 +458,7 @@ func (n *Node) startRPC() error { // Enable auth via WS server = n.wsServerForPort(port, true) - if err := server.setListenAddr(n.config.AuthAddr, port); err != nil { + if err := server.setListenAddr(n.config.HTTPProto, n.config.AuthAddr, port); err != nil { return err } if err := server.enableWS(allAPIs, wsConfig{ diff --git a/node/node_test.go b/node/node_test.go index 1552728d04..6542c43065 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -516,6 +516,7 @@ func TestNodeRPCPrefix(t *testing.T) { name := fmt.Sprintf("http=%s ws=%s", test.httpPrefix, test.wsPrefix) t.Run(name, func(t *testing.T) { cfg := &Config{ + HTTPProto: "tcp", HTTPHost: "127.0.0.1", HTTPPathPrefix: test.httpPrefix, WSHost: "127.0.0.1", diff --git a/node/rpcstack.go b/node/rpcstack.go index 1db2ed3f44..559d87aae3 100644 --- a/node/rpcstack.go +++ b/node/rpcstack.go @@ -86,6 +86,7 @@ type httpServer struct { // These are set by setListenAddr. endpoint string + proto string host string port int @@ -106,16 +107,21 @@ func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer { // setListenAddr configures the listening address of the server. // The address can only be set while the server isn't running. -func (h *httpServer) setListenAddr(host string, port int) error { +func (h *httpServer) setListenAddr(proto string, host string, port int) error { h.mu.Lock() defer h.mu.Unlock() - if h.listener != nil && (host != h.host || port != h.port) { + if h.listener != nil && (host != h.host || port != h.port || proto != h.proto) { return fmt.Errorf("HTTP server already running on %s", h.endpoint) } + h.proto, h.host, h.port = proto, host, port h.host, h.port = host, port - h.endpoint = net.JoinHostPort(host, fmt.Sprintf("%d", port)) + if h.proto != "unix" { + h.endpoint = net.JoinHostPort(host, fmt.Sprintf("%d", port)) + } else { + h.endpoint = host + } return nil } @@ -155,7 +161,7 @@ func (h *httpServer) start() error { } // Start the server. - listener, err := net.Listen("tcp", h.endpoint) + listener, err := net.Listen(h.proto, h.endpoint) if err != nil { // If the server fails to start, we need to clear out the RPC and WS // configuration so they can be configured another time. @@ -296,7 +302,7 @@ func (h *httpServer) doStop() { h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr()) // Clear out everything to allow re-configuring it later. - h.host, h.port, h.endpoint = "", 0, "" + h.proto, h.host, h.port, h.endpoint = "", "", 0, "" h.server, h.listener = nil, nil } diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index f5668abb08..7b86d3cb30 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -249,7 +249,7 @@ func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsCon if ws { assert.NoError(t, srv.enableWS(nil, *wsConf)) } - assert.NoError(t, srv.setListenAddr("localhost", 0)) + assert.NoError(t, srv.setListenAddr("tcp", "localhost", 0)) assert.NoError(t, srv.start()) return srv }