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:
parent
bd2c3c6754
commit
9eee21f1ca
|
@ -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, ¶ms)
|
||||
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
|
||||
|
|
|
@ -55,6 +55,7 @@ func TestMethodAndParamsFromBody(t *testing.T) {
|
|||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{}{}
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue