...

Source file src/github.com/grpc-ecosystem/grpc-gateway/runtime/proto_errors.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/runtime

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

View as plain text