Skip to content

Commit ef506b1

Browse files
author
Aryan Tikarya
committed
refactor: json rpc error and error codec interface
1 parent 65441ad commit ef506b1

File tree

7 files changed

+267
-183
lines changed

7 files changed

+267
-183
lines changed

client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ type clientResponse struct {
7171
Jsonrpc string `json:"jsonrpc"`
7272
Result json.RawMessage `json:"result"`
7373
ID interface{} `json:"id"`
74-
Error *respError `json:"error,omitempty"`
74+
Error *JSONRPCError `json:"error,omitempty"`
7575
}
7676

7777
type makeChanSink func() (context.Context, func([]byte, bool))

errors.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ type marshalable interface {
5959
json.Unmarshaler
6060
}
6161

62-
// ErrorWithData contains extra data to explain the error
63-
type ErrorWithData interface {
64-
Error() string // returns the message
65-
ErrorData() interface{} // returns the error data
62+
type ErrorCodec interface {
63+
FromJSONRPCError(JSONRPCError) error
64+
ToJSONRPCError() (JSONRPCError, error)
6665
}

handler.go

Lines changed: 20 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"context"
66
"encoding/base64"
77
"encoding/json"
8-
"errors"
98
"fmt"
109
"io"
1110
"reflect"
@@ -66,95 +65,6 @@ type request struct {
6665
// Configured by WithMaxRequestSize.
6766
const DEFAULT_MAX_REQUEST_SIZE = 100 << 20 // 100 MiB
6867

69-
type respError struct {
70-
Code ErrorCode `json:"code"`
71-
Message string `json:"message"`
72-
Meta json.RawMessage `json:"meta,omitempty"`
73-
Data interface{} `json:"data,omitempty"`
74-
}
75-
76-
func (e *respError) Error() string {
77-
if e.Code >= -32768 && e.Code <= -32000 {
78-
return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message)
79-
}
80-
return e.Message
81-
}
82-
83-
func (e *respError) ErrorData() interface{} {
84-
return e.Data
85-
}
86-
87-
var (
88-
marshalableRT = reflect.TypeOf(new(marshalable)).Elem()
89-
errorsRT = reflect.TypeOf(new(ErrorWithData)).Elem()
90-
)
91-
92-
func (e *respError) val(errors *Errors) reflect.Value {
93-
if errors != nil {
94-
t, ok := errors.byCode[e.Code]
95-
if ok {
96-
var v reflect.Value
97-
if t.Kind() == reflect.Ptr {
98-
v = reflect.New(t.Elem())
99-
} else {
100-
v = reflect.New(t)
101-
}
102-
103-
if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
104-
_ = v.Interface().(marshalable).UnmarshalJSON(e.Meta)
105-
}
106-
107-
msgField := v.Elem().FieldByName("Message")
108-
if msgField.IsValid() && msgField.CanSet() && msgField.Kind() == reflect.String {
109-
msgField.SetString(e.Message)
110-
}
111-
112-
if v.Type().Implements(errorsRT) {
113-
dataField := v.Elem().FieldByName("Data")
114-
if dataField.IsValid() && dataField.CanSet() {
115-
dataField.Set(reflect.ValueOf(e.Data))
116-
}
117-
}
118-
119-
if t.Kind() != reflect.Ptr {
120-
v = v.Elem()
121-
}
122-
return v
123-
}
124-
}
125-
126-
return reflect.ValueOf(e)
127-
}
128-
129-
type response struct {
130-
Jsonrpc string `json:"jsonrpc"`
131-
Result interface{} `json:"result,omitempty"`
132-
ID interface{} `json:"id"`
133-
Error *respError `json:"error,omitempty"`
134-
}
135-
136-
func (r response) MarshalJSON() ([]byte, error) {
137-
// Custom marshal logic as per JSON-RPC 2.0 spec:
138-
// > `result`:
139-
// > This member is REQUIRED on success.
140-
// > This member MUST NOT exist if there was an error invoking the method.
141-
//
142-
// > `error`:
143-
// > This member is REQUIRED on error.
144-
// > This member MUST NOT exist if there was no error triggered during invocation.
145-
data := map[string]interface{}{
146-
"jsonrpc": r.Jsonrpc,
147-
"id": r.ID,
148-
}
149-
150-
if r.Error != nil {
151-
data["error"] = r.Error
152-
} else {
153-
data["result"] = r.Result
154-
}
155-
return json.Marshal(data)
156-
}
157-
15868
type handler struct {
15969
methods map[string]methodHandler
16070
errors *Errors
@@ -359,7 +269,7 @@ func (s *handler) getSpan(ctx context.Context, req request) (context.Context, *t
359269
return ctx, span
360270
}
361271

362-
func (s *handler) createError(err error) *respError {
272+
func (s *handler) createError(err error) *JSONRPCError {
363273
var code ErrorCode = 1
364274
if s.errors != nil {
365275
c, ok := s.errors.byType[reflect.TypeOf(err)]
@@ -368,12 +278,20 @@ func (s *handler) createError(err error) *respError {
368278
}
369279
}
370280

371-
out := &respError{
281+
out := &JSONRPCError{
372282
Code: code,
373283
Message: err.Error(),
374284
}
375285

376-
if m, ok := err.(marshalable); ok {
286+
switch m := err.(type) {
287+
case ErrorCodec:
288+
o, err := m.ToJSONRPCError()
289+
if err != nil {
290+
log.Warnf("Failed to convert error to JSONRPCError: %v", err)
291+
} else {
292+
out = &o
293+
}
294+
case marshalable:
377295
meta, marshalErr := m.MarshalJSON()
378296
if marshalErr == nil {
379297
out.Meta = meta
@@ -382,11 +300,6 @@ func (s *handler) createError(err error) *respError {
382300
}
383301
}
384302

385-
var ed ErrorWithData
386-
if errors.As(err, &ed) {
387-
out.Data = ed.ErrorData()
388-
}
389-
390303
return out
391304
}
392305

@@ -536,14 +449,19 @@ func (s *handler) handle(ctx context.Context, req request, w func(func(io.Writer
536449

537450
log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
538451
stats.Record(ctx, metrics.RPCResponseError.M(1))
539-
respErr := &respError{
452+
453+
respErr := &JSONRPCError{
540454
Code: 1,
541455
Message: err.Error(),
542456
}
543457

544-
var ed ErrorWithData
545-
if errors.As(err, &ed) {
546-
respErr.Data = ed.ErrorData()
458+
if m, ok := err.(ErrorCodec); ok {
459+
respErr, err := m.ToJSONRPCError()
460+
if err != nil {
461+
log.Warnf("Failed to convert error to JSONRPCError: %v", err)
462+
} else {
463+
resp.Error.Data = respErr.Data
464+
}
547465
}
548466

549467
resp.Error = respErr

0 commit comments

Comments
 (0)