diff --git a/example/errors/handler/backend/handler.go b/example/errors/handler/backend/handler.go index 4528ca5..1fa1f44 100644 --- a/example/errors/handler/backend/handler.go +++ b/example/errors/handler/backend/handler.go @@ -75,6 +75,13 @@ func (h *Handler) MultiScalar(w http.ResponseWriter, r *http.Request) (e *backen } } +func (h *Handler) Struct(w http.ResponseWriter, r *http.Request) (e *backend.Struct) { + return &backend.Struct{ + Message: "my custom scalar", + Data: "hello world", + } +} + func (h *Handler) TypedError(w http.ResponseWriter, r *http.Request) (e error) { return ErrTyped } diff --git a/example/errors/main.go b/example/errors/main.go index 9f0f067..961e5b8 100644 --- a/example/errors/main.go +++ b/example/errors/main.go @@ -1,5 +1,9 @@ package main +// func () error + +// func () error => func () *Error => decode => func() error + import ( "context" "fmt" @@ -89,6 +93,18 @@ func call() { } } + { + fmt.Println("-------------------------") + strct, err := c.Struct(ctx) + if err != nil { + panic("client error should be nil") + } else if strct == nil { + panic("service error should not be nil") + } else if strct != nil { + fmt.Println("OK") + } + } + { fmt.Println("-------------------------") var gotsrpcErr *gotsrpc.Error diff --git a/example/errors/service/backend/gotsrpc_gen.go b/example/errors/service/backend/gotsrpc_gen.go index 1c1fd5b..f946efe 100644 --- a/example/errors/service/backend/gotsrpc_gen.go +++ b/example/errors/service/backend/gotsrpc_gen.go @@ -17,6 +17,7 @@ const ( ServiceGoTSRPCProxyMultiScalar = "MultiScalar" ServiceGoTSRPCProxyScalar = "Scalar" ServiceGoTSRPCProxyScalarError = "ScalarError" + ServiceGoTSRPCProxyStruct = "Struct" ServiceGoTSRPCProxyTypedCustomError = "TypedCustomError" ServiceGoTSRPCProxyTypedError = "TypedError" ServiceGoTSRPCProxyTypedScalarError = "TypedScalarError" @@ -146,6 +147,24 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) } gotsrpc.Monitor(w, r, args, rets, callStats) return + case ServiceGoTSRPCProxyStruct: + var ( + args []interface{} + rets []interface{} + ) + executionStart := time.Now() + rw := gotsrpc.ResponseWriter{ResponseWriter: w} + structE := p.service.Struct(&rw, r) + callStats.Execution = time.Since(executionStart) + if rw.Status() == http.StatusOK { + rets = []interface{}{structE} + if err := gotsrpc.Reply(rets, callStats, r, w); err != nil { + gotsrpc.ErrorCouldNotReply(w) + return + } + } + gotsrpc.Monitor(w, r, args, rets, callStats) + return case ServiceGoTSRPCProxyTypedCustomError: var ( args []interface{} diff --git a/example/errors/service/backend/gotsrpcclient_gen.go b/example/errors/service/backend/gotsrpcclient_gen.go index a41d597..298db53 100644 --- a/example/errors/service/backend/gotsrpcclient_gen.go +++ b/example/errors/service/backend/gotsrpcclient_gen.go @@ -16,6 +16,7 @@ type ServiceGoTSRPCClient interface { MultiScalar(ctx go_context.Context) (e *MultiScalar, clientErr error) Scalar(ctx go_context.Context) (e *Scalar, clientErr error) ScalarError(ctx go_context.Context) (e error, clientErr error) + Struct(ctx go_context.Context) (e *Struct, clientErr error) TypedCustomError(ctx go_context.Context) (e error, clientErr error) TypedError(ctx go_context.Context) (e error, clientErr error) TypedScalarError(ctx go_context.Context) (e error, clientErr error) @@ -94,6 +95,16 @@ func (tsc *HTTPServiceGoTSRPCClient) ScalarError(ctx go_context.Context) (e erro return } +func (tsc *HTTPServiceGoTSRPCClient) Struct(ctx go_context.Context) (e *Struct, clientErr error) { + args := []interface{}{} + reply := []interface{}{&e} + clientErr = tsc.Client.Call(ctx, tsc.URL, tsc.EndPoint, "Struct", args, reply) + if clientErr != nil { + clientErr = pkg_errors.WithMessage(clientErr, "failed to call backend.ServiceGoTSRPCProxy Struct") + } + return +} + func (tsc *HTTPServiceGoTSRPCClient) TypedCustomError(ctx go_context.Context) (e error, clientErr error) { args := []interface{}{} reply := []interface{}{&e} diff --git a/example/errors/service/backend/service.go b/example/errors/service/backend/service.go index 7b75fb3..5f93bfb 100644 --- a/example/errors/service/backend/service.go +++ b/example/errors/service/backend/service.go @@ -12,6 +12,23 @@ const ( type Scalar string +func (s Scalar) String() string { + return string(s) +} + +func (s *Scalar) Error() string { + return s.String() +} + +type Struct struct { + Message string `json:"message"` + Data string `json:"data"` +} + +func (s *Struct) Error() string { + return s.Message +} + type ( ScalarA string ScalarB string @@ -36,6 +53,7 @@ type Service interface { Error(w http.ResponseWriter, r *http.Request) (e error) Scalar(w http.ResponseWriter, r *http.Request) (e *Scalar) MultiScalar(w http.ResponseWriter, r *http.Request) (e *MultiScalar) + Struct(w http.ResponseWriter, r *http.Request) (e *Struct) TypedError(w http.ResponseWriter, r *http.Request) (e error) ScalarError(w http.ResponseWriter, r *http.Request) (e error) CustomError(w http.ResponseWriter, r *http.Request) (e error) diff --git a/transport.go b/transport.go index 629cdc4..419b7c4 100644 --- a/transport.go +++ b/transport.go @@ -4,8 +4,9 @@ import ( "net/http" "reflect" "time" - "unsafe" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" "github.com/ugorji/go/codec" ) @@ -53,22 +54,12 @@ var msgpackClientHandle = &clientHandle{ return ret, nil }, afterDecodeReply: func(reply *[]interface{}, wrappedReply []interface{}) error { - ptr := func(v reflect.Value) reflect.Value { - pt := reflect.PtrTo(v.Type()) // create a *T type. - pv := reflect.New(pt.Elem()) // create a reflect.Value of type *T. - pv.Elem().Set(v) // sets pv to point to underlying value of v. - return pv - } - for k, v := range wrappedReply { if e, ok := v.(*Error); ok && e != nil { if y, ok := (*reply)[k].(*error); ok { *y = e - } else { - rv := reflect.ValueOf((*reply)[k]).Elem() - elem := reflect.NewAt(rv.Type().Elem(), unsafe.Pointer(rv.UnsafeAddr())).Elem() - elem.Set(reflect.ValueOf(e.Data).Convert(elem.Type())) - rv.Set(ptr(elem)) + } else if err := mapstructure.Decode(e.Data, (*reply)[k]); err != nil { + return errors.Wrap(err, "failed to decode wrapped error") } } }