Browse Source

support key-value params in JSONRPC (Refs #1)

More changes:

- remove Client interface (reason: empty)
- introduce HTTPClient interface, which can be used for both ClientURI
  and ClientJSONRPC clients (so our users don't have to create their own) (Refs #8)
- rename integration tests script to `integration_test.sh`
- do not update deps on `get_deps`
pull/456/head
Anton Kaliaev 8 years ago
parent
commit
e1d5873bdf
No known key found for this signature in database GPG Key ID: 7B6881D965918214
10 changed files with 147 additions and 112 deletions
  1. +10
    -4
      Makefile
  2. +10
    -10
      README.md
  3. +17
    -15
      client/http_client.go
  4. +6
    -6
      client/ws_client.go
  5. +10
    -6
      rpc_test.go
  6. +63
    -40
      server/handlers.go
  7. +7
    -4
      test/data.json
  8. +14
    -17
      test/integration_test.sh
  9. +3
    -3
      test/main.go
  10. +7
    -7
      types/types.go

+ 10
- 4
Makefile View File

@ -1,9 +1,15 @@
.PHONY: all test get_deps
PACKAGES=$(shell go list ./...)
all: test all: test
test:
bash ./test/test.sh
test:
@echo "--> Running go test --race"
@go test --race $(PACKAGES)
@echo "--> Running integration tests"
@bash ./test/integration_test.sh
get_deps: get_deps:
go get -t -u github.com/tendermint/go-rpc/...
@echo "--> Running go get"
@go get -v -d $(PACKAGES)
.PHONY: all test get_deps

+ 10
- 10
README.md View File

@ -32,16 +32,16 @@ As a POST request, we use JSONRPC. For instance, the same request would have thi
``` ```
{ {
"jsonrpc":"2.0",
"id":"anything",
"method":"hello_world",
"params":["my_world", 5]
"jsonrpc": "2.0",
"id": "anything",
"method": "hello_world",
"params": {
"name": "my_world",
"num": 5
}
} }
``` ```
Note the `params` does not currently support key-value pairs (https://github.com/tendermint/go-rpc/issues/1), so order matters (you can get the order from making a
GET request to `/`)
With the above saved in file `data.json`, we can make the request with With the above saved in file `data.json`, we can make the request with
``` ```
@ -50,8 +50,8 @@ curl --data @data.json http://localhost:8008
## WebSocket (JSONRPC) ## WebSocket (JSONRPC)
All requests are exposed over websocket in the same form as the POST JSONRPC.
Websocket connections are available at their own endpoint, typically `/websocket`,
All requests are exposed over websocket in the same form as the POST JSONRPC.
Websocket connections are available at their own endpoint, typically `/websocket`,
though this is configurable when starting the server. though this is configurable when starting the server.
# Server Definition # Server Definition
@ -102,7 +102,7 @@ go func() {
Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`) Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`)
Now see all available endpoints by sending a GET request to `0.0.0.0:8008`. Now see all available endpoints by sending a GET request to `0.0.0.0:8008`.
Each route is available as a GET request, as a JSONRPCv2 POST request, and via JSONRPCv2 over websockets
Each route is available as a GET request, as a JSONRPCv2 POST request, and via JSONRPCv2 over websockets.
# Examples # Examples


+ 17
- 15
client/http_client.go View File

@ -3,7 +3,6 @@ package rpcclient
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
@ -12,11 +11,16 @@ import (
"reflect" "reflect"
"strings" "strings"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire"
// cmn "github.com/tendermint/go-common"
rpctypes "github.com/tendermint/go-rpc/types"
wire "github.com/tendermint/go-wire"
) )
// HTTPClient is a common interface for ClientJSONRPC and ClientURI.
type HTTPClient interface {
Call(method string, params []interface{}, result interface{}) (interface{}, error)
}
// TODO: Deprecate support for IP:PORT or /path/to/socket // TODO: Deprecate support for IP:PORT or /path/to/socket
func makeHTTPDialer(remoteAddr string) (string, func(string, string) (net.Conn, error)) { func makeHTTPDialer(remoteAddr string) (string, func(string, string) (net.Conn, error)) {
@ -49,11 +53,6 @@ func makeHTTPClient(remoteAddr string) (string, *http.Client) {
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
type Client interface {
}
//------------------------------------------------------------------------------------
// JSON rpc takes params as a slice // JSON rpc takes params as a slice
type ClientJSONRPC struct { type ClientJSONRPC struct {
address string address string
@ -68,11 +67,11 @@ func NewClientJSONRPC(remote string) *ClientJSONRPC {
} }
} }
func (c *ClientJSONRPC) Call(method string, params []interface{}, result interface{}) (interface{}, error) {
func (c *ClientJSONRPC) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
return c.call(method, params, result) return c.call(method, params, result)
} }
func (c *ClientJSONRPC) call(method string, params []interface{}, result interface{}) (interface{}, error) {
func (c *ClientJSONRPC) call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
// Make request and get responseBytes // Make request and get responseBytes
request := rpctypes.RPCRequest{ request := rpctypes.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
@ -80,7 +79,10 @@ func (c *ClientJSONRPC) call(method string, params []interface{}, result interfa
Params: params, Params: params,
ID: "", ID: "",
} }
requestBytes := wire.JSONBytes(request)
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
requestBuf := bytes.NewBuffer(requestBytes) requestBuf := bytes.NewBuffer(requestBytes)
// log.Info(Fmt("RPC request to %v (%v): %v", c.remote, method, string(requestBytes))) // log.Info(Fmt("RPC request to %v (%v): %v", c.remote, method, string(requestBytes)))
httpResponse, err := c.client.Post(c.address, "text/json", requestBuf) httpResponse, err := c.client.Post(c.address, "text/json", requestBuf)
@ -145,16 +147,16 @@ func unmarshalResponseBytes(responseBytes []byte, result interface{}) (interface
response := &rpctypes.RPCResponse{} response := &rpctypes.RPCResponse{}
err = json.Unmarshal(responseBytes, response) err = json.Unmarshal(responseBytes, response)
if err != nil { if err != nil {
return nil, errors.New(Fmt("Error unmarshalling rpc response: %v", err))
return nil, fmt.Errorf("Error unmarshalling rpc response: %v", err)
} }
errorStr := response.Error errorStr := response.Error
if errorStr != "" { if errorStr != "" {
return nil, errors.New(Fmt("Response error: %v", errorStr))
return nil, fmt.Errorf("Response error: %v", errorStr)
} }
// unmarshal the RawMessage into the result // unmarshal the RawMessage into the result
result = wire.ReadJSONPtr(result, *response.Result, &err) result = wire.ReadJSONPtr(result, *response.Result, &err)
if err != nil { if err != nil {
return nil, errors.New(Fmt("Error unmarshalling rpc response result: %v", err))
return nil, fmt.Errorf("Error unmarshalling rpc response result: %v", err)
} }
return result, nil return result, nil
} }


+ 6
- 6
client/ws_client.go View File

@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-rpc/types"
cmn "github.com/tendermint/go-common"
rpctypes "github.com/tendermint/go-rpc/types"
) )
const ( const (
@ -19,7 +19,7 @@ const (
) )
type WSClient struct { type WSClient struct {
BaseService
cmn.BaseService
Address string // IP:PORT or /path/to/socket Address string // IP:PORT or /path/to/socket
Endpoint string // /websocket/url/endpoint Endpoint string // /websocket/url/endpoint
Dialer func(string, string) (net.Conn, error) Dialer func(string, string) (net.Conn, error)
@ -39,7 +39,7 @@ func NewWSClient(remoteAddr, endpoint string) *WSClient {
ResultsCh: make(chan json.RawMessage, wsResultsChannelCapacity), ResultsCh: make(chan json.RawMessage, wsResultsChannelCapacity),
ErrorsCh: make(chan error, wsErrorsChannelCapacity), ErrorsCh: make(chan error, wsErrorsChannelCapacity),
} }
wsClient.BaseService = *NewBaseService(log, "WSClient", wsClient)
wsClient.BaseService = *cmn.NewBaseService(log, "WSClient", wsClient)
return wsClient return wsClient
} }
@ -122,7 +122,7 @@ func (wsc *WSClient) Subscribe(eventid string) error {
JSONRPC: "2.0", JSONRPC: "2.0",
ID: "", ID: "",
Method: "subscribe", Method: "subscribe",
Params: []interface{}{eventid},
Params: map[string]interface{}{"event": eventid},
}) })
return err return err
} }
@ -133,7 +133,7 @@ func (wsc *WSClient) Unsubscribe(eventid string) error {
JSONRPC: "2.0", JSONRPC: "2.0",
ID: "", ID: "",
Method: "unsubscribe", Method: "unsubscribe",
Params: []interface{}{eventid},
Params: map[string]interface{}{"event": eventid},
}) })
return err return err
} }

