package runtime import ( "io" "net/http" "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/status" ) // ProtoErrorHandlerFunc handles the error as a gRPC error generated via status package and replies to the request. type ProtoErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error) var _ ProtoErrorHandlerFunc = DefaultHTTPProtoErrorHandler // DefaultHTTPProtoErrorHandler is an implementation of HTTPError. // If "err" is an error from gRPC system, the function replies with the status code mapped by HTTPStatusFromCode. // If otherwise, it replies with http.StatusInternalServerError. // // The response body returned by this function is a Status message marshaled by a Marshaler. // // Do not set this function to HTTPError variable directly, use WithProtoErrorHandler option instead. func DefaultHTTPProtoErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, _ *http.Request, err error) { // return Internal when Marshal failed const fallback = `{"code": 13, "message": "failed to marshal error message"}` w.Header().Del("Trailer") w.Header().Set("Content-Type", marshaler.ContentType()) s, ok := status.FromError(err) if !ok { s = status.New(codes.Unknown, err.Error()) } buf, merr := marshaler.Marshal(s.Proto()) if merr != nil { grpclog.Printf("Failed to marshal error message %q: %v", s.Proto(), merr) w.WriteHeader(http.StatusInternalServerError) if _, err := io.WriteString(w, fallback); err != nil { grpclog.Printf("Failed to write response: %v", err) } return } md, ok := ServerMetadataFromContext(ctx) if !ok { grpclog.Printf("Failed to extract ServerMetadata from context") } handleForwardResponseServerMetadata(w, mux, md) handleForwardResponseTrailerHeader(w, md) st := HTTPStatusFromCode(s.Code()) w.WriteHeader(st) if _, err := w.Write(buf); err != nil { grpclog.Printf("Failed to write response: %v", err) } handleForwardResponseTrailer(w, md) }