109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
|
package node
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ethereum/go-ethereum/les/status"
|
||
|
"github.com/ethereum/go-ethereum/log"
|
||
|
"github.com/status-im/status-go/geth/common"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
jsonrpcVersion = "2.0"
|
||
|
)
|
||
|
|
||
|
type jsonRequest struct {
|
||
|
Method string `json:"method"`
|
||
|
Version string `json:"jsonrpc"`
|
||
|
ID int `json:"id,omitempty"`
|
||
|
Payload json.RawMessage `json:"params,omitempty"`
|
||
|
}
|
||
|
|
||
|
type jsonError struct {
|
||
|
Code int `json:"code"`
|
||
|
Message string `json:"message"`
|
||
|
Data interface{} `json:"data,omitempty"`
|
||
|
}
|
||
|
|
||
|
type jsonErrResponse struct {
|
||
|
Version string `json:"jsonrpc"`
|
||
|
ID interface{} `json:"id,omitempty"`
|
||
|
Error jsonError `json:"error"`
|
||
|
}
|
||
|
|
||
|
// RPCManager abstract RPC management API (for both client and server)
|
||
|
type RPCManager struct {
|
||
|
sync.Mutex
|
||
|
requestID int
|
||
|
nodeManager common.NodeManager
|
||
|
}
|
||
|
|
||
|
// errors
|
||
|
var (
|
||
|
ErrInvalidMethod = errors.New("method does not exist")
|
||
|
ErrRPCServerTimeout = errors.New("RPC server cancelled call due to timeout")
|
||
|
ErrRPCServerCallFailed = errors.New("RPC server cannot complete request")
|
||
|
)
|
||
|
|
||
|
// NewRPCManager returns new instance of RPC client
|
||
|
func NewRPCManager(nodeManager common.NodeManager) *RPCManager {
|
||
|
return &RPCManager{
|
||
|
nodeManager: nodeManager,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Call executes RPC request on node's in-proc RPC server
|
||
|
func (c *RPCManager) Call(inputJSON string) string {
|
||
|
server, err := c.nodeManager.RPCServer()
|
||
|
if err != nil {
|
||
|
return c.makeJSONErrorResponse(err)
|
||
|
}
|
||
|
|
||
|
// allow HTTP requests to block w/o
|
||
|
outputJSON := make(chan string, 1)
|
||
|
go func() {
|
||
|
httpReq := httptest.NewRequest("POST", "/", strings.NewReader(inputJSON))
|
||
|
rr := httptest.NewRecorder()
|
||
|
server.ServeHTTP(rr, httpReq)
|
||
|
|
||
|
// Check the status code is what we expect.
|
||
|
if respStatus := rr.Code; respStatus != http.StatusOK {
|
||
|
log.Error("handler returned wrong status code", "got", respStatus, "want", http.StatusOK)
|
||
|
outputJSON <- c.makeJSONErrorResponse(ErrRPCServerCallFailed)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// everything is ok, return
|
||
|
outputJSON <- rr.Body.String()
|
||
|
}()
|
||
|
|
||
|
// wait till call is complete
|
||
|
select {
|
||
|
case out := <-outputJSON:
|
||
|
return out
|
||
|
case <-time.After((status.DefaultTxSendCompletionTimeout + 10) * time.Minute): // give up eventually
|
||
|
// pass
|
||
|
}
|
||
|
|
||
|
return c.makeJSONErrorResponse(ErrRPCServerTimeout)
|
||
|
}
|
||
|
|
||
|
// makeJSONErrorResponse returns error as JSON response
|
||
|
func (c *RPCManager) makeJSONErrorResponse(err error) string {
|
||
|
response := jsonErrResponse{
|
||
|
Version: jsonrpcVersion,
|
||
|
Error: jsonError{
|
||
|
Message: err.Error(),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
outBytes, _ := json.Marshal(&response)
|
||
|
return string(outBytes)
|
||
|
}
|