+ 10
- 6
rpc_test.go View File

@ -5,10 +5,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/tendermint/go-rpc/client"
"github.com/tendermint/go-rpc/server"
"github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire"
rpcclient "github.com/tendermint/go-rpc/client"
rpcserver "github.com/tendermint/go-rpc/server"
rpctypes "github.com/tendermint/go-rpc/types"
wire "github.com/tendermint/go-wire"
) )
// Client and Server should work over tcp or unix sockets // Client and Server should work over tcp or unix sockets
@ -88,7 +88,9 @@ func testURI(t *testing.T, cl *rpcclient.ClientURI) {
func testJSONRPC(t *testing.T, cl *rpcclient.ClientJSONRPC) { func testJSONRPC(t *testing.T, cl *rpcclient.ClientJSONRPC) {
val := "acbd" val := "acbd"
params := []interface{}{val}
params := map[string]interface{}{
"arg": val,
}
var result Result var result Result
_, err := cl.Call("status", params, &result) _, err := cl.Call("status", params, &result)
if err != nil { if err != nil {
@ -102,7 +104,9 @@ func testJSONRPC(t *testing.T, cl *rpcclient.ClientJSONRPC) {
func testWS(t *testing.T, cl *rpcclient.WSClient) { func testWS(t *testing.T, cl *rpcclient.WSClient) {
val := "acbd" val := "acbd"
params := []interface{}{val}
params := map[string]interface{}{
"arg": val,
}
err := cl.WriteJSON(rpctypes.RPCRequest{ err := cl.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: "", ID: "",


+ 63
- 40
server/handlers.go View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -14,10 +13,10 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-events"
. "github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire"
cmn "github.com/tendermint/go-common"
events "github.com/tendermint/go-events"
types "github.com/tendermint/go-rpc/types"
wire "github.com/tendermint/go-wire"
) )
// Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions. // Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
@ -105,75 +104,99 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc {
return return
} }
var request RPCRequest
var request types.RPCRequest
err := json.Unmarshal(b, &request) err := json.Unmarshal(b, &request)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, NewRPCResponse("", nil, fmt.Sprintf("Error unmarshalling request: %v", err.Error())))
WriteRPCResponseHTTP(w, types.NewRPCResponse("", nil, fmt.Sprintf("Error unmarshalling request: %v", err.Error())))
return return
} }
if len(r.URL.Path) > 1 { if len(r.URL.Path) > 1 {
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path)))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path)))
return return
} }
rpcFunc := funcMap[request.Method] rpcFunc := funcMap[request.Method]
if rpcFunc == nil { if rpcFunc == nil {
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
return return
} }
if rpcFunc.ws { if rpcFunc.ws {
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, nil, "RPC method is only for websockets: "+request.Method))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, nil, "RPC method is only for websockets: "+request.Method))
return return
} }
args, err := jsonParamsToArgs(rpcFunc, request.Params) args, err := jsonParamsToArgs(rpcFunc, request.Params)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, nil, fmt.Sprintf("Error converting json params to arguments: %v", err.Error())))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Error converting json params to arguments: %v", err.Error())))
return return
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
log.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) log.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, result, fmt.Sprintf("Error unreflecting result: %v", err.Error())))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, result, fmt.Sprintf("Error unreflecting result: %v", err.Error())))
return return
} }
WriteRPCResponseHTTP(w, NewRPCResponse(request.ID, result, ""))
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, result, ""))
} }
} }
// Convert a list of interfaces to properly typed values // Convert a list of interfaces to properly typed values
func jsonParamsToArgs(rpcFunc *RPCFunc, params []interface{}) ([]reflect.Value, error) {
func jsonParamsToArgs(rpcFunc *RPCFunc, params map[string]interface{}) ([]reflect.Value, error) {
if len(rpcFunc.argNames) != len(params) { if len(rpcFunc.argNames) != len(params) {
return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)",
len(rpcFunc.argNames), rpcFunc.argNames, len(params), params))
return nil, fmt.Errorf("Expected %v parameters (%v), got %v (%v)",
len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
} }
values := make([]reflect.Value, len(params)) values := make([]reflect.Value, len(params))
for i, p := range params {
for name, param := range params {
i := indexOf(name, rpcFunc.argNames)
if -1 == i {
return nil, fmt.Errorf("%s is not an argument (args: %v)", name, rpcFunc.argNames)
}
ty := rpcFunc.args[i] ty := rpcFunc.args[i]
v, err := _jsonObjectToArg(ty, p)
v, err := _jsonObjectToArg(ty, param)
if err != nil { if err != nil {
return nil, err return nil, err
} }
values[i] = v values[i] = v
} }
return values, nil return values, nil
} }
// indexOf returns index of a string in a slice of strings, -1 if not found.
func indexOf(value string, values []string) int {
for i, v := range values {
if v == value {
return i
}
}
return -1
}
// Same as above, but with the first param the websocket connection // Same as above, but with the first param the websocket connection
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params []interface{}, wsCtx WSRPCContext) ([]reflect.Value, error) {
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params map[string]interface{}, wsCtx types.WSRPCContext) ([]reflect.Value, error) {
if len(rpcFunc.argNames) != len(params) { if len(rpcFunc.argNames) != len(params) {
return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)",
len(rpcFunc.argNames)-1, rpcFunc.argNames[1:], len(params), params))
return nil, fmt.Errorf("Expected %v parameters (%v), got %v (%v)",
len(rpcFunc.argNames)-1, rpcFunc.argNames[1:], len(params), params)
} }
values := make([]reflect.Value, len(params)+1) values := make([]reflect.Value, len(params)+1)
values[0] = reflect.ValueOf(wsCtx) values[0] = reflect.ValueOf(wsCtx)
for i, p := range params {
for name, param := range params {
i := indexOf(name, rpcFunc.argNames)
if -1 == i {
return nil, fmt.Errorf("%s is not an argument (args: %v)", name, rpcFunc.argNames)
}
ty := rpcFunc.args[i+1] ty := rpcFunc.args[i+1]
v, err := _jsonObjectToArg(ty, p)
v, err := _jsonObjectToArg(ty, param)
if err != nil { if err != nil {
return nil, err return nil, err
} }
values[i+1] = v values[i+1] = v
} }
return values, nil return values, nil
} }
@ -197,7 +220,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc) func(http.ResponseWriter, *http.Request)
// Exception for websocket endpoints // Exception for websocket endpoints
if rpcFunc.ws { if rpcFunc.ws {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
WriteRPCResponseHTTP(w, NewRPCResponse("", nil, "This RPC method is only for websockets"))
WriteRPCResponseHTTP(w, types.NewRPCResponse("", nil, "This RPC method is only for websockets"))
} }
} }
// All other endpoints // All other endpoints
@ -205,17 +228,17 @@ func makeHTTPHandler(rpcFunc *RPCFunc) func(http.ResponseWriter, *http.Request)
log.Debug("HTTP HANDLER", "req", r) log.Debug("HTTP HANDLER", "req", r)
args, err := httpParamsToArgs(rpcFunc, r) args, err := httpParamsToArgs(rpcFunc, r)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, NewRPCResponse("", nil, fmt.Sprintf("Error converting http params to args: %v", err.Error())))
WriteRPCResponseHTTP(w, types.NewRPCResponse("", nil, fmt.Sprintf("Error converting http params to args: %v", err.Error())))
return return
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
log.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) log.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, NewRPCResponse("", nil, fmt.Sprintf("Error unreflecting result: %v", err.Error())))
WriteRPCResponseHTTP(w, types.NewRPCResponse("", nil, fmt.Sprintf("Error unreflecting result: %v", err.Error())))
return return
} }
WriteRPCResponseHTTP(w, NewRPCResponse("", result, ""))
WriteRPCResponseHTTP(w, types.NewRPCResponse("", result, ""))
} }
} }
@ -313,11 +336,11 @@ const (
// contains listener id, underlying ws connection, // contains listener id, underlying ws connection,
// and the event switch for subscribing to events // and the event switch for subscribing to events
type wsConnection struct { type wsConnection struct {
BaseService
cmn.BaseService
remoteAddr string remoteAddr string
baseConn *websocket.Conn baseConn *websocket.Conn
writeChan chan RPCResponse
writeChan chan types.RPCResponse
readTimeout *time.Timer readTimeout *time.Timer
pingTicker *time.Ticker pingTicker *time.Ticker
@ -330,11 +353,11 @@ func NewWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, evsw
wsc := &wsConnection{ wsc := &wsConnection{
remoteAddr: baseConn.RemoteAddr().String(), remoteAddr: baseConn.RemoteAddr().String(),
baseConn: baseConn, baseConn: baseConn,
writeChan: make(chan RPCResponse, writeChanCapacity), // error when full.
writeChan: make(chan types.RPCResponse, writeChanCapacity), // error when full.
funcMap: funcMap, funcMap: funcMap,
evsw: evsw, evsw: evsw,
} }
wsc.BaseService = *NewBaseService(log, "wsConnection", wsc)
wsc.BaseService = *cmn.NewBaseService(log, "wsConnection", wsc)
return wsc return wsc
} }
@ -399,7 +422,7 @@ func (wsc *wsConnection) GetEventSwitch() events.EventSwitch {
// Implements WSRPCConnection // Implements WSRPCConnection
// Blocking write to writeChan until service stops. // Blocking write to writeChan until service stops.
// Goroutine-safe // Goroutine-safe
func (wsc *wsConnection) WriteRPCResponse(resp RPCResponse) {
func (wsc *wsConnection) WriteRPCResponse(resp types.RPCResponse) {
select { select {
case <-wsc.Quit: case <-wsc.Quit:
return return
@ -410,7 +433,7 @@ func (wsc *wsConnection) WriteRPCResponse(resp RPCResponse) {
// Implements WSRPCConnection // Implements WSRPCConnection
// Nonblocking write. // Nonblocking write.
// Goroutine-safe // Goroutine-safe
func (wsc *wsConnection) TryWriteRPCResponse(resp RPCResponse) bool {
func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool {
select { select {
case <-wsc.Quit: case <-wsc.Quit:
return false return false
@ -444,11 +467,11 @@ func (wsc *wsConnection) readRoutine() {
wsc.Stop() wsc.Stop()
return return
} }
var request RPCRequest
var request types.RPCRequest
err = json.Unmarshal(in, &request) err = json.Unmarshal(in, &request)
if err != nil { if err != nil {
errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error()) errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
wsc.WriteRPCResponse(NewRPCResponse(request.ID, nil, errStr))
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, errStr))
continue continue
} }
@ -456,28 +479,28 @@ func (wsc *wsConnection) readRoutine() {
rpcFunc := wsc.funcMap[request.Method] rpcFunc := wsc.funcMap[request.Method]
if rpcFunc == nil { if rpcFunc == nil {
wsc.WriteRPCResponse(NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
continue continue
} }
var args []reflect.Value var args []reflect.Value
if rpcFunc.ws { if rpcFunc.ws {
wsCtx := WSRPCContext{Request: request, WSRPCConnection: wsc}
wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc}
args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx) args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx)
} else { } else {
args, err = jsonParamsToArgs(rpcFunc, request.Params) args, err = jsonParamsToArgs(rpcFunc, request.Params)
} }
if err != nil { if err != nil {
wsc.WriteRPCResponse(NewRPCResponse(request.ID, nil, err.Error()))
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
continue continue
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
log.Info("WSJSONRPC", "method", request.Method, "args", args, "returns", returns) log.Info("WSJSONRPC", "method", request.Method, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
wsc.WriteRPCResponse(NewRPCResponse(request.ID, nil, err.Error()))
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
continue continue
} else { } else {
wsc.WriteRPCResponse(NewRPCResponse(request.ID, result, ""))
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, result, ""))
continue continue
} }


+ 7
- 4
test/data.json View File

@ -1,6 +1,9 @@
{ {
"jsonrpc":"2.0",
"id":"",
"method":"hello_world",
"params":["my_world", 5]
"jsonrpc": "2.0",
"id": "",
"method": "hello_world",
"params": {
"name": "my_world",
"num": 5
}
} }

test/test.sh → test/integration_test.sh View File


+ 3
- 3
test/main.go View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
. "github.com/tendermint/go-common"
cmn "github.com/tendermint/go-common"
rpcserver "github.com/tendermint/go-rpc/server" rpcserver "github.com/tendermint/go-rpc/server"
) )
@ -25,11 +25,11 @@ func main() {
rpcserver.RegisterRPCFuncs(mux, routes) rpcserver.RegisterRPCFuncs(mux, routes)
_, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux) _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux)
if err != nil { if err != nil {
Exit(err.Error())
cmn.Exit(err.Error())
} }
// Wait forever // Wait forever
TrapSignal(func() {
cmn.TrapSignal(func() {
}) })
} }

+ 7
- 7
types/types.go View File

@ -4,18 +4,18 @@ import (
"encoding/json" "encoding/json"
"strings" "strings"
"github.com/tendermint/go-events"
"github.com/tendermint/go-wire"
events "github.com/tendermint/go-events"
wire "github.com/tendermint/go-wire"
) )
type RPCRequest struct { type RPCRequest struct {
JSONRPC string `json:"jsonrpc"`
ID string `json:"id"`
Method string `json:"method"`
Params []interface{} `json:"params"`
JSONRPC string `json:"jsonrpc"`
ID string `json:"id"`
Method string `json:"method"`
Params map[string]interface{} `json:"params"`
} }
func NewRPCRequest(id string, method string, params []interface{}) RPCRequest {
func NewRPCRequest(id string, method string, params map[string]interface{}) RPCRequest {
return RPCRequest{ return RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: id, ID: id,


Loading…
Cancel
Save