package runtime
import (
"context"
"errors"
"io"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
type ErrorHandlerFunc func (context .Context , *ServeMux , Marshaler , http .ResponseWriter , *http .Request , error )
type StreamErrorHandlerFunc func (context .Context , error ) *status .Status
type RoutingErrorHandlerFunc func (context .Context , *ServeMux , Marshaler , http .ResponseWriter , *http .Request , int )
type HTTPStatusError struct {
HTTPStatus int
Err error
}
func (e *HTTPStatusError ) Error () string {
return e .Err .Error()
}
func HTTPStatusFromCode (code codes .Code ) int {
switch code {
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 :
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" , code )
return http .StatusInternalServerError
}
}
func HTTPError (ctx context .Context , mux *ServeMux , marshaler Marshaler , w http .ResponseWriter , r *http .Request , err error ) {
mux .errorHandler (ctx , mux , marshaler , w , r , err )
}
func HTTPStreamError (ctx context .Context , mux *ServeMux , marshaler Marshaler , w http .ResponseWriter , r *http .Request , err error ) {
st := mux .streamErrorHandler (ctx , err )
msg := errorChunk (st )
buf , err := marshaler .Marshal (msg )
if err != nil {
grpclog .Errorf ("Failed to marshal an error: %v" , err )
return
}
if _ , err := w .Write (buf ); err != nil {
grpclog .Errorf ("Failed to notify error to client: %v" , err )
return
}
}
func DefaultHTTPErrorHandler (ctx context .Context , mux *ServeMux , marshaler Marshaler , w http .ResponseWriter , r *http .Request , err error ) {
const fallback = `{"code": 13, "message": "failed to marshal error message"}`
const fallbackRewriter = `{"code": 13, "message": "failed to rewrite error message"}`
var customStatus *HTTPStatusError
if errors .As (err , &customStatus ) {
err = customStatus .Err
}
s := status .Convert (err )
w .Header ().Del ("Trailer" )
w .Header ().Del ("Transfer-Encoding" )
respRw , err := mux .forwardResponseRewriter (ctx , s .Proto ())
if err != nil {
grpclog .Errorf ("Failed to rewrite error message %q: %v" , s , err )
w .WriteHeader (http .StatusInternalServerError )
if _ , err := io .WriteString (w , fallbackRewriter ); err != nil {
grpclog .Errorf ("Failed to write response: %v" , err )
}
return
}
contentType := marshaler .ContentType (respRw )
w .Header ().Set ("Content-Type" , contentType )
if s .Code () == codes .Unauthenticated {
w .Header ().Set ("WWW-Authenticate" , s .Message ())
}
buf , merr := marshaler .Marshal (respRw )
if merr != nil {
grpclog .Errorf ("Failed to marshal error message %q: %v" , s , merr )
w .WriteHeader (http .StatusInternalServerError )
if _ , err := io .WriteString (w , fallback ); err != nil {
grpclog .Errorf ("Failed to write response: %v" , err )
}
return
}
md , ok := ServerMetadataFromContext (ctx )
if ok {
handleForwardResponseServerMetadata (w , mux , md )
doForwardTrailers := requestAcceptsTrailers (r )
if doForwardTrailers {
handleForwardResponseTrailerHeader (w , mux , md )
w .Header ().Set ("Transfer-Encoding" , "chunked" )
}
}
st := HTTPStatusFromCode (s .Code ())
if customStatus != nil {
st = customStatus .HTTPStatus
}
w .WriteHeader (st )
if _ , err := w .Write (buf ); err != nil {
grpclog .Errorf ("Failed to write response: %v" , err )
}
if ok && requestAcceptsTrailers (r ) {
handleForwardResponseTrailer (w , mux , md )
}
}
func DefaultStreamErrorHandler (_ context .Context , err error ) *status .Status {
return status .Convert (err )
}
func DefaultRoutingErrorHandler (ctx context .Context , mux *ServeMux , marshaler Marshaler , w http .ResponseWriter , r *http .Request , httpStatus int ) {
sterr := status .Error (codes .Internal , "Unexpected routing error" )
switch httpStatus {
case http .StatusBadRequest :
sterr = status .Error (codes .InvalidArgument , http .StatusText (httpStatus ))
case http .StatusMethodNotAllowed :
sterr = status .Error (codes .Unimplemented , http .StatusText (httpStatus ))
case http .StatusNotFound :
sterr = status .Error (codes .NotFound , http .StatusText (httpStatus ))
}
mux .errorHandler (ctx , mux , marshaler , w , r , sterr )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .