From f74de4cb8664f915ea8216bd76ad65d7736d4614 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 26 May 2017 17:45:09 +0200 Subject: [PATCH] include optional data field in error object ``` data A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). ``` --- rpc/lib/server/handlers.go | 22 ++++++++--------- rpc/lib/server/http_server.go | 3 ++- rpc/lib/types/types.go | 45 ++++++++++++++++++++--------------- rpc/lib/types/types_test.go | 6 ++--- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index a4df4b84..070072d8 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -110,11 +110,11 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han var request types.RPCRequest err := json.Unmarshal(b, &request) if err != nil { - WriteRPCResponseHTTP(w, types.RPCParseError("")) + WriteRPCResponseHTTP(w, types.RPCParseError("", errors.Wrap(err, "Error unmarshalling request"))) return } if len(r.URL.Path) > 1 { - WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID)) + WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path))) return } rpcFunc := funcMap[request.Method] @@ -123,19 +123,19 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han return } if rpcFunc.ws { - WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID)) + WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, errors.New("Trying to use Websocket method in non-ws context"))) return } args, err := jsonParamsToArgsRPC(rpcFunc, request.Params) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID)) + WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) return } returns := rpcFunc.f.Call(args) logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) result, err := unreflectResult(returns) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID)) + WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err)) return } WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(request.ID, result)) @@ -229,7 +229,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit // Exception for websocket endpoints if rpcFunc.ws { return func(w http.ResponseWriter, r *http.Request) { - WriteRPCResponseHTTP(w, types.RPCInternalError("")) + WriteRPCResponseHTTP(w, types.RPCInternalError("", errors.New("Trying to use Websocket method in non-ws context"))) } } // All other endpoints @@ -237,14 +237,14 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit logger.Debug("HTTP HANDLER", "req", r) args, err := httpParamsToArgs(rpcFunc, r) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("")) + WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("", errors.Wrap(err, "Error converting http params to arguments"))) return } returns := rpcFunc.f.Call(args) logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) result, err := unreflectResult(returns) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInternalError("")) + WriteRPCResponseHTTP(w, types.RPCInternalError("", err)) return } WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result)) @@ -509,7 +509,7 @@ func (wsc *wsConnection) readRoutine() { var request types.RPCRequest err = json.Unmarshal(in, &request) if err != nil { - wsc.WriteRPCResponse(types.RPCParseError("")) + wsc.WriteRPCResponse(types.RPCParseError("", errors.Wrap(err, "Error unmarshaling request"))) continue } @@ -528,7 +528,7 @@ func (wsc *wsConnection) readRoutine() { args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) } if err != nil { - wsc.WriteRPCResponse(types.RPCInternalError(request.ID)) + wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) continue } returns := rpcFunc.f.Call(args) @@ -538,7 +538,7 @@ func (wsc *wsConnection) readRoutine() { result, err := unreflectResult(returns) if err != nil { - wsc.WriteRPCResponse(types.RPCInternalError(request.ID)) + wsc.WriteRPCResponse(types.RPCInternalError(request.ID, err)) continue } else { wsc.WriteRPCResponse(types.NewRPCSuccessResponse(request.ID, result)) diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go index 2d6f5f1b..7623337d 100644 --- a/rpc/lib/server/http_server.go +++ b/rpc/lib/server/http_server.go @@ -12,6 +12,7 @@ import ( "time" "github.com/pkg/errors" + types "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tmlibs/log" ) @@ -99,7 +100,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler // For the rest, logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack())) rww.WriteHeader(http.StatusInternalServerError) - WriteRPCResponseHTTP(rww, types.RPCInternalError("")) + WriteRPCResponseHTTP(rww, types.RPCInternalError("", e.(error))) } } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index 528255a8..b649d1b9 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -5,13 +5,13 @@ import ( "fmt" "strings" + "github.com/pkg/errors" + events "github.com/tendermint/tmlibs/events" ) -type RpcError struct { - Code int `json:"code"` - Message string `json:"message"` -} +//---------------------------------------- +// REQUEST type RPCRequest struct { JSONRPC string `json:"jsonrpc"` @@ -52,6 +52,13 @@ func ArrayToRequest(id string, method string, params []interface{}) (RPCRequest, } //---------------------------------------- +// RESPONSE + +type RpcError struct { + Code int `json:"code"` + Message string `json:"message"` + Data string `json:"data,omitempty"` +} type RPCResponse struct { JSONRPC string `json:"jsonrpc"` @@ -67,7 +74,7 @@ func NewRPCSuccessResponse(id string, res interface{}) RPCResponse { var js []byte js, err := json.Marshal(res) if err != nil { - return RPCInternalError(id) + return RPCInternalError(id, errors.Wrap(err, "Error marshalling response")) } rawMsg := json.RawMessage(js) raw = &rawMsg @@ -76,11 +83,11 @@ func NewRPCSuccessResponse(id string, res interface{}) RPCResponse { return RPCResponse{JSONRPC: "2.0", ID: id, Result: raw} } -func NewRPCErrorResponse(id string, code int, msg string) RPCResponse { +func NewRPCErrorResponse(id string, code int, msg string, data string) RPCResponse { return RPCResponse{ JSONRPC: "2.0", ID: id, - Error: &RpcError{Code: code, Message: msg}, + Error: &RpcError{Code: code, Message: msg, Data: data}, } } @@ -92,28 +99,28 @@ func (resp RPCResponse) String() string { } } -func RPCParseError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON") +func RPCParseError(id string, err error) RPCResponse { + return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON", err.Error()) } -func RPCInvalidRequestError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32600, "Invalid Request") +func RPCInvalidRequestError(id string, err error) RPCResponse { + return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error()) } func RPCMethodNotFoundError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32601, "Method not found") + return NewRPCErrorResponse(id, -32601, "Method not found", "") } -func RPCInvalidParamsError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32602, "Invalid params") +func RPCInvalidParamsError(id string, err error) RPCResponse { + return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error()) } -func RPCInternalError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32603, "Internal error") +func RPCInternalError(id string, err error) RPCResponse { + return NewRPCErrorResponse(id, -32603, "Internal error", err.Error()) } -func RPCServerError(id string) RPCResponse { - return NewRPCErrorResponse(id, -32000, "Server error") +func RPCServerError(id string, err error) RPCResponse { + return NewRPCErrorResponse(id, -32000, "Server error", err.Error()) } //---------------------------------------- @@ -133,7 +140,7 @@ type WSRPCContext struct { } //---------------------------------------- -// sockets +// SOCKETS // // Determine if its a unix or tcp socket. // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port diff --git a/rpc/lib/types/types_test.go b/rpc/lib/types/types_test.go index 14688ece..bab42124 100644 --- a/rpc/lib/types/types_test.go +++ b/rpc/lib/types/types_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "testing" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -19,14 +20,13 @@ func TestResponses(t *testing.T) { s := `{"jsonrpc":"2.0","id":"1","result":{"Value":"hello"}}` assert.Equal(string(s), string(b)) - d := RPCParseError("1") + d := RPCParseError("1", errors.New("Hello world")) e, _ := json.Marshal(d) - f := `{"jsonrpc":"2.0","id":"1","error":{"code":-32700,"message":"Parse error. Invalid JSON"}}` + f := `{"jsonrpc":"2.0","id":"1","error":{"code":-32700,"message":"Parse error. Invalid JSON","data":"Hello world"}}` assert.Equal(string(f), string(e)) g := RPCMethodNotFoundError("2") h, _ := json.Marshal(g) i := `{"jsonrpc":"2.0","id":"2","error":{"code":-32601,"message":"Method not found"}}` assert.Equal(string(h), string(i)) - }