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`
This commit is contained in:
Anton Kaliaev
2017-03-07 18:34:54 +04:00
parent b03facd828
commit e1d5873bdf
10 changed files with 150 additions and 115 deletions

View File

@ -1,9 +1,15 @@
.PHONY: all test get_deps PACKAGES=$(shell go list ./...)
all: test all: test
test: test:
bash ./test/test.sh @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

View File

@ -32,16 +32,16 @@ As a POST request, we use JSONRPC. For instance, the same request would have thi
``` ```
{ {
"jsonrpc":"2.0", "jsonrpc": "2.0",
"id":"anything", "id": "anything",
"method":"hello_world", "method": "hello_world",
"params":["my_world", 5] "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. All requests are exposed over websocket in the same form as the POST JSONRPC.
Websocket connections are available at their own endpoint, typically `/websocket`, 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

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" // cmn "github.com/tendermint/go-common"
"github.com/tendermint/go-rpc/types" rpctypes "github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire" 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
} }

View File

@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
. "github.com/tendermint/go-common" cmn "github.com/tendermint/go-common"
"github.com/tendermint/go-rpc/types" 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
} }

View File

@ -5,10 +5,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/tendermint/go-rpc/client" rpcclient "github.com/tendermint/go-rpc/client"
"github.com/tendermint/go-rpc/server" rpcserver "github.com/tendermint/go-rpc/server"
"github.com/tendermint/go-rpc/types" rpctypes "github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire" 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: "",

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" cmn "github.com/tendermint/go-common"
"github.com/tendermint/go-events" events "github.com/tendermint/go-events"
. "github.com/tendermint/go-rpc/types" types "github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire" 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)", return nil, fmt.Errorf("Expected %v parameters (%v), got %v (%v)",
len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)) 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
} }
// Same as above, but with the first param the websocket connection // indexOf returns index of a string in a slice of strings, -1 if not found.
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params []interface{}, wsCtx WSRPCContext) ([]reflect.Value, error) { func indexOf(value string, values []string) int {
if len(rpcFunc.argNames) != len(params) { for i, v := range values {
return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)", if v == value {
len(rpcFunc.argNames)-1, rpcFunc.argNames[1:], len(params), params)) return i
}
} }
return -1
}
// Same as above, but with the first param the websocket connection
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params map[string]interface{}, wsCtx types.WSRPCContext) ([]reflect.Value, error) {
if len(rpcFunc.argNames) != len(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
} }

View File

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

View File

@ -1,26 +1,22 @@
#! /bin/bash #!/usr/bin/env bash
cd $GOPATH/src/github.com/tendermint/go-rpc
# get deps
go get -u -t ./...
# go tests
go test --race github.com/tendermint/go-rpc/...
# integration tests
cd test
set -e set -e
# Get the directory of where this script is.
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
# Change into that dir because we expect that.
pushd "$DIR"
go build -o server main.go go build -o server main.go
./server > /dev/null & ./server > /dev/null &
PID=$! PID=$!
sleep 2 sleep 2
# simple request # simple request
R1=`curl -s 'http://localhost:8008/hello_world?name="my_world"&num=5'` R1=$(curl -s 'http://localhost:8008/hello_world?name="my_world"&num=5')
R2=`curl -s --data @data.json http://localhost:8008` R2=$(curl -s --data @data.json http://localhost:8008)
if [[ "$R1" != "$R2" ]]; then if [[ "$R1" != "$R2" ]]; then
echo "responses are not identical:" echo "responses are not identical:"
echo "R1: $R1" echo "R1: $R1"
@ -31,7 +27,7 @@ else
fi fi
# request with 0x-prefixed hex string arg # request with 0x-prefixed hex string arg
R1=`curl -s 'http://localhost:8008/hello_world?name=0x41424344&num=123'` R1=$(curl -s 'http://localhost:8008/hello_world?name=0x41424344&num=123')
R2='{"jsonrpc":"2.0","id":"","result":{"Result":"hi ABCD 123"},"error":""}' R2='{"jsonrpc":"2.0","id":"","result":{"Result":"hi ABCD 123"},"error":""}'
if [[ "$R1" != "$R2" ]]; then if [[ "$R1" != "$R2" ]]; then
echo "responses are not identical:" echo "responses are not identical:"
@ -43,7 +39,7 @@ else
fi fi
# request with unquoted string arg # request with unquoted string arg
R1=`curl -s 'http://localhost:8008/hello_world?name=abcd&num=123'` R1=$(curl -s 'http://localhost:8008/hello_world?name=abcd&num=123')
R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: invalid character 'a' looking for beginning of value\"}" R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: invalid character 'a' looking for beginning of value\"}"
if [[ "$R1" != "$R2" ]]; then if [[ "$R1" != "$R2" ]]; then
echo "responses are not identical:" echo "responses are not identical:"
@ -55,7 +51,7 @@ else
fi fi
# request with string type when expecting number arg # request with string type when expecting number arg
R1=`curl -s 'http://localhost:8008/hello_world?name="abcd"&num=0xabcd'` R1=$(curl -s 'http://localhost:8008/hello_world?name="abcd"&num=0xabcd')
R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: Got a hex string arg, but expected 'int'\"}" R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: Got a hex string arg, but expected 'int'\"}"
if [[ "$R1" != "$R2" ]]; then if [[ "$R1" != "$R2" ]]; then
echo "responses are not identical:" echo "responses are not identical:"
@ -67,3 +63,4 @@ else
fi fi
kill -9 $PID || exit 0 kill -9 $PID || exit 0
popd

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() {
}) })
} }

View File

@ -4,18 +4,18 @@ import (
"encoding/json" "encoding/json"
"strings" "strings"
"github.com/tendermint/go-events" events "github.com/tendermint/go-events"
"github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
) )
type RPCRequest struct { type RPCRequest struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
ID string `json:"id"` ID string `json:"id"`
Method string `json:"method"` Method string `json:"method"`
Params []interface{} `json:"params"` 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,