53176dcb0e
if your server is built to make drpc connections, clients can still connect with grpc. thus, your responses to grpc clients must still look the same, so we have to have all of our status wrapping include codes for both the drpc and grpc servers to return the right thing. Change-Id: If99fa0e674dec2e20ddd372a827f1c01b4d305b2
178 lines
4.0 KiB
Go
178 lines
4.0 KiB
Go
// Copyright (C) 2019 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package rpcstatus
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"storj.io/drpc/drpcerr"
|
|
)
|
|
|
|
// StatusCode is an enumeration of rpc status codes.
|
|
type StatusCode uint64
|
|
|
|
// These constants are all the rpc error codes. It is important that
|
|
// their numerical values do not change.
|
|
const (
|
|
Unknown StatusCode = iota
|
|
OK
|
|
Canceled
|
|
InvalidArgument
|
|
DeadlineExceeded
|
|
NotFound
|
|
AlreadyExists
|
|
PermissionDenied
|
|
ResourceExhausted
|
|
FailedPrecondition
|
|
Aborted
|
|
OutOfRange
|
|
Unimplemented
|
|
Internal
|
|
Unavailable
|
|
DataLoss
|
|
Unauthenticated
|
|
)
|
|
|
|
// Code returns the status code associated with the error.
|
|
func Code(err error) StatusCode {
|
|
// special case: if the error is context canceled or deadline exceeded, the code
|
|
// must be those. additionally, grpc returns OK for a nil error, so we will, too.
|
|
switch err {
|
|
case nil:
|
|
return OK
|
|
case context.Canceled:
|
|
return Canceled
|
|
case context.DeadlineExceeded:
|
|
return DeadlineExceeded
|
|
default:
|
|
if code := StatusCode(drpcerr.Code(err)); code != Unknown {
|
|
return code
|
|
}
|
|
if grpccode := status.Code(err); grpccode != codes.Unknown {
|
|
return statusCodeFromGRPC(grpccode)
|
|
}
|
|
return Unknown
|
|
}
|
|
}
|
|
|
|
// Error wraps the message with a status code into an error.
|
|
func Error(code StatusCode, msg string) error {
|
|
return &codeErr{
|
|
err: errors.New(msg),
|
|
code: code,
|
|
grpc: status.New(code.toGRPC(), msg),
|
|
}
|
|
}
|
|
|
|
// Errorf : Error :: fmt.Sprintf : fmt.Sprint
|
|
func Errorf(code StatusCode, format string, a ...interface{}) error {
|
|
return &codeErr{
|
|
err: fmt.Errorf(format, a...),
|
|
code: code,
|
|
grpc: status.Newf(code.toGRPC(), format, a...),
|
|
}
|
|
}
|
|
|
|
// codeErr implements error that can work both in grpc and drpc.
|
|
type codeErr struct {
|
|
err error
|
|
code StatusCode
|
|
grpc *status.Status
|
|
}
|
|
|
|
func (c *codeErr) Error() string { return c.err.Error() }
|
|
func (c *codeErr) Unwrap() error { return c.err }
|
|
func (c *codeErr) Cause() error { return c.err }
|
|
func (c *codeErr) Code() uint64 { return uint64(c.code) }
|
|
func (c *codeErr) GRPCStatus() *status.Status { return c.grpc }
|
|
|
|
// toGRPC returns the grpc version of the status code.
|
|
func (s StatusCode) toGRPC() codes.Code {
|
|
switch s {
|
|
case Unknown:
|
|
return codes.Unknown
|
|
case OK:
|
|
return codes.OK
|
|
case Canceled:
|
|
return codes.Canceled
|
|
case InvalidArgument:
|
|
return codes.InvalidArgument
|
|
case DeadlineExceeded:
|
|
return codes.DeadlineExceeded
|
|
case NotFound:
|
|
return codes.NotFound
|
|
case AlreadyExists:
|
|
return codes.AlreadyExists
|
|
case PermissionDenied:
|
|
return codes.PermissionDenied
|
|
case ResourceExhausted:
|
|
return codes.ResourceExhausted
|
|
case FailedPrecondition:
|
|
return codes.FailedPrecondition
|
|
case Aborted:
|
|
return codes.Aborted
|
|
case OutOfRange:
|
|
return codes.OutOfRange
|
|
case Unimplemented:
|
|
return codes.Unimplemented
|
|
case Internal:
|
|
return codes.Internal
|
|
case Unavailable:
|
|
return codes.Unavailable
|
|
case DataLoss:
|
|
return codes.DataLoss
|
|
case Unauthenticated:
|
|
return codes.Unauthenticated
|
|
default:
|
|
return codes.Unknown
|
|
}
|
|
}
|
|
|
|
// statusCodeFromGRPC turns the grpc status code into a StatusCode.
|
|
func statusCodeFromGRPC(code codes.Code) StatusCode {
|
|
switch code {
|
|
case codes.Unknown:
|
|
return Unknown
|
|
case codes.OK:
|
|
return OK
|
|
case codes.Canceled:
|
|
return Canceled
|
|
case codes.InvalidArgument:
|
|
return InvalidArgument
|
|
case codes.DeadlineExceeded:
|
|
return DeadlineExceeded
|
|
case codes.NotFound:
|
|
return NotFound
|
|
case codes.AlreadyExists:
|
|
return AlreadyExists
|
|
case codes.PermissionDenied:
|
|
return PermissionDenied
|
|
case codes.ResourceExhausted:
|
|
return ResourceExhausted
|
|
case codes.FailedPrecondition:
|
|
return FailedPrecondition
|
|
case codes.Aborted:
|
|
return Aborted
|
|
case codes.OutOfRange:
|
|
return OutOfRange
|
|
case codes.Unimplemented:
|
|
return Unimplemented
|
|
case codes.Internal:
|
|
return Internal
|
|
case codes.Unavailable:
|
|
return Unavailable
|
|
case codes.DataLoss:
|
|
return DataLoss
|
|
case codes.Unauthenticated:
|
|
return Unauthenticated
|
|
default:
|
|
return Unknown
|
|
}
|
|
}
|