rpc, signer: implement jsonrpc2.0 notifications (responseless)

This commit is contained in:
Martin Holst Swende 2018-11-10 16:03:40 +01:00
parent 1ff152f3a4
commit 8540a7f088
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
3 changed files with 56 additions and 5 deletions

View file

@ -245,6 +245,28 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er
return c.CallContext(ctx, result, method, args...) return c.CallContext(ctx, result, method, args...)
} }
// Notify sends a JSON-RPC notification with the given arguments, and does not wait for a response,
// effectively ignoring any responses sent
//
// https://www.jsonrpc.org/specification#request_object:
// A Notification is a Request object without an "id" member.
// A Request object that is a Notification signifies the Client's lack of interest in the
// corresponding Response object, and as such no Response object needs to be returned to the client.
// The Server MUST NOT reply to a Notification, including those that are within a batch request.
func (c *Client) Notify(method string, args ...interface{}) error {
ctx := context.Background()
msg, err := c.newNotification(method, args...)
if err != nil {
return err
}
if c.isHTTP {
err = c.sendHTTP(ctx, nil, msg)
} else {
err = c.send(ctx, nil, msg)
}
return err
}
// CallContext performs a JSON-RPC call with the given arguments. If the context is // CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately. // canceled before the call has successfully returned, CallContext returns immediately.
// //
@ -416,9 +438,23 @@ func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMes
return &jsonrpcMessage{Version: "2.0", ID: c.nextID(), Method: method, Params: params}, nil return &jsonrpcMessage{Version: "2.0", ID: c.nextID(), Method: method, Params: params}, nil
} }
func (c *Client) newNotification(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) {
params, err := json.Marshal(paramsIn)
if err != nil {
return nil, err
}
return &jsonrpcMessage{Version: "2.0", Method: method, Params: params}, nil
}
// send registers op with the dispatch loop, then sends msg on the connection. // send registers op with the dispatch loop, then sends msg on the connection.
// if sending fails, op is deregistered. // if sending fails, op is deregistered.
func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error {
if jsonrequest, ok := msg.(jsonrpcMessage); ok {
if jsonrequest.isNotification() {
// If we send a json-rpc notification, we expect no response
return c.write(ctx, msg)
}
}
select { select {
case c.requestOp <- op: case c.requestOp <- op:
log.Trace("", "msg", log.Lazy{Fn: func() string { log.Trace("", "msg", log.Lazy{Fn: func() string {

View file

@ -136,6 +136,12 @@ func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) e
} }
return err return err
} }
if jsonrequest, ok := msg.(jsonrpcMessage); ok {
if jsonrequest.isNotification() {
// If we send a json-rpc notification, we expect no response
return nil
}
}
var respmsg jsonrpcMessage var respmsg jsonrpcMessage
if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil {
return err return err

View file

@ -49,6 +49,15 @@ func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interf
return err return err
} }
// notify sends a request over the stdio, and does not listen for a response
func (ui *StdIOUI) notify(serviceMethod string, args interface{}) error {
err := ui.client.Notify(serviceMethod, args)
if err != nil {
log.Info("Error", "exc", err.Error())
}
return err
}
func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
var result SignTxResponse var result SignTxResponse
err := ui.dispatch("ApproveTx", request, &result) err := ui.dispatch("ApproveTx", request, &result)
@ -86,34 +95,34 @@ func (ui *StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResp
} }
func (ui *StdIOUI) ShowError(message string) { func (ui *StdIOUI) ShowError(message string) {
err := ui.dispatch("ShowError", &Message{message}, nil) err := ui.notify("ShowError", &Message{message})
if err != nil { if err != nil {
log.Info("Error calling 'ShowError'", "exc", err.Error(), "msg", message) log.Info("Error calling 'ShowError'", "exc", err.Error(), "msg", message)
} }
} }
func (ui *StdIOUI) ShowInfo(message string) { func (ui *StdIOUI) ShowInfo(message string) {
err := ui.dispatch("ShowInfo", Message{message}, nil) err := ui.notify("ShowInfo", Message{message})
if err != nil { if err != nil {
log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message) log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
} }
} }
func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) { func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
err := ui.dispatch("OnApprovedTx", tx, nil) err := ui.notify("OnApprovedTx", tx)
if err != nil { if err != nil {
log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx) log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
} }
} }
func (ui *StdIOUI) OnSignerStartup(info StartupInfo) { func (ui *StdIOUI) OnSignerStartup(info StartupInfo) {
err := ui.dispatch("OnSignerStartup", info, nil) err := ui.notify("OnSignerStartup", info)
if err != nil { if err != nil {
log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info) log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info)
} }
} }
func (ui *StdIOUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) { func (ui *StdIOUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
var result UserInputResponse var result UserInputResponse
err := ui.dispatch("OnInputRequired", info, &result) err := ui.notify("OnInputRequired", info)
if err != nil { if err != nil {
log.Info("Error calling 'OnInputRequired'", "exc", err.Error(), "info", info) log.Info("Error calling 'OnInputRequired'", "exc", err.Error(), "info", info)
} }