mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-22 19:01:19 +00:00
commit
fcea0cda21
8
Makefile
8
Makefile
@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
all: test
|
all: test
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test --race github.com/tendermint/go-rpc/...
|
bash ./test/test.sh
|
||||||
cd ./test && bash test.sh
|
|
||||||
|
|
||||||
|
|
||||||
get_deps:
|
get_deps:
|
||||||
go get -t -d github.com/tendermint/go-rpc/...
|
go get -t -u github.com/tendermint/go-rpc/...
|
||||||
|
@ -16,7 +16,7 @@ checkout:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- "cd $REPO && make get_deps"
|
- "cd $REPO"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
@ -119,7 +121,7 @@ func (c *ClientURI) call(method string, params map[string]interface{}, result in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//log.Info(Fmt("URI request to %v (%v): %v", c.address, method, values))
|
// log.Info(Fmt("URI request to %v (%v): %v", c.address, method, values))
|
||||||
resp, err := c.client.PostForm(c.address+"/"+method, values)
|
resp, err := c.client.PostForm(c.address+"/"+method, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -176,6 +178,14 @@ func argsToJson(args map[string]interface{}) error {
|
|||||||
var n int
|
var n int
|
||||||
var err error
|
var err error
|
||||||
for k, v := range args {
|
for k, v := range args {
|
||||||
|
// Convert byte slices to "0x"-prefixed hex
|
||||||
|
byteSlice, isByteSlice := reflect.ValueOf(v).Interface().([]byte)
|
||||||
|
if isByteSlice {
|
||||||
|
args[k] = fmt.Sprintf("0x%X", byteSlice)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass everything else to go-wire
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
wire.WriteJSON(v, buf, &n, &err)
|
wire.WriteJSON(v, buf, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,7 +19,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WSClient struct {
|
type WSClient struct {
|
||||||
QuitService
|
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.QuitService = *NewQuitService(log, "WSClient", wsClient)
|
wsClient.BaseService = *NewBaseService(log, "WSClient", wsClient)
|
||||||
return wsClient
|
return wsClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ func (wsc *WSClient) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wsc *WSClient) OnStart() error {
|
func (wsc *WSClient) OnStart() error {
|
||||||
wsc.QuitService.OnStart()
|
wsc.BaseService.OnStart()
|
||||||
err := wsc.dial()
|
err := wsc.dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -84,7 +84,7 @@ func (wsc *WSClient) dial() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wsc *WSClient) OnStop() {
|
func (wsc *WSClient) OnStop() {
|
||||||
wsc.QuitService.OnStop()
|
wsc.BaseService.OnStop()
|
||||||
// ResultsCh/ErrorsCh is closed in receiveEventsRoutine.
|
// ResultsCh/ErrorsCh is closed in receiveEventsRoutine.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
rpc_test.go
36
rpc_test.go
@ -164,3 +164,39 @@ func TestWS_UNIX(t *testing.T) {
|
|||||||
}
|
}
|
||||||
testWS(t, cl)
|
testWS(t, cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHexStringArg(t *testing.T) {
|
||||||
|
cl := rpcclient.NewClientURI(tcpAddr)
|
||||||
|
// should NOT be handled as hex
|
||||||
|
val := "0xabc"
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"arg": val,
|
||||||
|
}
|
||||||
|
var result Result
|
||||||
|
_, err := cl.Call("status", params, &result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got := result.(*ResultStatus).Value
|
||||||
|
if got != val {
|
||||||
|
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuotedStringArg(t *testing.T) {
|
||||||
|
cl := rpcclient.NewClientURI(tcpAddr)
|
||||||
|
// should NOT be unquoted
|
||||||
|
val := "\"abc\""
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"arg": val,
|
||||||
|
}
|
||||||
|
var result Result
|
||||||
|
_, err := cl.Call("status", params, &result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got := result.(*ResultStatus).Value
|
||||||
|
if got != val {
|
||||||
|
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package rpcserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -224,12 +225,22 @@ func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error
|
|||||||
argTypes := rpcFunc.args
|
argTypes := rpcFunc.args
|
||||||
argNames := rpcFunc.argNames
|
argNames := rpcFunc.argNames
|
||||||
|
|
||||||
var err error
|
|
||||||
values := make([]reflect.Value, len(argNames))
|
values := make([]reflect.Value, len(argNames))
|
||||||
for i, name := range argNames {
|
for i, name := range argNames {
|
||||||
ty := argTypes[i]
|
ty := argTypes[i]
|
||||||
arg := GetParam(r, name)
|
arg := GetParam(r, name)
|
||||||
//log.Notice("param to arg", "ty", ty, "name", name, "arg", arg)
|
// log.Notice("param to arg", "ty", ty, "name", name, "arg", arg)
|
||||||
|
|
||||||
|
v, err, ok := nonJsonToArg(ty, arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
values[i] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass values to go-wire
|
||||||
values[i], err = _jsonStringToArg(ty, arg)
|
values[i], err = _jsonStringToArg(ty, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -249,6 +260,44 @@ func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nonJsonToArg(ty reflect.Type, arg string) (reflect.Value, error, bool) {
|
||||||
|
isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
|
||||||
|
isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
|
||||||
|
expectingString := ty.Kind() == reflect.String
|
||||||
|
expectingByteSlice := ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8
|
||||||
|
|
||||||
|
if isHexString {
|
||||||
|
if !expectingString && !expectingByteSlice {
|
||||||
|
err := fmt.Errorf("Got a hex string arg, but expected '%s'",
|
||||||
|
ty.Kind().String())
|
||||||
|
return reflect.ValueOf(nil), err, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var value []byte
|
||||||
|
value, err := hex.DecodeString(arg[2:])
|
||||||
|
if err != nil {
|
||||||
|
return reflect.ValueOf(nil), err, false
|
||||||
|
}
|
||||||
|
if ty.Kind() == reflect.String {
|
||||||
|
return reflect.ValueOf(string(value)), nil, true
|
||||||
|
}
|
||||||
|
return reflect.ValueOf([]byte(value)), nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isQuotedString && expectingByteSlice {
|
||||||
|
var err error
|
||||||
|
v := reflect.New(reflect.TypeOf(""))
|
||||||
|
wire.ReadJSONPtr(v.Interface(), []byte(arg), &err)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.ValueOf(nil), err, false
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
return reflect.ValueOf([]byte(v.String())), nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.ValueOf(nil), nil, false
|
||||||
|
}
|
||||||
|
|
||||||
// rpc.http
|
// rpc.http
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// rpc.websocket
|
// rpc.websocket
|
||||||
@ -264,7 +313,7 @@ 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 {
|
||||||
QuitService
|
BaseService
|
||||||
|
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
baseConn *websocket.Conn
|
baseConn *websocket.Conn
|
||||||
@ -285,13 +334,13 @@ func NewWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, evsw
|
|||||||
funcMap: funcMap,
|
funcMap: funcMap,
|
||||||
evsw: evsw,
|
evsw: evsw,
|
||||||
}
|
}
|
||||||
wsc.QuitService = *NewQuitService(log, "wsConnection", wsc)
|
wsc.BaseService = *NewBaseService(log, "wsConnection", wsc)
|
||||||
return wsc
|
return wsc
|
||||||
}
|
}
|
||||||
|
|
||||||
// wsc.Start() blocks until the connection closes.
|
// wsc.Start() blocks until the connection closes.
|
||||||
func (wsc *wsConnection) OnStart() error {
|
func (wsc *wsConnection) OnStart() error {
|
||||||
wsc.QuitService.OnStart()
|
wsc.BaseService.OnStart()
|
||||||
|
|
||||||
// Read subscriptions/unsubscriptions to events
|
// Read subscriptions/unsubscriptions to events
|
||||||
go wsc.readRoutine()
|
go wsc.readRoutine()
|
||||||
@ -318,7 +367,7 @@ func (wsc *wsConnection) OnStart() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wsc *wsConnection) OnStop() {
|
func (wsc *wsConnection) OnStop() {
|
||||||
wsc.QuitService.OnStop()
|
wsc.BaseService.OnStop()
|
||||||
wsc.evsw.RemoveListener(wsc.remoteAddr)
|
wsc.evsw.RemoveListener(wsc.remoteAddr)
|
||||||
wsc.readTimeout.Stop()
|
wsc.readTimeout.Stop()
|
||||||
wsc.pingTicker.Stop()
|
wsc.pingTicker.Stop()
|
||||||
|
60
test/test.sh
Normal file → Executable file
60
test/test.sh
Normal file → Executable file
@ -1,4 +1,16 @@
|
|||||||
#! /bin/bash
|
#! /bin/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
|
||||||
|
|
||||||
go build -o server main.go
|
go build -o server main.go
|
||||||
@ -6,18 +18,52 @@ go build -o server main.go
|
|||||||
PID=$!
|
PID=$!
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
# 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`
|
||||||
|
|
||||||
kill -9 $PID
|
|
||||||
|
|
||||||
if [[ "$R1" != "$R2" ]]; then
|
if [[ "$R1" != "$R2" ]]; then
|
||||||
echo "responses are not identical:"
|
echo "responses are not identical:"
|
||||||
echo "R1: $R1"
|
echo "R1: $R1"
|
||||||
echo "R2: $R2"
|
echo "R2: $R2"
|
||||||
exit 1
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Success"
|
||||||
fi
|
fi
|
||||||
echo "Success"
|
|
||||||
|
# request with 0x-prefixed hex string arg
|
||||||
|
R1=`curl -s 'http://localhost:8008/hello_world?name=0x41424344&num=123'`
|
||||||
|
R2='{"jsonrpc":"2.0","id":"","result":{"Result":"hi ABCD 123"},"error":""}'
|
||||||
|
if [[ "$R1" != "$R2" ]]; then
|
||||||
|
echo "responses are not identical:"
|
||||||
|
echo "R1: $R1"
|
||||||
|
echo "R2: $R2"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Success"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# request with unquoted string arg
|
||||||
|
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\"}"
|
||||||
|
if [[ "$R1" != "$R2" ]]; then
|
||||||
|
echo "responses are not identical:"
|
||||||
|
echo "R1: $R1"
|
||||||
|
echo "R2: $R2"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Success"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# request with string type when expecting number arg
|
||||||
|
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'\"}"
|
||||||
|
if [[ "$R1" != "$R2" ]]; then
|
||||||
|
echo "responses are not identical:"
|
||||||
|
echo "R1: $R1"
|
||||||
|
echo "R2: $R2"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Success"
|
||||||
|
fi
|
||||||
|
|
||||||
|
kill -9 $PID || exit 0
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
const Maj = "0"
|
const Maj = "0"
|
||||||
const Min = "5" // refactored out of tendermint/tendermint; RPCResponse.Result is RawJSON
|
const Min = "6" // 0x-prefixed string args handled as hex
|
||||||
const Fix = "1" // support tcp:// or unix:// prefixes
|
const Fix = "0" //
|
||||||
|
|
||||||
const Version = Maj + "." + Min + "." + Fix
|
const Version = Maj + "." + Min + "." + Fix
|
||||||
|
Loading…
x
Reference in New Issue
Block a user