Skip to content

Commit 3f4cdfe

Browse files
niczyNicholas Zhaofjl
authored andcommitted
rpc: improve error codes for internal server errors ethereum#25678
This changes the error code returned by the RPC server in certain situations: - handler panic: code -32603 - result marshaling error: code -32603 - attempt to subscribe via HTTP: code -32001 In all of the above cases, the server previously returned the default error code -32000. Co-authored-by: Nicholas Zhao <nicholas.zhao@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com>
1 parent 69323a2 commit 3f4cdfe

File tree

8 files changed

+53
-11
lines changed

8 files changed

+53
-11
lines changed

rpc/client_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,15 @@ func TestClientErrorData(t *testing.T) {
150150
}
151151

152152
// Check code.
153+
// The method handler returns an error value which implements the rpc.Error
154+
// interface, i.e. it has a custom error code. The server returns this error code.
155+
expectedCode := testError{}.ErrorCode()
153156
if e, ok := err.(Error); !ok {
154157
t.Fatalf("client did not return rpc.Error, got %#v", e)
155-
} else if e.ErrorCode() != (testError{}.ErrorCode()) {
156-
t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode())
158+
} else if e.ErrorCode() != expectedCode {
159+
t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), expectedCode)
157160
}
161+
158162
// Check data.
159163
if e, ok := err.(DataError); !ok {
160164
t.Fatalf("client did not return rpc.DataError, got %#v", e)

rpc/errors.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ var (
5454
_ Error = new(invalidRequestError)
5555
_ Error = new(invalidMessageError)
5656
_ Error = new(invalidParamsError)
57+
_ Error = new(internalServerError)
5758
)
5859

59-
const defaultErrorCode = -32000
60+
const (
61+
errcodeDefault = -32000
62+
errcodeNotificationsUnsupported = -32001
63+
errcodePanic = -32603
64+
errcodeMarshalError = -32603
65+
)
6066

6167
type methodNotFoundError struct{ method string }
6268

@@ -101,3 +107,13 @@ type invalidParamsError struct{ message string }
101107
func (e *invalidParamsError) ErrorCode() int { return -32602 }
102108

103109
func (e *invalidParamsError) Error() string { return e.message }
110+
111+
// internalServerError is used for server errors during request processing.
112+
type internalServerError struct {
113+
code int
114+
message string
115+
}
116+
117+
func (e *internalServerError) ErrorCode() int { return e.code }
118+
119+
func (e *internalServerError) Error() string { return e.message }

rpc/handler.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import (
4646
// Now send the request, then wait for the reply to be delivered through handleMsg:
4747
//
4848
// if err := op.wait(...); err != nil {
49-
// h.removeRequestOp(op) // timeout, etc.
49+
// h.removeRequestOp(op) // timeout, etc.
5050
// }
5151
type handler struct {
5252
reg *serviceRegistry
@@ -353,7 +353,10 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
353353
// handleSubscribe processes *_subscribe method calls.
354354
func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
355355
if !h.allowSubscribe {
356-
return msg.errorResponse(ErrNotificationsUnsupported)
356+
return msg.errorResponse(&internalServerError{
357+
code: errcodeNotificationsUnsupported,
358+
message: ErrNotificationsUnsupported.Error(),
359+
})
357360
}
358361

359362
// Subscription method name is first argument.

rpc/json.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,14 @@ func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage {
100100
func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage {
101101
enc, err := json.Marshal(result)
102102
if err != nil {
103-
// TODO: wrap with 'internal server error'
104-
return msg.errorResponse(err)
103+
return msg.errorResponse(&internalServerError{errcodeMarshalError, err.Error()})
105104
}
106105
return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc}
107106
}
108107

109108
func errorMessage(err error) *jsonrpcMessage {
110109
msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{
111-
Code: defaultErrorCode,
110+
Code: errcodeDefault,
112111
Message: err.Error(),
113112
}}
114113
ec, ok := err.(Error)

rpc/server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestServerRegisterName(t *testing.T) {
4545
t.Fatalf("Expected service calc to be registered")
4646
}
4747

48-
wantCallbacks := 11
48+
wantCallbacks := 13
4949
if len(svc.callbacks) != wantCallbacks {
5050
t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
5151
}

rpc/service.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package rpc
1818

1919
import (
2020
"context"
21-
"errors"
2221
"fmt"
2322
"reflect"
2423
"runtime"
@@ -199,7 +198,7 @@ func (c *callback) call(ctx context.Context, method string, args []reflect.Value
199198
buf := make([]byte, size)
200199
buf = buf[:runtime.Stack(buf, false)]
201200
log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf))
202-
errRes = errors.New("method handler crashed")
201+
errRes = &internalServerError{errcodePanic, "method handler crashed"}
203202
}
204203
}()
205204
// Run the callback.

rpc/testdata/internal-error.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// These tests trigger various 'internal error' conditions.
2+
3+
--> {"jsonrpc":"2.0","id":1,"method":"test_marshalError","params": []}
4+
<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"json: error calling MarshalText for type *rpc.MarshalErrObj: marshal error"}}
5+
6+
--> {"jsonrpc":"2.0","id":2,"method":"test_panic","params": []}
7+
<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"method handler crashed"}}

rpc/testservice_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ func (testError) Error() string { return "testError" }
7070
func (testError) ErrorCode() int { return 444 }
7171
func (testError) ErrorData() interface{} { return "testError data" }
7272

73+
type MarshalErrObj struct{}
74+
75+
func (o *MarshalErrObj) MarshalText() ([]byte, error) {
76+
return nil, errors.New("marshal error")
77+
}
78+
7379
func (s *testService) NoArgsRets() {}
7480

7581
func (s *testService) Echo(str string, i int, args *echoArgs) echoResult {
@@ -118,6 +124,14 @@ func (s *testService) ReturnError() error {
118124
return testError{}
119125
}
120126

127+
func (s *testService) MarshalError() *MarshalErrObj {
128+
return &MarshalErrObj{}
129+
}
130+
131+
func (s *testService) Panic() string {
132+
panic("service panic")
133+
}
134+
121135
func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) {
122136
c, ok := ClientFromContext(ctx)
123137
if !ok {

0 commit comments

Comments
 (0)