rpclib: improve errors

This commit is contained in:
Łukasz Magiera 2019-07-02 15:49:10 +02:00
parent bed044b5a1
commit 573509b3e6
3 changed files with 57 additions and 22 deletions

View File

@ -64,7 +64,7 @@ func NewClient(addr string, namespace string, handler interface{}) {
valOut, errOut, nout := processFuncOut(ftyp) valOut, errOut, nout := processFuncOut(ftyp)
processResponse := func(resp clientResponse) []reflect.Value { processResponse := func(resp clientResponse, code int) []reflect.Value {
out := make([]reflect.Value, nout) out := make([]reflect.Value, nout)
if valOut != -1 { if valOut != -1 {
@ -73,7 +73,7 @@ func NewClient(addr string, namespace string, handler interface{}) {
if errOut != -1 { if errOut != -1 {
out[errOut] = reflect.New(errorType).Elem() out[errOut] = reflect.New(errorType).Elem()
if resp.Error != nil { if resp.Error != nil {
out[errOut].Set(reflect.ValueOf(errors.New(resp.Error.Message))) out[errOut].Set(reflect.ValueOf(resp.Error))
} }
} }
@ -139,11 +139,6 @@ func NewClient(addr string, namespace string, handler interface{}) {
// process response // process response
// TODO: check error codes in spec
if httpResp.StatusCode != 200 {
return processError(errors.New("non 200 response code"))
}
var resp clientResponse var resp clientResponse
if valOut != -1 { if valOut != -1 {
resp.Result = result(reflect.New(ftyp.Out(valOut))) resp.Result = result(reflect.New(ftyp.Out(valOut)))
@ -157,7 +152,7 @@ func NewClient(addr string, namespace string, handler interface{}) {
return processError(errors.New("request and response id didn't match")) return processError(errors.New("request and response id didn't match"))
} }
return processResponse(resp) return processResponse(resp, httpResp.StatusCode)
}) })
val.Elem().Field(i).Set(fn) val.Elem().Field(i).Set(fn)

View File

@ -8,6 +8,14 @@ import (
"reflect" "reflect"
) )
const (
rpcParseError = -32700
rpcInvalidRequest = -32600
rpcMethodNotFound = -32601
rpcInvalidParams = -32602
rpcInternalError = -32603
)
type rpcHandler struct { type rpcHandler struct {
paramReceivers []reflect.Type paramReceivers []reflect.Type
nParams int nParams int
@ -59,6 +67,13 @@ type respError struct {
Message string `json:"message"` Message string `json:"message"`
} }
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
}
type response struct { type response struct {
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"` Result interface{} `json:"result,omitempty"`
@ -70,21 +85,18 @@ type response struct {
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var req request var req request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
fmt.Println(err) s.rpcError(w, &req, rpcParseError, err)
w.WriteHeader(500)
return return
} }
handler, ok := s.methods[req.Method] handler, ok := s.methods[req.Method]
if !ok { if !ok {
fmt.Printf("rpcserver: unknown method %s\n", req.Method) s.rpcError(w, &req, rpcMethodNotFound, fmt.Errorf("method '%s' not found", req.Method))
w.WriteHeader(500)
return return
} }
if len(req.Params) != handler.nParams { if len(req.Params) != handler.nParams {
fmt.Println("rpcserver: wrong param count") s.rpcError(w, &req, rpcInvalidParams, fmt.Errorf("wrong param count"))
w.WriteHeader(500)
return return
} }
@ -97,8 +109,7 @@ func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for i := 0; i < handler.nParams; i++ { for i := 0; i < handler.nParams; i++ {
rp := reflect.New(handler.paramReceivers[i]) rp := reflect.New(handler.paramReceivers[i])
if err := json.NewDecoder(bytes.NewReader(req.Params[i].data)).Decode(rp.Interface()); err != nil { if err := json.NewDecoder(bytes.NewReader(req.Params[i].data)).Decode(rp.Interface()); err != nil {
w.WriteHeader(500) s.rpcError(w, &req, rpcParseError, err)
fmt.Println(err)
return return
} }
@ -130,11 +141,28 @@ func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
fmt.Println(err) fmt.Println(err)
w.WriteHeader(500)
return return
} }
} }
func (s *RPCServer) rpcError(w http.ResponseWriter, req *request, code int, err error) {
w.WriteHeader(500)
if req.Id == nil { // notification
return
}
resp := response{
Jsonrpc: "2.0",
Id: *req.Id,
Error: &respError{
Code: code,
Message: err.(error).Error(),
},
}
_ = json.NewEncoder(w).Encode(resp)
}
func (s *RPCServer) Register(namespace string, r interface{}) { func (s *RPCServer) Register(namespace string, r interface{}) {
val := reflect.ValueOf(r) val := reflect.ValueOf(r)
//TODO: expect ptr //TODO: expect ptr
@ -198,3 +226,5 @@ func processFuncOut(funcType reflect.Type) (valOut int, errOut int, n int) {
return return
} }
var _ error = &respError{}

View File

@ -87,7 +87,7 @@ func TestRPC(t *testing.T) {
t.Fatal("expected error") t.Fatal("expected error")
} }
if err.Error() != "test" { if err.Error() != "test" {
t.Fatal("wrong error") t.Fatal("wrong error", err)
} }
// AddGet(int) int // AddGet(int) int
@ -151,8 +151,8 @@ func TestRPC(t *testing.T) {
NewClient(testServ.URL, "SimpleServerHandler", &erronly) NewClient(testServ.URL, "SimpleServerHandler", &erronly)
_, err = erronly.AddGet() _, err = erronly.AddGet()
if err == nil || err.Error() != "RPC client error: non 200 response code" { if err == nil || err.Error() != "RPC error (-32602): wrong param count" {
t.Error("wrong error") t.Error("wrong error:", err)
} }
var wrongtype struct { var wrongtype struct {
@ -161,8 +161,18 @@ func TestRPC(t *testing.T) {
NewClient(testServ.URL, "SimpleServerHandler", &wrongtype) NewClient(testServ.URL, "SimpleServerHandler", &wrongtype)
err = wrongtype.Add("not an int") err = wrongtype.Add("not an int")
if err == nil || err.Error() != "RPC client error: non 200 response code" { if err == nil || err.Error() != "RPC error (-32700): json: cannot unmarshal string into Go value of type int" {
t.Error("wrong error") t.Error("wrong error:", err)
}
var notfound struct {
NotThere func(string) error
}
NewClient(testServ.URL, "SimpleServerHandler", &notfound)
err = notfound.NotThere("hello?")
if err == nil || err.Error() != "RPC error (-32601): method 'SimpleServerHandler.NotThere' not found" {
t.Error("wrong error:", err)
} }
} }