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

Conversation

asutula
Copy link

@asutula asutula commented Apr 12, 2021

Hey turns out this repo contains the best looking JSON RPC 2 library out there. I was trying to use its client code against a server that expects params to be an object with member names that match what the server expects (this is supported by the JSON RPC 2 spec), but it seems that params is always encoded as an array. This PR changes that, allowing the caller to specify params as an array or struct/map. This would make the rpc package more generally usable... Not sure if that aligns with the goals of this code base (or maybe it could be it's own code base), but I know the larger community of Go developers would appreciate it.

rpc/client.go Outdated
Comment on lines 281 to 285
// CallArgsOpt is created by calling PositionalArgs, NamesArgs or NoArgs
// and controls the encoding of arguments in the RPC request.
type CallArgsOpt func(*callArgs)

// PositionalArgs is used to provide args in the server-expected order.
// They will be encoded as an array.
func PositionalArgs(args ...interface{}) CallArgsOpt {
return func(ca *callArgs) {
ca.list = args
}
}

// NamedArgs is used to provide args as a struct or map
// with member names that match the server-expected arg names.
// They will be encoded as an object.
func NamedArgs(arg interface{}) CallArgsOpt {
return func(ca *callArgs) {
ca.object = arg
}
}

// NoArgs is used to specify that there are no args.
func NoArgs() CallArgsOpt {
return func(ca *callArgs) {}
}

Copy link
Author

Choose a reason for hiding this comment

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

Here are the new "args options" to choose from. The two funcs below are updated to accept args in this way, and basically the rest of the changes in this PR are just refactoring to accommodate that change.

Result: new(int),
Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"},
},
}
for i := range batch {
batch[i].ArgsOpt = nil
}
Copy link
Author

@asutula asutula Apr 12, 2021

Choose a reason for hiding this comment

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

Tough decision here... was trying to understand the extent of what we're really trying to test, and decided that dropping the deep equal check for ArgsOpt was the easiest but still safe-ish way to go. Something like this is necessary because ArgsOpt is a func and non-nil funcs are never deeply equal. There would be other possibilities for testing this but they would require larger changes to this test, so I just stuck with simple for now.

@fjl
Copy link
Contributor

fjl commented Apr 13, 2021

We have discussed this today, and the question has come up a couple times before. For Ethereum itself, named args are not used and would not add much value. We would also need to standardize the arg names for the API, which is not done right now. However, I understand that the go-ethereum RPC implementation is generally useful even if you don't care about Ethereum at all, and in other APIs named args are more useful.

The way you have implemented it here is not great because it is a backwards-incompatible change and requires writing rpc.PositionalArgs every time you want to make a call. We could integrate support for named parameters in the client by definining a special, internally-recognized type NamedArgs, and change the client to use named parameters if len(args) == 1 && reflect.TypeOf(args[0]) == reflect.TypeOf(NamedArgs{}).

@fjl fjl removed the status:triage label Apr 13, 2021
@asutula asutula closed this Apr 13, 2021
Signed-off-by: Aaron Sutula <hi@asutula.com>
@asutula asutula reopened this Apr 13, 2021
@asutula
Copy link
Author

asutula commented Apr 13, 2021

Yea that makes sense. I took a stab at that here, let me know if that is what you're thinking.

Comment on lines +54 to +85
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)
}
}

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.

@fjl fjl changed the title RPC Object Param Support rpc: support for named parameters in client Apr 13, 2021
@fjl fjl self-assigned this Apr 13, 2021
@fjl fjl added this to the 1.10.4 milestone May 11, 2021
@fjl fjl modified the milestones: 1.10.4, 1.10.5 Jun 17, 2021
@karalabe karalabe modified the milestones: 1.10.5, 1.10.6 Jul 14, 2021
@fjl fjl modified the milestones: 1.10.6, 1.10.7 Jul 27, 2021
@karalabe karalabe modified the milestones: 1.10.7, 1.10.8 Aug 10, 2021
@fjl fjl modified the milestones: 1.10.8, 1.10.9 Aug 24, 2021
@fjl fjl modified the milestones: 1.10.9, 1.10.10 Sep 29, 2021
@fjl fjl removed this from the 1.10.10 milestone Oct 13, 2021
@fjl fjl added this to the 1.10.11 milestone Oct 13, 2021
@holiman holiman modified the milestones: 1.10.11, 1.10.12 Oct 20, 2021
@karalabe karalabe modified the milestones: 1.10.12, 1.10.13 Nov 8, 2021
@holiman holiman modified the milestones: 1.10.13, 1.10.14 Nov 24, 2021
@fjl fjl removed this from the 1.10.14 milestone Dec 23, 2021
Signed-off-by: Aaron Sutula <asutula@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants