errors.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package runtime
  2. import (
  3. "context"
  4. "io"
  5. "net/http"
  6. "github.com/golang/protobuf/proto"
  7. "github.com/golang/protobuf/ptypes/any"
  8. "google.golang.org/grpc/codes"
  9. "google.golang.org/grpc/grpclog"
  10. "google.golang.org/grpc/status"
  11. )
  12. // HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status.
  13. // See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
  14. func HTTPStatusFromCode(code codes.Code) int {
  15. switch code {
  16. case codes.OK:
  17. return http.StatusOK
  18. case codes.Canceled:
  19. return http.StatusRequestTimeout
  20. case codes.Unknown:
  21. return http.StatusInternalServerError
  22. case codes.InvalidArgument:
  23. return http.StatusBadRequest
  24. case codes.DeadlineExceeded:
  25. return http.StatusGatewayTimeout
  26. case codes.NotFound:
  27. return http.StatusNotFound
  28. case codes.AlreadyExists:
  29. return http.StatusConflict
  30. case codes.PermissionDenied:
  31. return http.StatusForbidden
  32. case codes.Unauthenticated:
  33. return http.StatusUnauthorized
  34. case codes.ResourceExhausted:
  35. return http.StatusTooManyRequests
  36. case codes.FailedPrecondition:
  37. // Note, this deliberately doesn't translate to the similarly named '412 Precondition Failed' HTTP response status.
  38. return http.StatusBadRequest
  39. case codes.Aborted:
  40. return http.StatusConflict
  41. case codes.OutOfRange:
  42. return http.StatusBadRequest
  43. case codes.Unimplemented:
  44. return http.StatusNotImplemented
  45. case codes.Internal:
  46. return http.StatusInternalServerError
  47. case codes.Unavailable:
  48. return http.StatusServiceUnavailable
  49. case codes.DataLoss:
  50. return http.StatusInternalServerError
  51. }
  52. grpclog.Infof("Unknown gRPC error code: %v", code)
  53. return http.StatusInternalServerError
  54. }
  55. var (
  56. // HTTPError replies to the request with the error.
  57. // You can set a custom function to this variable to customize error format.
  58. HTTPError = DefaultHTTPError
  59. // OtherErrorHandler handles the following error used by the gateway: StatusMethodNotAllowed StatusNotFound and StatusBadRequest
  60. OtherErrorHandler = DefaultOtherErrorHandler
  61. )
  62. type errorBody struct {
  63. Error string `protobuf:"bytes,1,name=error" json:"error"`
  64. // This is to make the error more compatible with users that expect errors to be Status objects:
  65. // https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto
  66. // It should be the exact same message as the Error field.
  67. Message string `protobuf:"bytes,1,name=message" json:"message"`
  68. Code int32 `protobuf:"varint,2,name=code" json:"code"`
  69. Details []*any.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"`
  70. }
  71. // Make this also conform to proto.Message for builtin JSONPb Marshaler
  72. func (e *errorBody) Reset() { *e = errorBody{} }
  73. func (e *errorBody) String() string { return proto.CompactTextString(e) }
  74. func (*errorBody) ProtoMessage() {}
  75. // DefaultHTTPError is the default implementation of HTTPError.
  76. // If "err" is an error from gRPC system, the function replies with the status code mapped by HTTPStatusFromCode.
  77. // If otherwise, it replies with http.StatusInternalServerError.
  78. //
  79. // The response body returned by this function is a JSON object,
  80. // which contains a member whose key is "error" and whose value is err.Error().
  81. func DefaultHTTPError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
  82. const fallback = `{"error": "failed to marshal error message"}`
  83. s, ok := status.FromError(err)
  84. if !ok {
  85. s = status.New(codes.Unknown, err.Error())
  86. }
  87. w.Header().Del("Trailer")
  88. contentType := marshaler.ContentType()
  89. // Check marshaler on run time in order to keep backwards compatability
  90. // An interface param needs to be added to the ContentType() function on
  91. // the Marshal interface to be able to remove this check
  92. if httpBodyMarshaler, ok := marshaler.(*HTTPBodyMarshaler); ok {
  93. pb := s.Proto()
  94. contentType = httpBodyMarshaler.ContentTypeFromMessage(pb)
  95. }
  96. w.Header().Set("Content-Type", contentType)
  97. body := &errorBody{
  98. Error: s.Message(),
  99. Message: s.Message(),
  100. Code: int32(s.Code()),
  101. Details: s.Proto().GetDetails(),
  102. }
  103. buf, merr := marshaler.Marshal(body)
  104. if merr != nil {
  105. grpclog.Infof("Failed to marshal error message %q: %v", body, merr)
  106. w.WriteHeader(http.StatusInternalServerError)
  107. if _, err := io.WriteString(w, fallback); err != nil {
  108. grpclog.Infof("Failed to write response: %v", err)
  109. }
  110. return
  111. }
  112. md, ok := ServerMetadataFromContext(ctx)
  113. if !ok {
  114. grpclog.Infof("Failed to extract ServerMetadata from context")
  115. }
  116. handleForwardResponseServerMetadata(w, mux, md)
  117. handleForwardResponseTrailerHeader(w, md)
  118. st := HTTPStatusFromCode(s.Code())
  119. w.WriteHeader(st)
  120. if _, err := w.Write(buf); err != nil {
  121. grpclog.Infof("Failed to write response: %v", err)
  122. }
  123. handleForwardResponseTrailer(w, md)
  124. }
  125. // DefaultOtherErrorHandler is the default implementation of OtherErrorHandler.
  126. // It simply writes a string representation of the given error into "w".
  127. func DefaultOtherErrorHandler(w http.ResponseWriter, _ *http.Request, msg string, code int) {
  128. http.Error(w, msg, code)
  129. }