github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/api/server/httputils/errors.go (about)

     1  package httputils
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/docker/docker/api/errdefs"
     8  	"github.com/docker/docker/api/types"
     9  	"github.com/docker/docker/api/types/versions"
    10  	"github.com/gorilla/mux"
    11  	"github.com/sirupsen/logrus"
    12  	"google.golang.org/grpc"
    13  	"google.golang.org/grpc/codes"
    14  )
    15  
    16  type causer interface {
    17  	Cause() error
    18  }
    19  
    20  // GetHTTPErrorStatusCode retrieves status code from error message.
    21  func GetHTTPErrorStatusCode(err error) int {
    22  	if err == nil {
    23  		logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
    24  		return http.StatusInternalServerError
    25  	}
    26  
    27  	var statusCode int
    28  
    29  	// Stop right there
    30  	// Are you sure you should be adding a new error class here? Do one of the existing ones work?
    31  
    32  	// Note that the below functions are already checking the error causal chain for matches.
    33  	switch {
    34  	case errdefs.IsNotFound(err):
    35  		statusCode = http.StatusNotFound
    36  	case errdefs.IsInvalidParameter(err):
    37  		statusCode = http.StatusBadRequest
    38  	case errdefs.IsConflict(err):
    39  		statusCode = http.StatusConflict
    40  	case errdefs.IsUnauthorized(err):
    41  		statusCode = http.StatusUnauthorized
    42  	case errdefs.IsUnavailable(err):
    43  		statusCode = http.StatusServiceUnavailable
    44  	case errdefs.IsForbidden(err):
    45  		statusCode = http.StatusForbidden
    46  	case errdefs.IsNotModified(err):
    47  		statusCode = http.StatusNotModified
    48  	case errdefs.IsNotImplemented(err):
    49  		statusCode = http.StatusNotImplemented
    50  	case errdefs.IsSystem(err) || errdefs.IsUnknown(err):
    51  		statusCode = http.StatusInternalServerError
    52  	default:
    53  		statusCode = statusCodeFromGRPCError(err)
    54  		if statusCode != http.StatusInternalServerError {
    55  			return statusCode
    56  		}
    57  
    58  		if e, ok := err.(causer); ok {
    59  			return GetHTTPErrorStatusCode(e.Cause())
    60  		}
    61  
    62  		logrus.WithFields(logrus.Fields{
    63  			"module":     "api",
    64  			"error_type": fmt.Sprintf("%T", err),
    65  		}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
    66  	}
    67  
    68  	if statusCode == 0 {
    69  		statusCode = http.StatusInternalServerError
    70  	}
    71  
    72  	return statusCode
    73  }
    74  
    75  func apiVersionSupportsJSONErrors(version string) bool {
    76  	const firstAPIVersionWithJSONErrors = "1.23"
    77  	return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
    78  }
    79  
    80  // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
    81  // returns it in the response.
    82  func MakeErrorHandler(err error) http.HandlerFunc {
    83  	return func(w http.ResponseWriter, r *http.Request) {
    84  		statusCode := GetHTTPErrorStatusCode(err)
    85  		vars := mux.Vars(r)
    86  		if apiVersionSupportsJSONErrors(vars["version"]) {
    87  			response := &types.ErrorResponse{
    88  				Message: err.Error(),
    89  			}
    90  			WriteJSON(w, statusCode, response)
    91  		} else {
    92  			http.Error(w, grpc.ErrorDesc(err), statusCode)
    93  		}
    94  	}
    95  }
    96  
    97  // statusCodeFromGRPCError returns status code according to gRPC error
    98  func statusCodeFromGRPCError(err error) int {
    99  	switch grpc.Code(err) {
   100  	case codes.InvalidArgument: // code 3
   101  		return http.StatusBadRequest
   102  	case codes.NotFound: // code 5
   103  		return http.StatusNotFound
   104  	case codes.AlreadyExists: // code 6
   105  		return http.StatusConflict
   106  	case codes.PermissionDenied: // code 7
   107  		return http.StatusForbidden
   108  	case codes.FailedPrecondition: // code 9
   109  		return http.StatusBadRequest
   110  	case codes.Unauthenticated: // code 16
   111  		return http.StatusUnauthorized
   112  	case codes.OutOfRange: // code 11
   113  		return http.StatusBadRequest
   114  	case codes.Unimplemented: // code 12
   115  		return http.StatusNotImplemented
   116  	case codes.Unavailable: // code 14
   117  		return http.StatusServiceUnavailable
   118  	default:
   119  		if e, ok := err.(causer); ok {
   120  			return statusCodeFromGRPCError(e.Cause())
   121  		}
   122  		// codes.Canceled(1)
   123  		// codes.Unknown(2)
   124  		// codes.DeadlineExceeded(4)
   125  		// codes.ResourceExhausted(8)
   126  		// codes.Aborted(10)
   127  		// codes.Internal(13)
   128  		// codes.DataLoss(15)
   129  		return http.StatusInternalServerError
   130  	}
   131  }