proto_errors.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package runtime
  2. import (
  3. "context"
  4. "io"
  5. "net/http"
  6. "github.com/golang/protobuf/ptypes/any"
  7. "github.com/grpc-ecosystem/grpc-gateway/internal"
  8. "google.golang.org/grpc/codes"
  9. "google.golang.org/grpc/grpclog"
  10. "google.golang.org/grpc/status"
  11. )
  12. // StreamErrorHandlerFunc accepts an error as a gRPC error generated via status package and translates it into a
  13. // a proto struct used to represent error at the end of a stream.
  14. type StreamErrorHandlerFunc func(context.Context, error) *StreamError
  15. // StreamError is the payload for the final message in a server stream in the event that the server returns an
  16. // error after a response message has already been sent.
  17. type StreamError internal.StreamError
  18. // ProtoErrorHandlerFunc handles the error as a gRPC error generated via status package and replies to the request.
  19. type ProtoErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error)
  20. var _ ProtoErrorHandlerFunc = DefaultHTTPProtoErrorHandler
  21. // DefaultHTTPProtoErrorHandler is an implementation of HTTPError.
  22. // If "err" is an error from gRPC system, the function replies with the status code mapped by HTTPStatusFromCode.
  23. // If otherwise, it replies with http.StatusInternalServerError.
  24. //
  25. // The response body returned by this function is a Status message marshaled by a Marshaler.
  26. //
  27. // Do not set this function to HTTPError variable directly, use WithProtoErrorHandler option instead.
  28. func DefaultHTTPProtoErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
  29. // return Internal when Marshal failed
  30. const fallback = `{"code": 13, "message": "failed to marshal error message"}`
  31. s, ok := status.FromError(err)
  32. if !ok {
  33. s = status.New(codes.Unknown, err.Error())
  34. }
  35. w.Header().Del("Trailer")
  36. contentType := marshaler.ContentType()
  37. // Check marshaler on run time in order to keep backwards compatability
  38. // An interface param needs to be added to the ContentType() function on
  39. // the Marshal interface to be able to remove this check
  40. if httpBodyMarshaler, ok := marshaler.(*HTTPBodyMarshaler); ok {
  41. pb := s.Proto()
  42. contentType = httpBodyMarshaler.ContentTypeFromMessage(pb)
  43. }
  44. w.Header().Set("Content-Type", contentType)
  45. buf, merr := marshaler.Marshal(s.Proto())
  46. if merr != nil {
  47. grpclog.Infof("Failed to marshal error message %q: %v", s.Proto(), merr)
  48. w.WriteHeader(http.StatusInternalServerError)
  49. if _, err := io.WriteString(w, fallback); err != nil {
  50. grpclog.Infof("Failed to write response: %v", err)
  51. }
  52. return
  53. }
  54. md, ok := ServerMetadataFromContext(ctx)
  55. if !ok {
  56. grpclog.Infof("Failed to extract ServerMetadata from context")
  57. }
  58. handleForwardResponseServerMetadata(w, mux, md)
  59. handleForwardResponseTrailerHeader(w, md)
  60. st := HTTPStatusFromCode(s.Code())
  61. w.WriteHeader(st)
  62. if _, err := w.Write(buf); err != nil {
  63. grpclog.Infof("Failed to write response: %v", err)
  64. }
  65. handleForwardResponseTrailer(w, md)
  66. }
  67. // DefaultHTTPStreamErrorHandler converts the given err into a *StreamError via
  68. // default logic.
  69. //
  70. // It extracts the gRPC status from err if possible. The fields of the status are
  71. // used to populate the returned StreamError, and the HTTP status code is derived
  72. // from the gRPC code via HTTPStatusFromCode. If the given err does not contain a
  73. // gRPC status, an "Unknown" gRPC code is used and "Internal Server Error" HTTP code.
  74. func DefaultHTTPStreamErrorHandler(_ context.Context, err error) *StreamError {
  75. grpcCode := codes.Unknown
  76. grpcMessage := err.Error()
  77. var grpcDetails []*any.Any
  78. if s, ok := status.FromError(err); ok {
  79. grpcCode = s.Code()
  80. grpcMessage = s.Message()
  81. grpcDetails = s.Proto().GetDetails()
  82. }
  83. httpCode := HTTPStatusFromCode(grpcCode)
  84. return &StreamError{
  85. GrpcCode: int32(grpcCode),
  86. HttpCode: int32(httpCode),
  87. Message: grpcMessage,
  88. HttpStatus: http.StatusText(httpCode),
  89. Details: grpcDetails,
  90. }
  91. }