forked from cerc-io/plugeth
cmd, node, rpc: readd inproc RPC client, expose via node
This commit is contained in:
parent
900e124bee
commit
df75dbfd68
@ -20,7 +20,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -30,7 +29,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||||
@ -96,7 +94,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Create a networkless protocol stack
|
// Create a networkless protocol stack
|
||||||
stack, err := node.New(&node.Config{PrivateKey: testNodeKey, Name: "test", NoDiscovery: true, IPCPath: fmt.Sprintf("geth-test-%d.ipc", rand.Int63())})
|
stack, err := node.New(&node.Config{PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create node: %v", err)
|
t.Fatalf("failed to create node: %v", err)
|
||||||
}
|
}
|
||||||
@ -142,7 +140,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
|||||||
stack.Service(ðereum)
|
stack.Service(ðereum)
|
||||||
|
|
||||||
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
||||||
client, err := utils.NewRemoteRPCClientFromString("ipc:" + stack.IPCEndpoint())
|
client, err := stack.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to attach to node: %v", err)
|
t.Fatalf("failed to attach to node: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -425,7 +425,7 @@ func console(ctx *cli.Context) {
|
|||||||
startNode(ctx, node)
|
startNode(ctx, node)
|
||||||
|
|
||||||
// Attach to the newly started node, and either execute script or become interactive
|
// Attach to the newly started node, and either execute script or become interactive
|
||||||
client, err := utils.NewRemoteRPCClientFromString("ipc:" + node.IPCEndpoint())
|
client, err := node.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
@ -451,7 +451,7 @@ func execScripts(ctx *cli.Context) {
|
|||||||
startNode(ctx, node)
|
startNode(ctx, node)
|
||||||
|
|
||||||
// Attach to the newly started node and execute the given scripts
|
// Attach to the newly started node and execute the given scripts
|
||||||
client, err := utils.NewRemoteRPCClientFromString("ipc:" + node.IPCEndpoint())
|
client, err := node.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,5 @@ func NewRemoteRPCClientFromString(endpoint string) (rpc.Client, error) {
|
|||||||
if strings.HasPrefix(endpoint, "ws:") {
|
if strings.HasPrefix(endpoint, "ws:") {
|
||||||
return rpc.NewWSClient(endpoint)
|
return rpc.NewWSClient(endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("invalid endpoint")
|
return nil, fmt.Errorf("invalid endpoint")
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func (api *PrivateAdminAPI) StartWS(host string, port int, cors string, apis str
|
|||||||
defer api.node.lock.Unlock()
|
defer api.node.lock.Unlock()
|
||||||
|
|
||||||
if api.node.wsHandler != nil {
|
if api.node.wsHandler != nil {
|
||||||
return false, fmt.Errorf("WebSocker RPC already running on %s", api.node.wsEndpoint)
|
return false, fmt.Errorf("WebSocket RPC already running on %s", api.node.wsEndpoint)
|
||||||
}
|
}
|
||||||
if err := api.node.startWS(fmt.Sprintf("%s:%d", host, port), api.node.rpcAPIs, strings.Split(apis, ","), cors); err != nil {
|
if err := api.node.startWS(fmt.Sprintf("%s:%d", host, port), api.node.rpcAPIs, strings.Split(apis, ","), cors); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
55
node/node.go
55
node/node.go
@ -55,7 +55,9 @@ type Node struct {
|
|||||||
serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
|
serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
|
||||||
services map[reflect.Type]Service // Currently running services
|
services map[reflect.Type]Service // Currently running services
|
||||||
|
|
||||||
rpcAPIs []rpc.API // List of APIs currently provided by the node
|
rpcAPIs []rpc.API // List of APIs currently provided by the node
|
||||||
|
inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
|
||||||
|
|
||||||
ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled)
|
ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled)
|
||||||
ipcListener net.Listener // IPC RPC listener socket to serve API requests
|
ipcListener net.Listener // IPC RPC listener socket to serve API requests
|
||||||
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
|
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
|
||||||
@ -217,16 +219,22 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
|
|||||||
apis = append(apis, service.APIs()...)
|
apis = append(apis, service.APIs()...)
|
||||||
}
|
}
|
||||||
// Start the various API endpoints, terminating all in case of errors
|
// Start the various API endpoints, terminating all in case of errors
|
||||||
|
if err := n.startInProc(apis); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := n.startIPC(apis); err != nil {
|
if err := n.startIPC(apis); err != nil {
|
||||||
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.startHTTP(n.httpEndpoint, apis, n.httpWhitelist, n.httpCors); err != nil {
|
if err := n.startHTTP(n.httpEndpoint, apis, n.httpWhitelist, n.httpCors); err != nil {
|
||||||
n.stopIPC()
|
n.stopIPC()
|
||||||
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.startWS(n.wsEndpoint, apis, n.wsWhitelist, n.wsDomains); err != nil {
|
if err := n.startWS(n.wsEndpoint, apis, n.wsWhitelist, n.wsDomains); err != nil {
|
||||||
n.stopHTTP()
|
n.stopHTTP()
|
||||||
n.stopIPC()
|
n.stopIPC()
|
||||||
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// All API endpoints started successfully
|
// All API endpoints started successfully
|
||||||
@ -234,6 +242,28 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startInProc initializes an in-process RPC endpoint.
|
||||||
|
func (n *Node) startInProc(apis []rpc.API) error {
|
||||||
|
// Register all the APIs exposed by the services
|
||||||
|
handler := rpc.NewServer()
|
||||||
|
for _, api := range apis {
|
||||||
|
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("InProc registered %T under '%s'", api.Service, api.Namespace)
|
||||||
|
}
|
||||||
|
n.inprocHandler = handler
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stopInProc terminates the in-process RPC endpoint.
|
||||||
|
func (n *Node) stopInProc() {
|
||||||
|
if n.inprocHandler != nil {
|
||||||
|
n.inprocHandler.Stop()
|
||||||
|
n.inprocHandler = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// startIPC initializes and starts the IPC RPC endpoint.
|
// startIPC initializes and starts the IPC RPC endpoint.
|
||||||
func (n *Node) startIPC(apis []rpc.API) error {
|
func (n *Node) startIPC(apis []rpc.API) error {
|
||||||
// Short circuit if the IPC endpoint isn't being exposed
|
// Short circuit if the IPC endpoint isn't being exposed
|
||||||
@ -468,6 +498,19 @@ func (n *Node) Restart() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach creates an RPC client attached to an in-process API handler.
|
||||||
|
func (n *Node) Attach() (rpc.Client, error) {
|
||||||
|
n.lock.RLock()
|
||||||
|
defer n.lock.RUnlock()
|
||||||
|
|
||||||
|
// Short circuit if the node's not running
|
||||||
|
if n.server == nil {
|
||||||
|
return nil, ErrNodeStopped
|
||||||
|
}
|
||||||
|
// Otherwise attach to the API and return
|
||||||
|
return rpc.NewInProcRPCClient(n.inprocHandler), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Server retrieves the currently running P2P network layer. This method is meant
|
// Server retrieves the currently running P2P network layer. This method is meant
|
||||||
// only to inspect fields of the currently running server, life cycle management
|
// only to inspect fields of the currently running server, life cycle management
|
||||||
// should be left to this Node entity.
|
// should be left to this Node entity.
|
||||||
@ -506,6 +549,16 @@ func (n *Node) IPCEndpoint() string {
|
|||||||
return n.ipcEndpoint
|
return n.ipcEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack.
|
||||||
|
func (n *Node) HTTPEndpoint() string {
|
||||||
|
return n.httpEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// WSEndpoint retrieves the current WS endpoint used by the protocol stack.
|
||||||
|
func (n *Node) WSEndpoint() string {
|
||||||
|
return n.wsEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
// EventMux retrieves the event multiplexer used by all the network services in
|
// EventMux retrieves the event multiplexer used by all the network services in
|
||||||
// the current protocol stack.
|
// the current protocol stack.
|
||||||
func (n *Node) EventMux() *event.TypeMux {
|
func (n *Node) EventMux() *event.TypeMux {
|
||||||
|
@ -18,9 +18,7 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@ -37,7 +35,6 @@ var (
|
|||||||
|
|
||||||
func testNodeConfig() *Config {
|
func testNodeConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
IPCPath: fmt.Sprintf("test-%d.ipc", rand.Int63()),
|
|
||||||
PrivateKey: testNodeKey,
|
PrivateKey: testNodeKey,
|
||||||
Name: "test node",
|
Name: "test node",
|
||||||
}
|
}
|
||||||
@ -541,10 +538,11 @@ func TestAPIGather(t *testing.T) {
|
|||||||
defer stack.Stop()
|
defer stack.Stop()
|
||||||
|
|
||||||
// Connect to the RPC server and verify the various registered endpoints
|
// Connect to the RPC server and verify the various registered endpoints
|
||||||
ipcClient, err := rpc.NewIPCClient(stack.IPCEndpoint())
|
client, err := stack.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to connect to the IPC API server: %v", err)
|
t.Fatalf("failed to connect to the inproc API server: %v", err)
|
||||||
}
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Method string
|
Method string
|
||||||
@ -556,11 +554,11 @@ func TestAPIGather(t *testing.T) {
|
|||||||
{"multi.v2.nested_theOneMethod", "multi.v2.nested"},
|
{"multi.v2.nested_theOneMethod", "multi.v2.nested"},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
if err := ipcClient.Send(rpc.JSONRequest{Id: new(int64), Version: "2.0", Method: test.Method}); err != nil {
|
if err := client.Send(rpc.JSONRequest{Id: new(int64), Version: "2.0", Method: test.Method}); err != nil {
|
||||||
t.Fatalf("test %d: failed to send API request: %v", i, err)
|
t.Fatalf("test %d: failed to send API request: %v", i, err)
|
||||||
}
|
}
|
||||||
reply := new(rpc.JSONSuccessResponse)
|
reply := new(rpc.JSONSuccessResponse)
|
||||||
if err := ipcClient.Recv(reply); err != nil {
|
if err := client.Recv(reply); err != nil {
|
||||||
t.Fatalf("test %d: failed to read API reply: %v", i, err)
|
t.Fatalf("test %d: failed to read API reply: %v", i, err)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
@ -259,7 +259,7 @@ type httpClient struct {
|
|||||||
|
|
||||||
// NewHTTPClient create a new RPC clients that connection to a geth RPC server
|
// NewHTTPClient create a new RPC clients that connection to a geth RPC server
|
||||||
// over HTTP.
|
// over HTTP.
|
||||||
func NewHTTPClient(endpoint string) (*httpClient, error) {
|
func NewHTTPClient(endpoint string) (Client, error) {
|
||||||
url, err := url.Parse(endpoint)
|
url, err := url.Parse(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
111
rpc/inproc.go
Normal file
111
rpc/inproc.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// 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 rpc
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// NewInProcRPCClient creates an in-process buffer stream attachment to a given
|
||||||
|
// RPC server.
|
||||||
|
func NewInProcRPCClient(handler *Server) Client {
|
||||||
|
buffer := &inprocBuffer{
|
||||||
|
requests: make(chan []byte, 16),
|
||||||
|
responses: make(chan []byte, 16),
|
||||||
|
}
|
||||||
|
client := &inProcClient{
|
||||||
|
server: handler,
|
||||||
|
buffer: buffer,
|
||||||
|
}
|
||||||
|
go handler.ServeCodec(NewJSONCodec(client.buffer))
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// inProcClient is an in-process buffer stream attached to an RPC server.
|
||||||
|
type inProcClient struct {
|
||||||
|
server *Server
|
||||||
|
buffer *inprocBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close tears down the request channel of the in-proc client.
|
||||||
|
func (c *inProcClient) Close() {
|
||||||
|
c.buffer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send marshals a message into a json format and injects in into the client
|
||||||
|
// request channel.
|
||||||
|
func (c *inProcClient) Send(msg interface{}) error {
|
||||||
|
d, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.buffer.requests <- d
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recv reads a message from the response channel and tries to parse it into the
|
||||||
|
// given msg interface.
|
||||||
|
func (c *inProcClient) Recv(msg interface{}) error {
|
||||||
|
data := <-c.buffer.responses
|
||||||
|
return json.Unmarshal(data, &msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the collection of modules the RPC server offers.
|
||||||
|
func (c *inProcClient) SupportedModules() (map[string]string, error) {
|
||||||
|
return SupportedModules(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inprocBuffer represents the connection between the RPC server and console
|
||||||
|
type inprocBuffer struct {
|
||||||
|
readBuf []byte // store remaining request bytes after a partial read
|
||||||
|
requests chan []byte // list with raw serialized requests
|
||||||
|
responses chan []byte // list with raw serialized responses
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read will read the next request in json format.
|
||||||
|
func (b *inprocBuffer) Read(p []byte) (int, error) {
|
||||||
|
// last read didn't read entire request, return remaining bytes
|
||||||
|
if len(b.readBuf) > 0 {
|
||||||
|
n := copy(p, b.readBuf)
|
||||||
|
if n < len(b.readBuf) {
|
||||||
|
b.readBuf = b.readBuf[:n]
|
||||||
|
} else {
|
||||||
|
b.readBuf = b.readBuf[:0]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
// read next request
|
||||||
|
req := <-b.requests
|
||||||
|
n := copy(p, req)
|
||||||
|
if n < len(req) {
|
||||||
|
// inprocBuffer too small, store remaining chunk for next read
|
||||||
|
b.readBuf = req[n:]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write sends the given buffer to the backend.
|
||||||
|
func (b *inprocBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
b.responses <- p
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up obtained resources.
|
||||||
|
func (b *inprocBuffer) Close() error {
|
||||||
|
close(b.requests)
|
||||||
|
close(b.responses)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -38,7 +38,7 @@ type ipcClient struct {
|
|||||||
// NewIPCClient create a new IPC client that will connect on the given endpoint. Messages are JSON encoded and encoded.
|
// NewIPCClient create a new IPC client that will connect on the given endpoint. Messages are JSON encoded and encoded.
|
||||||
// On Unix it assumes the endpoint is the full path to a unix socket, and Windows the endpoint is an identifier for a
|
// On Unix it assumes the endpoint is the full path to a unix socket, and Windows the endpoint is an identifier for a
|
||||||
// named pipe.
|
// named pipe.
|
||||||
func NewIPCClient(endpoint string) (*ipcClient, error) {
|
func NewIPCClient(endpoint string) (Client, error) {
|
||||||
conn, err := newIPCConnection(endpoint)
|
conn, err := newIPCConnection(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -239,9 +239,6 @@ func Dial(address string) (*PipeConn, error) {
|
|||||||
for {
|
for {
|
||||||
conn, err := dial(address, nmpwait_wait_forever)
|
conn, err := dial(address, nmpwait_wait_forever)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Ugly hack working around some async connectivity issues
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
if isPipeNotReady(err) {
|
if isPipeNotReady(err) {
|
||||||
@ -363,9 +360,6 @@ func Listen(address string) (*PipeListener, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Ugly hack working around some async connectivity issues
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
return &PipeListener{
|
return &PipeListener{
|
||||||
addr: PipeAddr(address),
|
addr: PipeAddr(address),
|
||||||
handle: handle,
|
handle: handle,
|
||||||
|
@ -109,7 +109,7 @@ type wsClient struct {
|
|||||||
|
|
||||||
// NewWSClientj creates a new RPC client that communicates with a RPC server
|
// NewWSClientj creates a new RPC client that communicates with a RPC server
|
||||||
// that is listening on the given endpoint using JSON encoding.
|
// that is listening on the given endpoint using JSON encoding.
|
||||||
func NewWSClient(endpoint string) (*wsClient, error) {
|
func NewWSClient(endpoint string) (Client, error) {
|
||||||
return &wsClient{endpoint: endpoint}, nil
|
return &wsClient{endpoint: endpoint}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user