Add support for JSON-RPC array payload. (#335)

This PR introduces solution for #333 - it adds support for array JSON-RPC payload.

unmarshalMessage tries to unmarshal JSON paylod into *jsonrpcMessage object, and in case of failure, analyzes error and, if it's unmarshalling array error, tries to unmarshal it as an array.
This commit is contained in:
Ivan Daniluk 2017-09-17 16:06:18 +02:00 committed by Ivan Tomilov
parent bd2c3c6754
commit 9eee21f1ca
3 changed files with 72 additions and 11 deletions

View File

@ -3,6 +3,7 @@ package rpc
import (
"context"
"encoding/json"
"errors"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/geth/log"
@ -96,7 +97,6 @@ func methodAndParamsFromBody(body string) (string, []interface{}, json.RawMessag
if msg.Params != nil {
err = json.Unmarshal(msg.Params, &params)
if err != nil {
log.Error("unmarshal params", "error", err)
return "", nil, nil, err
}
}
@ -104,12 +104,44 @@ func methodAndParamsFromBody(body string) (string, []interface{}, json.RawMessag
return msg.Method, params, msg.ID, nil
}
// unmarshalMessage tries to unmarshal JSON-RPC message.
// somehow JSON-RPC input from web3.js can be in two forms:
//
// object: {"jsonrpc":"2.0", …}
// array: [{"jsonrpc":"2.0", …}]
//
// unmarhsalMessage tries first option and in case of error,
// tries to unmarshal it as an array.
//
// TODO(divan): fix the source of this error and cleanup.
func unmarshalMessage(body string) (*jsonrpcMessage, error) {
var msg jsonrpcMessage
err := json.Unmarshal([]byte(body), &msg)
// check for array case
if e, ok := err.(*json.UnmarshalTypeError); ok {
if e.Value == "array" {
return unmarshalMessageArray(body)
}
}
return &msg, err
}
func unmarshalMessageArray(body string) (*jsonrpcMessage, error) {
var msgs []*jsonrpcMessage
err := json.Unmarshal([]byte(body), &msgs)
if err != nil {
return nil, err
}
// return first element
if len(msgs) == 0 {
return nil, errors.New("empty array")
} else if len(msgs) > 1 {
log.Warn("JSON-RPC payload has more then 1 objects", "len", len(msgs), "body", body)
}
return msgs[0], nil
}
func newSuccessResponse(result json.RawMessage, id json.RawMessage) string {
if id == nil {
id = defaultMsgID

View File

@ -50,11 +50,12 @@ func TestUnmarshalMessage(t *testing.T) {
func TestMethodAndParamsFromBody(t *testing.T) {
cases := []struct {
name string
body string
params []interface{}
method string
id json.RawMessage
name string
body string
params []interface{}
method string
id json.RawMessage
shouldFail bool
}{
{
"params_array",
@ -67,30 +68,60 @@ func TestMethodAndParamsFromBody(t *testing.T) {
},
"subtract",
json.RawMessage(`42`),
false,
},
{
"params_empty_array",
`{"jsonrpc": "2.0", "method": "test", "params": []}`,
[]interface{}{},
"test",
json.RawMessage(nil),
nil,
false,
},
{
"params_none",
`{"jsonrpc": "2.0", "method": "test"}`,
[]interface{}{},
"test",
json.RawMessage(nil),
nil,
false,
},
{
"getFilterMessage",
`{"jsonrpc":"2.0","id":44,"method":"shh_getFilterMessages","params":["3de6a8867aeb75be74d68478b853b4b0e063704d30f8231c45d0fcbd97af207e"]}`,
[]interface{}{string("3de6a8867aeb75be74d68478b853b4b0e063704d30f8231c45d0fcbd97af207e")},
"shh_getFilterMessages",
json.RawMessage(`44`),
false,
},
{
"getFilterMessage_array",
`[{"jsonrpc":"2.0","id":44,"method":"shh_getFilterMessages","params":["3de6a8867aeb75be74d68478b853b4b0e063704d30f8231c45d0fcbd97af207e"]}]`,
[]interface{}{string("3de6a8867aeb75be74d68478b853b4b0e063704d30f8231c45d0fcbd97af207e")},
"shh_getFilterMessages",
json.RawMessage(`44`),
false,
},
{
"empty_array",
`[]`,
[]interface{}{},
"",
nil,
true,
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
method, params, id, err := methodAndParamsFromBody(test.body)
if test.shouldFail {
require.Error(t, err)
}
require.NoError(t, err)
require.Equal(t, test.method, method)
require.Equal(t, test.params, params)
require.Equal(t, test.id, id)
require.EqualValues(t, test.id, id)
})
}
}

View File

@ -6,7 +6,6 @@ import (
"net/http/httptest"
"testing"
"github.com/status-im/status-go/geth/log"
"github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/rpc"
@ -183,7 +182,6 @@ func (s *RPCTestSuite) TestCallRPC() {
"id": 1
}`,
func(resultJSON string) {
log.Info("eth_sendTransaction")
progress <- struct{}{}
},
},