package runtime

import (
	
	
	
	

	
	
	
)

// ErrorHandlerFunc is the signature used to configure error handling.
type ErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error)

// StreamErrorHandlerFunc is the signature used to configure stream error handling.
type StreamErrorHandlerFunc func(context.Context, error) *status.Status

// RoutingErrorHandlerFunc is the signature used to configure error handling for routing errors.
type RoutingErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, int)

// HTTPStatusError is the error to use when needing to provide a different HTTP status code for an error
// passed to the DefaultRoutingErrorHandler.
type HTTPStatusError struct {
	HTTPStatus int
	Err        error
}

func ( *HTTPStatusError) () string {
	return .Err.Error()
}

// HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status.
// See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
func ( codes.Code) int {
	switch  {
	case codes.OK:
		return http.StatusOK
	case codes.Canceled:
		return 499
	case codes.Unknown:
		return http.StatusInternalServerError
	case codes.InvalidArgument:
		return http.StatusBadRequest
	case codes.DeadlineExceeded:
		return http.StatusGatewayTimeout
	case codes.NotFound:
		return http.StatusNotFound
	case codes.AlreadyExists:
		return http.StatusConflict
	case codes.PermissionDenied:
		return http.StatusForbidden
	case codes.Unauthenticated:
		return http.StatusUnauthorized
	case codes.ResourceExhausted:
		return http.StatusTooManyRequests
	case codes.FailedPrecondition:
		// Note, this deliberately doesn't translate to the similarly named '412 Precondition Failed' HTTP response status.
		return http.StatusBadRequest
	case codes.Aborted:
		return http.StatusConflict
	case codes.OutOfRange:
		return http.StatusBadRequest
	case codes.Unimplemented:
		return http.StatusNotImplemented
	case codes.Internal:
		return http.StatusInternalServerError
	case codes.Unavailable:
		return http.StatusServiceUnavailable
	case codes.DataLoss:
		return http.StatusInternalServerError
	default:
		grpclog.Warningf("Unknown gRPC error code: %v", )
		return http.StatusInternalServerError
	}
}

// HTTPError uses the mux-configured error handler.
func ( context.Context,  *ServeMux,  Marshaler,  http.ResponseWriter,  *http.Request,  error) {
	.errorHandler(, , , , , )
}

// HTTPStreamError uses the mux-configured stream error handler to notify error to the client without closing the connection.
func ( context.Context,  *ServeMux,  Marshaler,  http.ResponseWriter,  *http.Request,  error) {
	 := .streamErrorHandler(, )
	 := errorChunk()
	,  := .Marshal()
	if  != nil {
		grpclog.Errorf("Failed to marshal an error: %v", )
		return
	}
	if ,  := .Write();  != nil {
		grpclog.Errorf("Failed to notify error to client: %v", )
		return
	}
}

// DefaultHTTPErrorHandler is the default error handler.
// If "err" is a gRPC Status, the function replies with the status code mapped by HTTPStatusFromCode.
// If "err" is a HTTPStatusError, the function replies with the status code provide by that struct. This is
// intended to allow passing through of specific statuses via the function set via WithRoutingErrorHandler
// for the ServeMux constructor to handle edge cases which the standard mappings in HTTPStatusFromCode
// are insufficient for.
// If otherwise, it replies with http.StatusInternalServerError.
//
// The response body written by this function is a Status message marshaled by the Marshaler.
func ( context.Context,  *ServeMux,  Marshaler,  http.ResponseWriter,  *http.Request,  error) {
	// return Internal when Marshal failed
	const  = `{"code": 13, "message": "failed to marshal error message"}`
	const  = `{"code": 13, "message": "failed to rewrite error message"}`

	var  *HTTPStatusError
	if errors.As(, &) {
		 = .Err
	}

	 := status.Convert()

	.Header().Del("Trailer")
	.Header().Del("Transfer-Encoding")

	,  := .forwardResponseRewriter(, .Proto())
	if  != nil {
		grpclog.Errorf("Failed to rewrite error message %q: %v", , )
		.WriteHeader(http.StatusInternalServerError)
		if ,  := io.WriteString(, );  != nil {
			grpclog.Errorf("Failed to write response: %v", )
		}
		return
	}

	 := .ContentType()
	.Header().Set("Content-Type", )

	if .Code() == codes.Unauthenticated {
		.Header().Set("WWW-Authenticate", .Message())
	}

	,  := .Marshal()
	if  != nil {
		grpclog.Errorf("Failed to marshal error message %q: %v", , )
		.WriteHeader(http.StatusInternalServerError)
		if ,  := io.WriteString(, );  != nil {
			grpclog.Errorf("Failed to write response: %v", )
		}
		return
	}

	,  := ServerMetadataFromContext()
	if  {
		handleForwardResponseServerMetadata(, , )

		// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
		// Unless the request includes a TE header field indicating "trailers"
		// is acceptable, as described in Section 4.3, a server SHOULD NOT
		// generate trailer fields that it believes are necessary for the user
		// agent to receive.
		 := requestAcceptsTrailers()

		if  {
			handleForwardResponseTrailerHeader(, , )
			.Header().Set("Transfer-Encoding", "chunked")
		}
	}

	 := HTTPStatusFromCode(.Code())
	if  != nil {
		 = .HTTPStatus
	}

	.WriteHeader()
	if ,  := .Write();  != nil {
		grpclog.Errorf("Failed to write response: %v", )
	}

	if  && requestAcceptsTrailers() {
		handleForwardResponseTrailer(, , )
	}
}

func ( context.Context,  error) *status.Status {
	return status.Convert()
}

// DefaultRoutingErrorHandler is our default handler for routing errors.
// By default http error codes mapped on the following error codes:
//
//	NotFound -> grpc.NotFound
//	StatusBadRequest -> grpc.InvalidArgument
//	MethodNotAllowed -> grpc.Unimplemented
//	Other -> grpc.Internal, method is not expecting to be called for anything else
func ( context.Context,  *ServeMux,  Marshaler,  http.ResponseWriter,  *http.Request,  int) {
	 := status.Error(codes.Internal, "Unexpected routing error")
	switch  {
	case http.StatusBadRequest:
		 = status.Error(codes.InvalidArgument, http.StatusText())
	case http.StatusMethodNotAllowed:
		 = status.Error(codes.Unimplemented, http.StatusText())
	case http.StatusNotFound:
		 = status.Error(codes.NotFound, http.StatusText())
	}
	.errorHandler(, , , , , )
}