rpc: pre-encode JSON-RPC error responses at creation time

This commit is contained in:
jonny rhea 2026-03-10 13:02:45 -05:00
parent d620f9f96a
commit 4a6e13623a
4 changed files with 31 additions and 25 deletions

View file

@ -364,7 +364,7 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str
resp := batchresp[0]
switch {
case resp.Error != nil:
return resp.Error
return resp.jsonErr()
case len(resp.Result) == 0:
return ErrNoResult
default:
@ -449,7 +449,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
elem := &b[index]
switch {
case resp.Error != nil:
elem.Error = resp.Error
elem.Error = resp.jsonErr()
case resp.Result == nil:
elem.Error = ErrNoResult
default:

View file

@ -237,7 +237,7 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
resp := h.handleCallMsg(cp, msg)
callBuffer.pushResponse(resp)
if resp != nil && h.batchResponseMaxSize != 0 {
responseBytes += len(resp.Result)
responseBytes += len(resp.Result) + len(resp.Error)
if responseBytes > h.batchResponseMaxSize {
err := &internalServerError{errcodeResponseTooLarge, errMsgResponseTooLarge}
callBuffer.respondWithError(cp.ctx, h.conn, err)
@ -415,7 +415,7 @@ func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*json
// the op.resp channel.
if op.sub != nil {
if msg.Error != nil {
op.err = msg.Error
op.err = msg.jsonErr()
} else {
op.err = json.Unmarshal(msg.Result, &op.sub.subid)
if op.err == nil {
@ -481,9 +481,10 @@ func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMess
var logctx []any
logctx = append(logctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start))
if resp.Error != nil {
logctx = append(logctx, "err", resp.Error.Message)
if resp.Error.Data != nil {
logctx = append(logctx, "errdata", formatErrorData(resp.Error.Data))
je := resp.jsonErr()
logctx = append(logctx, "err", je.Message)
if je.Data != nil {
logctx = append(logctx, "errdata", formatErrorData(je.Data))
}
h.log.Warn("Served "+msg.Method, logctx...)
} else {
@ -550,7 +551,7 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
answer := h.runMethod(rctx, msg, callb, args)
var rErr error
if answer.Error != nil {
rErr = errors.New(answer.Error.Message)
rErr = errors.New(answer.jsonErr().Message)
}
rSpanEnd(&rErr)
@ -623,7 +624,7 @@ func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *cal
_, _, spanEnd := telemetry.StartSpanWithTracer(ctx, h.tracer(), "rpc.encodeJSONResponse", attributes...)
response := msg.response(result)
if response.Error != nil {
err = errors.New(response.Error.Message)
err = errors.New(response.jsonErr().Message)
}
spanEnd(&err)
return response

View file

@ -60,7 +60,7 @@ type jsonrpcMessage struct {
ID json.RawMessage `json:"id,omitempty"`
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Error *jsonError `json:"error,omitempty"`
Error json.RawMessage `json:"error,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
}
@ -108,6 +108,16 @@ func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage {
return resp
}
// jsonErr decodes the Error field into a jsonError struct.
func (msg *jsonrpcMessage) jsonErr() *jsonError {
if msg.Error == nil {
return nil
}
je := new(jsonError)
json.Unmarshal(msg.Error, je)
return je
}
func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage {
var (
enc []byte
@ -125,19 +135,18 @@ func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage {
}
func errorMessage(err error) *jsonrpcMessage {
msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{
je := &jsonError{
Code: errcodeDefault,
Message: err.Error(),
}}
ec, ok := err.(Error)
if ok {
msg.Error.Code = ec.ErrorCode()
}
de, ok := err.(DataError)
if ok {
msg.Error.Data = de.ErrorData()
if ec, ok := err.(Error); ok {
je.Code = ec.ErrorCode()
}
return msg
if de, ok := err.(DataError); ok {
je.Data = de.ErrorData()
}
enc, _ := json.Marshal(je)
return &jsonrpcMessage{Version: vsn, ID: null, Error: enc}
}
type jsonError struct {
@ -248,12 +257,8 @@ func writeMessage(w io.Writer, msg *jsonrpcMessage) error {
buf = append(buf, msg.Params...)
}
if msg.Error != nil {
errBytes, err := json.Marshal(msg.Error)
if err != nil {
return err
}
buf = append(buf, `,"error":`...)
buf = append(buf, errBytes...)
buf = append(buf, msg.Error...)
}
if msg.Result != nil {
buf = append(buf, `,"result":`...)

View file

@ -220,7 +220,7 @@ func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionRe
case msg.isResponse():
var c subConfirmation
if msg.Error != nil {
return nil, nil, msg.Error
return nil, nil, msg.jsonErr()
} else if err := json.Unmarshal(msg.Result, &c.subid); err != nil {
return nil, nil, fmt.Errorf("invalid response: %v", err)
} else {