From 34a806578ac0dd578c5595365a9c1478e0a689a0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 2 Jan 2017 09:50:20 -0800 Subject: [PATCH] Handle hex strings and quoted strings in HTTP params Use 0x-prefixed hex strings in client server: Decode hex string args Encode all string args as 0x without trying to encode as JSON Added tests for special string arguments Fix server handling quoted string args Added string arg handling test cases to bash test script --- client/http_client.go | 19 ++++++++++++++++++- rpc_test.go | 36 ++++++++++++++++++++++++++++++++++++ server/handlers.go | 31 ++++++++++++++++++++++++++++++- test/test.sh | 31 +++++++++++++++++++++++++------ 4 files changed, 109 insertions(+), 8 deletions(-) mode change 100644 => 100755 test/test.sh diff --git a/client/http_client.go b/client/http_client.go index 816791b5c..54fbd1c2b 100644 --- a/client/http_client.go +++ b/client/http_client.go @@ -4,10 +4,12 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "io/ioutil" "net" "net/http" "net/url" + "reflect" "strings" . "github.com/tendermint/go-common" @@ -119,7 +121,7 @@ func (c *ClientURI) call(method string, params map[string]interface{}, result in if err != nil { 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) if err != nil { return nil, err @@ -176,6 +178,21 @@ func argsToJson(args map[string]interface{}) error { var n int var err error for k, v := range args { + // Convert strings to "0x"-prefixed hex + str, isString := reflect.ValueOf(v).Interface().(string) + if isString { + args[k] = fmt.Sprintf("0x%X", str) + continue + } + + // 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) wire.WriteJSON(v, buf, &n, &err) if err != nil { diff --git a/rpc_test.go b/rpc_test.go index 0c6e1dbdb..1fcda0e5e 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -164,3 +164,39 @@ func TestWS_UNIX(t *testing.T) { } 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) + } +} diff --git a/server/handlers.go b/server/handlers.go index dbc5c6e75..f5730213b 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -2,6 +2,7 @@ package rpcserver import ( "bytes" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -229,7 +230,35 @@ func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error for i, name := range argNames { ty := argTypes[i] 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) + + // Handle quoted strings + if strings.HasPrefix(arg, "\"") && strings.HasSuffix(arg, "\"") { + data := arg[1 : len(arg)-1] + if ty.Kind() == reflect.String { + values[i] = reflect.ValueOf(string(data)) + } else { + values[i] = reflect.ValueOf(data) + } + continue + } + + // Handle hex strings + if strings.HasPrefix(strings.ToLower(arg), "0x") { + var value []byte + value, err = hex.DecodeString(arg[2:]) + if err != nil { + return nil, err + } + if ty.Kind() == reflect.String { + values[i] = reflect.ValueOf(string(value)) + } else { + values[i] = reflect.ValueOf(value) + } + continue + } + + // Pass values to go-wire values[i], err = _jsonStringToArg(ty, arg) if err != nil { return nil, err diff --git a/test/test.sh b/test/test.sh old mode 100644 new mode 100755 index 3d1f599dd..a5f8abd44 --- a/test/test.sh +++ b/test/test.sh @@ -6,18 +6,37 @@ go build -o server main.go PID=$! sleep 2 - +# simple JSONRPC request R1=`curl -s 'http://localhost:8008/hello_world?name="my_world"&num=5'` - - R2=`curl -s --data @data.json http://localhost:8008` +if [[ "$R1" != "$R2" ]]; then + echo "responses are not identical:" + echo "R1: $R1" + echo "R2: $R2" +else + echo "Success" +fi -kill -9 $PID +# 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" +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 -echo "Success" + +kill -9 $PID