Commit 8ebf2d8f authored by Bas van Kervel's avatar Bas van Kervel Committed by Bas van Kervel

added RPC/IPC support

parent 2f55a1d7
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"github.com/ethereum/go-ethereum/rpc/shared"
) )
const ( const (
...@@ -34,3 +35,43 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. ...@@ -34,3 +35,43 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
return apis, nil return apis, nil
} }
// combines multiple API's
type mergedApi struct {
apis map[string]EthereumApi
}
// create new merged api instance
func newMergedApi(apis ...EthereumApi) *mergedApi {
mergedApi := new(mergedApi)
mergedApi.apis = make(map[string]EthereumApi)
for _, api := range apis {
for _, method := range api.Methods() {
mergedApi.apis[method] = api
}
}
return mergedApi
}
// Supported RPC methods
func (self *mergedApi) Methods() []string {
all := make([]string, len(self.apis))
for method, _ := range self.apis {
all = append(all, method)
}
return all
}
// Call the correct API's Execute method for the given request
func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) {
if api, found := self.apis[req.Method]; found {
return api.Execute(req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
// Merge multiple API's to a single API instance
func Merge(apis ...EthereumApi) EthereumApi {
return newMergedApi(apis...)
}
package comms
type EthereumClient interface {
Close()
Send(interface{}) error
Recv() (interface{}, error)
}
package comms
import (
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
)
type IpcConfig struct {
Endpoint string
}
type ipcClient struct {
c codec.ApiCoder
}
func (self *ipcClient) Close() {
self.c.Close()
}
func (self *ipcClient) Send(req interface{}) error {
return self.c.WriteResponse(req)
}
func (self *ipcClient) Recv() (interface{}, error) {
return self.c.ReadResponse()
}
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
return newIpcClient(cfg, codec)
}
// Start IPC server
func StartIpc(cfg IpcConfig, codec codec.Codec, apis ...api.EthereumApi) error {
offeredApi := api.Merge(apis...)
return startIpc(cfg, codec, offeredApi)
}
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
package comms
import (
"io"
"net"
"os"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"})
if err != nil {
return nil, err
}
return &ipcClient{codec.New(c)}, nil
}
func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error {
os.Remove(cfg.Endpoint) // in case it still exists from a previous run
l, err := net.ListenUnix("unix", &net.UnixAddr{Name: cfg.Endpoint, Net: "unix"})
if err != nil {
return err
}
os.Chmod(cfg.Endpoint, 0600)
go func() {
for {
conn, err := l.AcceptUnix()
if err != nil {
glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err)
continue
}
go func(conn net.Conn) {
codec := codec.New(conn)
for {
req, err := codec.ReadRequest()
if err == io.EOF {
codec.Close()
return
} else if err != nil {
glog.V(logger.Error).Infof("IPC recv err - %v\n", err)
codec.Close()
return
}
var rpcResponse interface{}
res, err := api.Execute(req)
rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
err = codec.WriteResponse(rpcResponse)
if err != nil {
glog.V(logger.Error).Infof("IPC send err - %v\n", err)
codec.Close()
return
}
}
}(conn)
}
os.Remove(cfg.Endpoint)
}()
glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint)
return nil
}
This diff is collapsed.
package shared package shared
import "encoding/json" import (
"encoding/json"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
// RPC request // RPC request
type Request struct { type Request struct {
...@@ -36,3 +40,24 @@ type ErrorObject struct { ...@@ -36,3 +40,24 @@ type ErrorObject struct {
Message string `json:"message"` Message string `json:"message"`
// Data interface{} `json:"data"` // Data interface{} `json:"data"`
} }
func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err error) *interface{} {
var response interface{}
switch err.(type) {
case nil:
response = &SuccessResponse{Jsonrpc: jsonrpcver, Id: id, Result: reply}
case *NotImplementedError:
jsonerr := &ErrorObject{-32601, err.Error()}
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
jsonerr := &ErrorObject{-32602, err.Error()}
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
default:
jsonerr := &ErrorObject{-32603, err.Error()}
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
}
glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
return &response
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment