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.).
```
This commit is contained in:
Anton Kaliaev 2017-05-26 17:45:09 +02:00 committed by Ethan Buchman
parent 6c1572c9b8
commit f74de4cb86
4 changed files with 42 additions and 34 deletions

View File

@ -110,11 +110,11 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
var request types.RPCRequest var request types.RPCRequest
err := json.Unmarshal(b, &request) err := json.Unmarshal(b, &request)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, types.RPCParseError("")) WriteRPCResponseHTTP(w, types.RPCParseError("", errors.Wrap(err, "Error unmarshalling request")))
return return
} }
if len(r.URL.Path) > 1 { 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 return
} }
rpcFunc := funcMap[request.Method] rpcFunc := funcMap[request.Method]
@ -123,19 +123,19 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
return return
} }
if rpcFunc.ws { 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 return
} }
args, err := jsonParamsToArgsRPC(rpcFunc, request.Params) args, err := jsonParamsToArgsRPC(rpcFunc, request.Params)
if err != nil { 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 return
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID)) WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err))
return return
} }
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(request.ID, result)) 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 // Exception for websocket endpoints
if rpcFunc.ws { if rpcFunc.ws {
return func(w http.ResponseWriter, r *http.Request) { 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 // All other endpoints
@ -237,14 +237,14 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
logger.Debug("HTTP HANDLER", "req", r) logger.Debug("HTTP HANDLER", "req", r)
args, err := httpParamsToArgs(rpcFunc, r) args, err := httpParamsToArgs(rpcFunc, r)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("")) WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("", errors.Wrap(err, "Error converting http params to arguments")))
return return
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
WriteRPCResponseHTTP(w, types.RPCInternalError("")) WriteRPCResponseHTTP(w, types.RPCInternalError("", err))
return return
} }
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result)) WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result))
@ -509,7 +509,7 @@ func (wsc *wsConnection) readRoutine() {
var request types.RPCRequest var request types.RPCRequest
err = json.Unmarshal(in, &request) err = json.Unmarshal(in, &request)
if err != nil { if err != nil {
wsc.WriteRPCResponse(types.RPCParseError("")) wsc.WriteRPCResponse(types.RPCParseError("", errors.Wrap(err, "Error unmarshaling request")))
continue continue
} }
@ -528,7 +528,7 @@ func (wsc *wsConnection) readRoutine() {
args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
} }
if err != nil { 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 continue
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
@ -538,7 +538,7 @@ func (wsc *wsConnection) readRoutine() {
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
wsc.WriteRPCResponse(types.RPCInternalError(request.ID)) wsc.WriteRPCResponse(types.RPCInternalError(request.ID, err))
continue continue
} else { } else {
wsc.WriteRPCResponse(types.NewRPCSuccessResponse(request.ID, result)) wsc.WriteRPCResponse(types.NewRPCSuccessResponse(request.ID, result))

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
types "github.com/tendermint/tendermint/rpc/lib/types" types "github.com/tendermint/tendermint/rpc/lib/types"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@ -99,7 +100,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
// For the rest, // For the rest,
logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack())) logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
rww.WriteHeader(http.StatusInternalServerError) rww.WriteHeader(http.StatusInternalServerError)
WriteRPCResponseHTTP(rww, types.RPCInternalError("")) WriteRPCResponseHTTP(rww, types.RPCInternalError("", e.(error)))
} }
} }

View File

@ -5,13 +5,13 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
events "github.com/tendermint/tmlibs/events" events "github.com/tendermint/tmlibs/events"
) )
type RpcError struct { //----------------------------------------
Code int `json:"code"` // REQUEST
Message string `json:"message"`
}
type RPCRequest struct { type RPCRequest struct {
JSONRPC string `json:"jsonrpc"` 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 { type RPCResponse struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
@ -67,7 +74,7 @@ func NewRPCSuccessResponse(id string, res interface{}) RPCResponse {
var js []byte var js []byte
js, err := json.Marshal(res) js, err := json.Marshal(res)
if err != nil { if err != nil {
return RPCInternalError(id) return RPCInternalError(id, errors.Wrap(err, "Error marshalling response"))
} }
rawMsg := json.RawMessage(js) rawMsg := json.RawMessage(js)
raw = &rawMsg raw = &rawMsg
@ -76,11 +83,11 @@ func NewRPCSuccessResponse(id string, res interface{}) RPCResponse {
return RPCResponse{JSONRPC: "2.0", ID: id, Result: raw} 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{ return RPCResponse{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: id, 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 { func RPCParseError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON") return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON", err.Error())
} }
func RPCInvalidRequestError(id string) RPCResponse { func RPCInvalidRequestError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32600, "Invalid Request") return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
} }
func RPCMethodNotFoundError(id string) RPCResponse { func RPCMethodNotFoundError(id string) RPCResponse {
return NewRPCErrorResponse(id, -32601, "Method not found") return NewRPCErrorResponse(id, -32601, "Method not found", "")
} }
func RPCInvalidParamsError(id string) RPCResponse { func RPCInvalidParamsError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32602, "Invalid params") return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
} }
func RPCInternalError(id string) RPCResponse { func RPCInternalError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32603, "Internal error") return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
} }
func RPCServerError(id string) RPCResponse { func RPCServerError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32000, "Server error") 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. // 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 // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -19,14 +20,13 @@ func TestResponses(t *testing.T) {
s := `{"jsonrpc":"2.0","id":"1","result":{"Value":"hello"}}` s := `{"jsonrpc":"2.0","id":"1","result":{"Value":"hello"}}`
assert.Equal(string(s), string(b)) assert.Equal(string(s), string(b))
d := RPCParseError("1") d := RPCParseError("1", errors.New("Hello world"))
e, _ := json.Marshal(d) 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)) assert.Equal(string(f), string(e))
g := RPCMethodNotFoundError("2") g := RPCMethodNotFoundError("2")
h, _ := json.Marshal(g) h, _ := json.Marshal(g)
i := `{"jsonrpc":"2.0","id":"2","error":{"code":-32601,"message":"Method not found"}}` i := `{"jsonrpc":"2.0","id":"2","error":{"code":-32601,"message":"Method not found"}}`
assert.Equal(string(h), string(i)) assert.Equal(string(h), string(i))
} }