mirror of https://github.com/grpc/grpc-go.git
status: fix panic when servers return a wrapped error with status OK (#6374)
This commit is contained in:
parent
acbfcbb8e8
commit
df3e021458
|
|
@ -77,11 +77,18 @@ func FromProto(s *spb.Status) *Status {
|
||||||
// FromError returns a Status representation of err.
|
// FromError returns a Status representation of err.
|
||||||
//
|
//
|
||||||
// - If err was produced by this package or implements the method `GRPCStatus()
|
// - If err was produced by this package or implements the method `GRPCStatus()
|
||||||
// *Status`, or if err wraps a type satisfying this, the appropriate Status is
|
// *Status` and `GRPCStatus()` does not return nil, or if err wraps a type
|
||||||
// returned. For wrapped errors, the message returned contains the entire
|
// satisfying this, the Status from `GRPCStatus()` is returned. For wrapped
|
||||||
// err.Error() text and not just the wrapped status.
|
// errors, the message returned contains the entire err.Error() text and not
|
||||||
|
// just the wrapped status. In that case, ok is true.
|
||||||
//
|
//
|
||||||
// - If err is nil, a Status is returned with codes.OK and no message.
|
// - If err is nil, a Status is returned with codes.OK and no message, and ok
|
||||||
|
// is true.
|
||||||
|
//
|
||||||
|
// - If err implements the method `GRPCStatus() *Status` and `GRPCStatus()`
|
||||||
|
// returns nil (which maps to Codes.OK), or if err wraps a type
|
||||||
|
// satisfying this, a Status is returned with codes.Unknown and err's
|
||||||
|
// Error() message, and ok is false.
|
||||||
//
|
//
|
||||||
// - Otherwise, err is an error not compatible with this package. In this
|
// - Otherwise, err is an error not compatible with this package. In this
|
||||||
// case, a Status is returned with codes.Unknown and err's Error() message,
|
// case, a Status is returned with codes.Unknown and err's Error() message,
|
||||||
|
|
@ -92,10 +99,24 @@ func FromError(err error) (s *Status, ok bool) {
|
||||||
}
|
}
|
||||||
type grpcstatus interface{ GRPCStatus() *Status }
|
type grpcstatus interface{ GRPCStatus() *Status }
|
||||||
if gs, ok := err.(grpcstatus); ok {
|
if gs, ok := err.(grpcstatus); ok {
|
||||||
|
if gs.GRPCStatus() == nil {
|
||||||
|
// Error has status nil, which maps to codes.OK. There
|
||||||
|
// is no sensible behavior for this, so we turn it into
|
||||||
|
// an error with codes.Unknown and discard the existing
|
||||||
|
// status.
|
||||||
|
return New(codes.Unknown, err.Error()), false
|
||||||
|
}
|
||||||
return gs.GRPCStatus(), true
|
return gs.GRPCStatus(), true
|
||||||
}
|
}
|
||||||
var gs grpcstatus
|
var gs grpcstatus
|
||||||
if errors.As(err, &gs) {
|
if errors.As(err, &gs) {
|
||||||
|
if gs.GRPCStatus() == nil {
|
||||||
|
// Error wraps an error that has status nil, which maps
|
||||||
|
// to codes.OK. There is no sensible behavior for this,
|
||||||
|
// so we turn it into an error with codes.Unknown and
|
||||||
|
// discard the existing status.
|
||||||
|
return New(codes.Unknown, err.Error()), false
|
||||||
|
}
|
||||||
p := gs.GRPCStatus().Proto()
|
p := gs.GRPCStatus().Proto()
|
||||||
p.Message = err.Error()
|
p.Message = err.Error()
|
||||||
return status.FromProto(p), true
|
return status.FromProto(p), true
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,33 @@ func (s) TestFromErrorWrapped(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type customErrorNilStatus struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c customErrorNilStatus) Error() string {
|
||||||
|
return "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c customErrorNilStatus) GRPCStatus() *Status {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s) TestFromErrorImplementsInterfaceReturnsOKStatus(t *testing.T) {
|
||||||
|
err := customErrorNilStatus{}
|
||||||
|
s, ok := FromError(err)
|
||||||
|
if ok || s.Code() != codes.Unknown || s.Message() != err.Error() {
|
||||||
|
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, codes.Unknown, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s) TestFromErrorImplementsInterfaceReturnsOKStatusWrapped(t *testing.T) {
|
||||||
|
err := fmt.Errorf("wrapping: %w", customErrorNilStatus{})
|
||||||
|
s, ok := FromError(err)
|
||||||
|
if ok || s.Code() != codes.Unknown || s.Message() != err.Error() {
|
||||||
|
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, codes.Unknown, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s) TestFromErrorImplementsInterfaceWrapped(t *testing.T) {
|
func (s) TestFromErrorImplementsInterfaceWrapped(t *testing.T) {
|
||||||
const code, message = codes.Internal, "test description"
|
const code, message = codes.Internal, "test description"
|
||||||
err := fmt.Errorf("wrapped error: %w", customError{Code: code, Message: message})
|
err := fmt.Errorf("wrapped error: %w", customError{Code: code, Message: message})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue