cleanup rpc/handlers
This commit is contained in:
parent
1a4aab4c35
commit
e257307b01
|
@ -150,7 +150,7 @@ func (n *Node) DialSeed() {
|
|||
}
|
||||
}
|
||||
|
||||
func (n *Node) StartRpc() {
|
||||
func (n *Node) StartRPC() {
|
||||
core.SetBlockStore(n.blockStore)
|
||||
core.SetConsensusState(n.consensusState)
|
||||
core.SetMempoolReactor(n.mempoolReactor)
|
||||
|
@ -185,7 +185,7 @@ func Daemon() {
|
|||
|
||||
// Run the RPC server.
|
||||
if config.App().GetString("RPC.HTTP.ListenAddr") != "" {
|
||||
n.StartRpc()
|
||||
n.StartRPC()
|
||||
}
|
||||
|
||||
// Sleep forever and then...
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package rpc
|
||||
|
||||
/*
|
||||
TODO: support Call && GetStorage.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -12,7 +16,6 @@ import (
|
|||
|
||||
// cache all type information about each function up front
|
||||
// (func, responseStruct, argNames)
|
||||
// XXX: response structs are allocated once and reused - will this cause an issue eg. if a field ever not overwritten?
|
||||
var funcMap = map[string]*FuncWrapper{
|
||||
"status": funcWrap(core.Status, []string{}),
|
||||
"net_info": funcWrap(core.NetInfo, []string{}),
|
||||
|
@ -26,6 +29,18 @@ var funcMap = map[string]*FuncWrapper{
|
|||
"unsafe/sign_tx": funcWrap(core.SignTx, []string{"tx", "privAccounts"}),
|
||||
}
|
||||
|
||||
func initHandlers() {
|
||||
// HTTP endpoints
|
||||
for funcName, funcInfo := range funcMap {
|
||||
http.HandleFunc("/"+funcName, toHttpHandler(funcInfo))
|
||||
}
|
||||
|
||||
// JSONRPC endpoints
|
||||
http.HandleFunc("/", JSONRPCHandler)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
// holds all type information for each function
|
||||
type FuncWrapper struct {
|
||||
f reflect.Value // function from "rpc/core"
|
||||
|
@ -43,170 +58,6 @@ func funcWrap(f interface{}, args []string) *FuncWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
// convert from a function name to the http handler
|
||||
func toHandler(funcName string) func(http.ResponseWriter, *http.Request) {
|
||||
funcInfo := funcMap[funcName]
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
values, err := queryToValues(funcInfo, r)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(values)
|
||||
response, err := returnsToResponse(funcInfo, returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
}
|
||||
|
||||
// convert a (json) string to a given type
|
||||
func jsonToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
||||
var err error
|
||||
v := reflect.New(ty)
|
||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v = v.Elem()
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) {
|
||||
var err error
|
||||
v := reflect.New(ty)
|
||||
binary.ReadJSONFromObject(v.Interface(), object, &err)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v = v.Elem()
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// covert an http query to a list of properly typed values.
|
||||
// to be properly decoded the arg must be a concrete type from tendermint (if its an interface).
|
||||
func queryToValues(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, error) {
|
||||
argTypes := funcInfo.args
|
||||
argNames := funcInfo.argNames
|
||||
|
||||
var err error
|
||||
values := make([]reflect.Value, len(argNames))
|
||||
for i, name := range argNames {
|
||||
ty := argTypes[i]
|
||||
arg := GetParam(r, name)
|
||||
//fmt.Println("GetParam()", r, name, arg)
|
||||
values[i], err = jsonToArg(ty, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// covert a list of interfaces to properly typed values
|
||||
func paramsToValues(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) {
|
||||
values := make([]reflect.Value, len(params))
|
||||
for i, p := range params {
|
||||
ty := funcInfo.args[i]
|
||||
v, err := jsonObjectToArg(ty, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[i] = v
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// returns is Response struct and error. If error is not nil, return it
|
||||
func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interface{}, error) {
|
||||
errV := returns[1]
|
||||
if errV.Interface() != nil {
|
||||
return nil, fmt.Errorf("%v", errV.Interface())
|
||||
}
|
||||
return returns[0].Interface(), nil
|
||||
}
|
||||
|
||||
/*
|
||||
// convert a list of values to a populated struct with the correct types
|
||||
func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interface{}, error) {
|
||||
returnTypes := funcInfo.returns
|
||||
finalType := returnTypes[len(returnTypes)-1]
|
||||
if finalType.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||
errV := returns[len(returnTypes)-1]
|
||||
if errV.Interface() != nil {
|
||||
return nil, fmt.Errorf("%v", errV.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
// copy the response struct (New returns a pointer so we have to Elem() twice)
|
||||
v := reflect.New(funcInfo.response.Elem().Type()).Elem()
|
||||
nFields := v.NumField()
|
||||
for i := 0; i < nFields; i++ {
|
||||
field := v.FieldByIndex([]int{i})
|
||||
field.Set(returns[i])
|
||||
}
|
||||
|
||||
return v.Interface(), nil
|
||||
}*/
|
||||
|
||||
// jsonrpc calls grab the given method's function info and runs reflect.Call
|
||||
func JsonRpcHandler(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := ioutil.ReadAll(r.Body)
|
||||
var jrpc JsonRpc
|
||||
err := json.Unmarshal(b, &jrpc)
|
||||
if err != nil {
|
||||
// TODO
|
||||
}
|
||||
|
||||
funcInfo := funcMap[jrpc.Method]
|
||||
values, err := paramsToValues(funcInfo, jrpc.Params)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(values)
|
||||
response, err := returnsToResponse(funcInfo, returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
|
||||
func initHandlers() {
|
||||
// HTTP endpoints
|
||||
// toHandler runs once for each function and caches
|
||||
// all reflection data
|
||||
http.HandleFunc("/status", toHandler("status"))
|
||||
http.HandleFunc("/net_info", toHandler("net_info"))
|
||||
http.HandleFunc("/blockchain", toHandler("blockchain"))
|
||||
http.HandleFunc("/get_block", toHandler("get_block"))
|
||||
http.HandleFunc("/get_account", toHandler("get_account"))
|
||||
http.HandleFunc("/list_validators", toHandler("list_validators"))
|
||||
http.HandleFunc("/broadcast_tx", toHandler("broadcast_tx"))
|
||||
http.HandleFunc("/list_accounts", toHandler("list_accounts"))
|
||||
http.HandleFunc("/unsafe/gen_priv_account", toHandler("unsafe/gen_priv_account"))
|
||||
http.HandleFunc("/unsafe/sign_tx", toHandler("unsafe/sign_tx"))
|
||||
//http.HandleFunc("/call", CallHandler)
|
||||
//http.HandleFunc("/get_storage", GetStorageHandler)
|
||||
|
||||
// JsonRPC endpoints
|
||||
http.HandleFunc("/", JsonRpcHandler)
|
||||
// unsafe JsonRPC endpoints
|
||||
//http.HandleFunc("/unsafe", UnsafeJsonRpcHandler)
|
||||
|
||||
}
|
||||
|
||||
type JsonRpc struct {
|
||||
JsonRpc string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params []interface{} `json:"params"`
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
// this will panic if not passed a function
|
||||
func funcArgTypes(f interface{}) []reflect.Type {
|
||||
t := reflect.TypeOf(f)
|
||||
n := t.NumIn()
|
||||
|
@ -226,3 +77,126 @@ func funcReturnTypes(f interface{}) []reflect.Type {
|
|||
}
|
||||
return types
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// rpc.json
|
||||
|
||||
type JSONRPC struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params []interface{} `json:"params"`
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
// jsonrpc calls grab the given method's function info and runs reflect.Call
|
||||
func JSONRPCHandler(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := ioutil.ReadAll(r.Body)
|
||||
var jrpc JSONRPC
|
||||
err := json.Unmarshal(b, &jrpc)
|
||||
if err != nil {
|
||||
// TODO
|
||||
}
|
||||
|
||||
funcInfo := funcMap[jrpc.Method]
|
||||
args, err := jsonParamsToArgs(funcInfo, jrpc.Params)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(args)
|
||||
response, err := returnsToResponse(returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
|
||||
// covert a list of interfaces to properly typed values
|
||||
func jsonParamsToArgs(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) {
|
||||
values := make([]reflect.Value, len(params))
|
||||
for i, p := range params {
|
||||
ty := funcInfo.args[i]
|
||||
v, err := _jsonObjectToArg(ty, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[i] = v
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) {
|
||||
var err error
|
||||
v := reflect.New(ty)
|
||||
binary.ReadJSONFromObject(v.Interface(), object, &err)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v = v.Elem()
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// rpc.json
|
||||
//-----------------------------------------------------------------------------
|
||||
// rpc.http
|
||||
|
||||
// convert from a function name to the http handler
|
||||
func toHttpHandler(funcInfo *FuncWrapper) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
args, err := httpParamsToArgs(funcInfo, r)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(args)
|
||||
response, err := returnsToResponse(returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Covert an http query to a list of properly typed values.
|
||||
// To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
|
||||
func httpParamsToArgs(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, error) {
|
||||
argTypes := funcInfo.args
|
||||
argNames := funcInfo.argNames
|
||||
|
||||
var err error
|
||||
values := make([]reflect.Value, len(argNames))
|
||||
for i, name := range argNames {
|
||||
ty := argTypes[i]
|
||||
arg := GetParam(r, name)
|
||||
values[i], err = _jsonStringToArg(ty, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
||||
var err error
|
||||
v := reflect.New(ty)
|
||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v = v.Elem()
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// rpc.http
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// returns is Response struct and error. If error is not nil, return it
|
||||
func returnsToResponse(returns []reflect.Value) (interface{}, error) {
|
||||
errV := returns[1]
|
||||
if errV.Interface() != nil {
|
||||
return nil, fmt.Errorf("%v", errV.Interface())
|
||||
}
|
||||
return returns[0].Interface(), nil
|
||||
}
|
|
@ -18,8 +18,8 @@ import (
|
|||
)
|
||||
|
||||
func TestJSONStatus(t *testing.T) {
|
||||
s := rpc.JsonRpc{
|
||||
JsonRpc: "2.0",
|
||||
s := rpc.JSONRPC{
|
||||
JSONRPC: "2.0",
|
||||
Method: "status",
|
||||
Params: []interface{}{},
|
||||
Id: 0,
|
||||
|
@ -53,8 +53,8 @@ func TestJSONStatus(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJSONGenPriv(t *testing.T) {
|
||||
s := rpc.JsonRpc{
|
||||
JsonRpc: "2.0",
|
||||
s := rpc.JSONRPC{
|
||||
JSONRPC: "2.0",
|
||||
Method: "unsafe/gen_priv_account",
|
||||
Params: []interface{}{},
|
||||
Id: 0,
|
||||
|
|
|
@ -41,7 +41,7 @@ func newNode(ready chan struct{}) {
|
|||
node.Start()
|
||||
|
||||
// Run the RPC server.
|
||||
node.StartRpc()
|
||||
node.StartRPC()
|
||||
ready <- struct{}{}
|
||||
|
||||
// Sleep forever
|
||||
|
@ -72,8 +72,8 @@ func getAccount(t *testing.T, typ string, addr []byte) *account.Account {
|
|||
var err error
|
||||
switch typ {
|
||||
case "JSONRPC":
|
||||
s := rpc.JsonRpc{
|
||||
JsonRpc: "2.0",
|
||||
s := rpc.JSONRPC{
|
||||
JSONRPC: "2.0",
|
||||
Method: "get_account",
|
||||
Params: []interface{}{hex.EncodeToString(addr)},
|
||||
Id: 0,
|
||||
|
|
Loading…
Reference in New Issue