Skip to content

Commit

Permalink
feat: add optional data field to error returns (#123)
Browse files Browse the repository at this point in the history
Co-authored-by: Aryan Tikarya <aryan.tikarya@dojima.network>
  • Loading branch information
virajbhartiya and akaladarshi authored Oct 24, 2024
1 parent c185272 commit a9fac20
Show file tree
Hide file tree
Showing 7 changed files with 424 additions and 78 deletions.
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type clientResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result json.RawMessage `json:"result"`
ID interface{} `json:"id"`
Error *respError `json:"error,omitempty"`
Error *JSONRPCError `json:"error,omitempty"`
}

type makeChanSink func() (context.Context, func([]byte, bool))
Expand Down
5 changes: 5 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ type marshalable interface {
json.Marshaler
json.Unmarshaler
}

type RPCErrorCodec interface {
FromJSONRPCError(JSONRPCError) error
ToJSONRPCError() (JSONRPCError, error)
}
88 changes: 17 additions & 71 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,71 +65,6 @@ type request struct {
// Configured by WithMaxRequestSize.
const DEFAULT_MAX_REQUEST_SIZE = 100 << 20 // 100 MiB

type respError struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Meta json.RawMessage `json:"meta,omitempty"`
}

func (e *respError) Error() string {
if e.Code >= -32768 && e.Code <= -32000 {
return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message)
}
return e.Message
}

var marshalableRT = reflect.TypeOf(new(marshalable)).Elem()

func (e *respError) val(errors *Errors) reflect.Value {
if errors != nil {
t, ok := errors.byCode[e.Code]
if ok {
var v reflect.Value
if t.Kind() == reflect.Ptr {
v = reflect.New(t.Elem())
} else {
v = reflect.New(t)
}
if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
_ = v.Interface().(marshalable).UnmarshalJSON(e.Meta)
}
if t.Kind() != reflect.Ptr {
v = v.Elem()
}
return v
}
}

return reflect.ValueOf(e)
}

type response struct {
Jsonrpc string
Result interface{}
ID interface{}
Error *respError
}

func (r response) MarshalJSON() ([]byte, error) {
// Custom marshal logic as per JSON-RPC 2.0 spec:
// > `result`:
// > This member is REQUIRED on success.
// > This member MUST NOT exist if there was an error invoking the method.
//
// > `error`:
// > This member is REQUIRED on error.
// > This member MUST NOT exist if there was no error triggered during invocation.
data := make(map[string]interface{})
data["jsonrpc"] = r.Jsonrpc
data["id"] = r.ID
if r.Error != nil {
data["error"] = r.Error
} else {
data["result"] = r.Result
}
return json.Marshal(data)
}

type handler struct {
methods map[string]methodHandler
errors *Errors
Expand Down Expand Up @@ -334,7 +269,7 @@ func (s *handler) getSpan(ctx context.Context, req request) (context.Context, *t
return ctx, span
}

func (s *handler) createError(err error) *respError {
func (s *handler) createError(err error) *JSONRPCError {
var code ErrorCode = 1
if s.errors != nil {
c, ok := s.errors.byType[reflect.TypeOf(err)]
Expand All @@ -343,15 +278,25 @@ func (s *handler) createError(err error) *respError {
}
}

out := &respError{
out := &JSONRPCError{
Code: code,
Message: err.Error(),
}

if m, ok := err.(marshalable); ok {
meta, err := m.MarshalJSON()
if err == nil {
switch m := err.(type) {
case RPCErrorCodec:
o, err := m.ToJSONRPCError()
if err != nil {
log.Errorf("Failed to convert error to JSONRPCError: %w", err)
} else {
out = &o
}
case marshalable:
meta, marshalErr := m.MarshalJSON()
if marshalErr == nil {
out.Meta = meta
} else {
log.Errorf("Failed to marshal error metadata: %w", marshalErr)
}
}

Expand Down Expand Up @@ -504,7 +449,8 @@ func (s *handler) handle(ctx context.Context, req request, w func(func(io.Writer

log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
stats.Record(ctx, metrics.RPCResponseError.M(1))
resp.Error = &respError{

resp.Error = &JSONRPCError{
Code: 1,
Message: err.Error(),
}
Expand Down
Loading

0 comments on commit a9fac20

Please sign in to comment.