blob: b7fa32e45dbe4d0523bc5cd35a2aec5fb776b265 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001package runtime
2
3import (
4 "io"
5 "net/http"
6
7 "context"
8 "google.golang.org/grpc/codes"
9 "google.golang.org/grpc/grpclog"
10 "google.golang.org/grpc/status"
11)
12
13// ProtoErrorHandlerFunc handles the error as a gRPC error generated via status package and replies to the request.
14type ProtoErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error)
15
16var _ ProtoErrorHandlerFunc = DefaultHTTPProtoErrorHandler
17
18// DefaultHTTPProtoErrorHandler is an implementation of HTTPError.
19// If "err" is an error from gRPC system, the function replies with the status code mapped by HTTPStatusFromCode.
20// If otherwise, it replies with http.StatusInternalServerError.
21//
22// The response body returned by this function is a Status message marshaled by a Marshaler.
23//
24// Do not set this function to HTTPError variable directly, use WithProtoErrorHandler option instead.
25func DefaultHTTPProtoErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
26 // return Internal when Marshal failed
27 const fallback = `{"code": 13, "message": "failed to marshal error message"}`
28
29 s, ok := status.FromError(err)
30 if !ok {
31 s = status.New(codes.Unknown, err.Error())
32 }
33
34 w.Header().Del("Trailer")
35
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
46 buf, merr := marshaler.Marshal(s.Proto())
47 if merr != nil {
48 grpclog.Infof("Failed to marshal error message %q: %v", s.Proto(), merr)
49 w.WriteHeader(http.StatusInternalServerError)
50 if _, err := io.WriteString(w, fallback); err != nil {
51 grpclog.Infof("Failed to write response: %v", err)
52 }
53 return
54 }
55
56 md, ok := ServerMetadataFromContext(ctx)
57 if !ok {
58 grpclog.Infof("Failed to extract ServerMetadata from context")
59 }
60
61 handleForwardResponseServerMetadata(w, mux, md)
62 handleForwardResponseTrailerHeader(w, md)
63 st := HTTPStatusFromCode(s.Code())
64 w.WriteHeader(st)
65 if _, err := w.Write(buf); err != nil {
66 grpclog.Infof("Failed to write response: %v", err)
67 }
68
69 handleForwardResponseTrailer(w, md)
70}