gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

errors.go (6269B)


      1 package runtime
      2 
      3 import (
      4 	"context"
      5 	"errors"
      6 	"io"
      7 	"net/http"
      8 
      9 	"google.golang.org/grpc/codes"
     10 	"google.golang.org/grpc/grpclog"
     11 	"google.golang.org/grpc/status"
     12 )
     13 
     14 // ErrorHandlerFunc is the signature used to configure error handling.
     15 type ErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error)
     16 
     17 // StreamErrorHandlerFunc is the signature used to configure stream error handling.
     18 type StreamErrorHandlerFunc func(context.Context, error) *status.Status
     19 
     20 // RoutingErrorHandlerFunc is the signature used to configure error handling for routing errors.
     21 type RoutingErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, int)
     22 
     23 // HTTPStatusError is the error to use when needing to provide a different HTTP status code for an error
     24 // passed to the DefaultRoutingErrorHandler.
     25 type HTTPStatusError struct {
     26 	HTTPStatus int
     27 	Err        error
     28 }
     29 
     30 func (e *HTTPStatusError) Error() string {
     31 	return e.Err.Error()
     32 }
     33 
     34 // HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status.
     35 // See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
     36 func HTTPStatusFromCode(code codes.Code) int {
     37 	switch code {
     38 	case codes.OK:
     39 		return http.StatusOK
     40 	case codes.Canceled:
     41 		return http.StatusRequestTimeout
     42 	case codes.Unknown:
     43 		return http.StatusInternalServerError
     44 	case codes.InvalidArgument:
     45 		return http.StatusBadRequest
     46 	case codes.DeadlineExceeded:
     47 		return http.StatusGatewayTimeout
     48 	case codes.NotFound:
     49 		return http.StatusNotFound
     50 	case codes.AlreadyExists:
     51 		return http.StatusConflict
     52 	case codes.PermissionDenied:
     53 		return http.StatusForbidden
     54 	case codes.Unauthenticated:
     55 		return http.StatusUnauthorized
     56 	case codes.ResourceExhausted:
     57 		return http.StatusTooManyRequests
     58 	case codes.FailedPrecondition:
     59 		// Note, this deliberately doesn't translate to the similarly named '412 Precondition Failed' HTTP response status.
     60 		return http.StatusBadRequest
     61 	case codes.Aborted:
     62 		return http.StatusConflict
     63 	case codes.OutOfRange:
     64 		return http.StatusBadRequest
     65 	case codes.Unimplemented:
     66 		return http.StatusNotImplemented
     67 	case codes.Internal:
     68 		return http.StatusInternalServerError
     69 	case codes.Unavailable:
     70 		return http.StatusServiceUnavailable
     71 	case codes.DataLoss:
     72 		return http.StatusInternalServerError
     73 	}
     74 
     75 	grpclog.Infof("Unknown gRPC error code: %v", code)
     76 	return http.StatusInternalServerError
     77 }
     78 
     79 // HTTPError uses the mux-configured error handler.
     80 func HTTPError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
     81 	mux.errorHandler(ctx, mux, marshaler, w, r, err)
     82 }
     83 
     84 // DefaultHTTPErrorHandler is the default error handler.
     85 // If "err" is a gRPC Status, the function replies with the status code mapped by HTTPStatusFromCode.
     86 // If "err" is a HTTPStatusError, the function replies with the status code provide by that struct. This is
     87 // intended to allow passing through of specific statuses via the function set via WithRoutingErrorHandler
     88 // for the ServeMux constructor to handle edge cases which the standard mappings in HTTPStatusFromCode
     89 // are insufficient for.
     90 // If otherwise, it replies with http.StatusInternalServerError.
     91 //
     92 // The response body written by this function is a Status message marshaled by the Marshaler.
     93 func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
     94 	// return Internal when Marshal failed
     95 	const fallback = `{"code": 13, "message": "failed to marshal error message"}`
     96 
     97 	var customStatus *HTTPStatusError
     98 	if errors.As(err, &customStatus) {
     99 		err = customStatus.Err
    100 	}
    101 
    102 	s := status.Convert(err)
    103 	pb := s.Proto()
    104 
    105 	w.Header().Del("Trailer")
    106 	w.Header().Del("Transfer-Encoding")
    107 
    108 	contentType := marshaler.ContentType(pb)
    109 	w.Header().Set("Content-Type", contentType)
    110 
    111 	if s.Code() == codes.Unauthenticated {
    112 		w.Header().Set("WWW-Authenticate", s.Message())
    113 	}
    114 
    115 	buf, merr := marshaler.Marshal(pb)
    116 	if merr != nil {
    117 		grpclog.Infof("Failed to marshal error message %q: %v", s, merr)
    118 		w.WriteHeader(http.StatusInternalServerError)
    119 		if _, err := io.WriteString(w, fallback); err != nil {
    120 			grpclog.Infof("Failed to write response: %v", err)
    121 		}
    122 		return
    123 	}
    124 
    125 	md, ok := ServerMetadataFromContext(ctx)
    126 	if !ok {
    127 		grpclog.Infof("Failed to extract ServerMetadata from context")
    128 	}
    129 
    130 	handleForwardResponseServerMetadata(w, mux, md)
    131 
    132 	// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
    133 	// Unless the request includes a TE header field indicating "trailers"
    134 	// is acceptable, as described in Section 4.3, a server SHOULD NOT
    135 	// generate trailer fields that it believes are necessary for the user
    136 	// agent to receive.
    137 	doForwardTrailers := requestAcceptsTrailers(r)
    138 
    139 	if doForwardTrailers {
    140 		handleForwardResponseTrailerHeader(w, md)
    141 		w.Header().Set("Transfer-Encoding", "chunked")
    142 	}
    143 
    144 	st := HTTPStatusFromCode(s.Code())
    145 	if customStatus != nil {
    146 		st = customStatus.HTTPStatus
    147 	}
    148 
    149 	w.WriteHeader(st)
    150 	if _, err := w.Write(buf); err != nil {
    151 		grpclog.Infof("Failed to write response: %v", err)
    152 	}
    153 
    154 	if doForwardTrailers {
    155 		handleForwardResponseTrailer(w, md)
    156 	}
    157 }
    158 
    159 func DefaultStreamErrorHandler(_ context.Context, err error) *status.Status {
    160 	return status.Convert(err)
    161 }
    162 
    163 // DefaultRoutingErrorHandler is our default handler for routing errors.
    164 // By default http error codes mapped on the following error codes:
    165 //   NotFound -> grpc.NotFound
    166 //   StatusBadRequest -> grpc.InvalidArgument
    167 //   MethodNotAllowed -> grpc.Unimplemented
    168 //   Other -> grpc.Internal, method is not expecting to be called for anything else
    169 func DefaultRoutingErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
    170 	sterr := status.Error(codes.Internal, "Unexpected routing error")
    171 	switch httpStatus {
    172 	case http.StatusBadRequest:
    173 		sterr = status.Error(codes.InvalidArgument, http.StatusText(httpStatus))
    174 	case http.StatusMethodNotAllowed:
    175 		sterr = status.Error(codes.Unimplemented, http.StatusText(httpStatus))
    176 	case http.StatusNotFound:
    177 		sterr = status.Error(codes.NotFound, http.StatusText(httpStatus))
    178 	}
    179 	mux.errorHandler(ctx, mux, marshaler, w, r, sterr)
    180 }