Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc: support for named parameters in client #22656

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion rpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,22 @@ func (c *Client) SetHeader(key, value string) {
conn.mu.Unlock()
}

// NewNamedParams is a qucick way to create NamedParams.
func NewNamedParams(argsObject interface{}) NamedParams {
return NamedParams{Value: argsObject}
}

// NamedParams wraps a struct or map value to provide RPC params as an object.
type NamedParams struct {
Value interface{}
}

// Call performs a JSON-RPC call with the given arguments and unmarshals into
// result if no error occurred.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) Call(result interface{}, method string, args ...interface{}) error {
Expand All @@ -287,6 +300,9 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er
// CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
Expand Down Expand Up @@ -393,6 +409,10 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
}

// Notify sends a notification, i.e. a method call that doesn't expect a response.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
//
func (c *Client) Notify(ctx context.Context, method string, args ...interface{}) error {
op := new(requestOp)
msg, err := c.newMessage(method, args...)
Expand All @@ -408,11 +428,18 @@ func (c *Client) Notify(ctx context.Context, method string, args ...interface{})
}

// EthSubscribe registers a subscription under the "eth" namespace.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "eth", channel, args...)
}

// ShhSubscribe registers a subscription under the "shh" namespace.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
//
// Deprecated: use Subscribe(ctx, "shh", ...).
func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "shh", channel, args...)
Expand All @@ -423,6 +450,9 @@ func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...
// sent to the given channel. The element type of the channel must match the
// expected type of content returned by the subscription.
//
// Args are encoded into the RPC request as ordered array params by default,
// but can be encoded as a named params object by passing a single NamedParams arg.
//
// The context argument cancels the RPC request that sets up the subscription but has no
// effect on the subscription after Subscribe has returned.
//
Expand Down Expand Up @@ -464,8 +494,12 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
return op.sub, nil
}

func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) {
func (c *Client) newMessage(method string, args ...interface{}) (*jsonrpcMessage, error) {
msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method}
var paramsIn interface{} = args
if len(args) == 1 && reflect.TypeOf(args[0]) == reflect.TypeOf(NamedParams{}) {
paramsIn = args[0].(NamedParams).Value
}
if paramsIn != nil { // prevent sending "params":null
var err error
if msg.Params, err = json.Marshal(paramsIn); err != nil {
Expand Down
32 changes: 32 additions & 0 deletions rpc/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,38 @@ func TestClientRequest(t *testing.T) {
}
}

func TestClientNamedParams(t *testing.T) {
client := Client{}

type params struct {
Str string
Int int
Args *echoArgs
}

p := params{
Str: "hello",
Int: 10,
Args: &echoArgs{"world"},
}

method := "test_echo"
msg, err := client.newMessage(method, NewNamedParams(p))
if err != nil {
t.Fatal(err)
}
if msg.Method != method {
t.Fatalf("expect method %s but got %s", method, msg.Method)
}
var msgParams params
if err := json.Unmarshal(msg.Params, &msgParams); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(msgParams, p) {
t.Errorf("incorrect params %#v", msgParams)
}
}

Comment on lines +54 to +85
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal of this test is to ensure that request params are correctly encoded when NamedParams are passed as the sole argument. Testing named params with an actual server would require that the server supports named params, which I don't think this package's server implementation does. Changing that would be beyond the scope of this PR since the goal here is just to allow the client to be able to communicate with servers that do support named params.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understand.

func TestClientResponseType(t *testing.T) {
server := newTestServer()
defer server.Stop()
Expand Down