Index: server/prpc/encoding.go |
diff --git a/server/prpc/encoding.go b/server/prpc/encoding.go |
index 2788e2e29d1d8b4a2a4b50380368de5e54399482..f02423850fb467447713a5bc75275074a6c63360 100644 |
--- a/server/prpc/encoding.go |
+++ b/server/prpc/encoding.go |
@@ -11,6 +11,7 @@ import ( |
"io" |
"net/http" |
"sort" |
+ "strconv" |
"github.com/golang/protobuf/jsonpb" |
"github.com/golang/protobuf/proto" |
@@ -18,11 +19,14 @@ import ( |
"google.golang.org/grpc" |
"google.golang.org/grpc/codes" |
+ "github.com/luci/luci-go/common/errors" |
"github.com/luci/luci-go/common/logging" |
+ "github.com/luci/luci-go/common/prpc" |
) |
const ( |
- headerAccept = "Accept" |
+ headerAccept = "Accept" |
+ headerGRPCCode = "X-Prpc-Grpc-Code" |
dnj
2016/01/21 07:44:28
Make this exported and reuse it in client? No poin
nodir
2016/01/22 00:47:24
Done, but vice-versa: common cannot import server
|
) |
// responseFormat returns the format to be used in a response. |
@@ -114,30 +118,6 @@ func writeMessage(w http.ResponseWriter, msg proto.Message, format format) error |
return err |
} |
-// codeToStatus maps gRPC codes to HTTP statuses. |
-// This map may need to be corrected when |
-// https://github.com/grpc/grpc-common/issues/210 |
-// is closed. |
-var codeToStatus = map[codes.Code]int{ |
- codes.OK: http.StatusOK, |
- codes.Canceled: http.StatusNoContent, |
- codes.Unknown: http.StatusInternalServerError, |
- codes.InvalidArgument: http.StatusBadRequest, |
- codes.DeadlineExceeded: http.StatusServiceUnavailable, |
- codes.NotFound: http.StatusNotFound, |
- codes.AlreadyExists: http.StatusConflict, |
- codes.PermissionDenied: http.StatusForbidden, |
- codes.Unauthenticated: http.StatusUnauthorized, |
- codes.ResourceExhausted: http.StatusServiceUnavailable, |
- codes.FailedPrecondition: http.StatusPreconditionFailed, |
- codes.Aborted: http.StatusInternalServerError, |
- codes.OutOfRange: http.StatusBadRequest, |
- codes.Unimplemented: http.StatusNotImplemented, |
- codes.Internal: http.StatusInternalServerError, |
- codes.Unavailable: http.StatusServiceUnavailable, |
- codes.DataLoss: http.StatusInternalServerError, |
-} |
- |
// ErrorStatus returns HTTP status for an error. |
// In particular, it maps gRPC codes to HTTP statuses. |
// Status of nil is 200. |
@@ -148,7 +128,7 @@ func ErrorStatus(err error) int { |
return err.status |
} |
- status, ok := codeToStatus[grpc.Code(err)] |
+ status, ok := prpc.CodeStatus(grpc.Code(err)) |
if !ok { |
status = http.StatusInternalServerError |
} |
@@ -186,6 +166,7 @@ func writeError(c context.Context, w http.ResponseWriter, err error) { |
logging.Errorf(c, "HTTP %d: %s", status, ErrorDesc(err)) |
} |
+ w.Header().Set(headerGRPCCode, strconv.Itoa(int(grpcCode(err)))) |
w.Header().Set(headerContentType, "text/plain") |
w.WriteHeader(status) |
@@ -200,6 +181,30 @@ func writeError(c context.Context, w http.ResponseWriter, err error) { |
} |
} |
+// returns the most appropriate gRPC code for an error. |
+func grpcCode(err error) codes.Code { |
+ if err == nil { |
+ return codes.OK |
+ } |
+ |
+ for ; err != nil; err = errors.Unwrap(err) { |
+ if code := grpc.Code(err); code != codes.Unknown { |
+ return code |
+ } |
+ |
+ switch err { |
+ |
+ case context.Canceled: |
+ return codes.Canceled |
+ |
+ case context.DeadlineExceeded: |
+ return codes.DeadlineExceeded |
+ |
+ } |
+ } |
+ return codes.Unknown |
+} |
+ |
func assert(condition bool) { |
if !condition { |
panicf("assertion failed") |