github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/api/server/httpstatus/status.go (about)

     1  package httpstatus // import "github.com/docker/docker/api/server/httpstatus"
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	containerderrors "github.com/containerd/containerd/errdefs"
     8  	"github.com/docker/distribution/registry/api/errcode"
     9  	"github.com/docker/docker/errdefs"
    10  	"github.com/sirupsen/logrus"
    11  	"google.golang.org/grpc/codes"
    12  	"google.golang.org/grpc/status"
    13  )
    14  
    15  type causer interface {
    16  	Cause() error
    17  }
    18  
    19  // FromError retrieves status code from error message.
    20  func FromError(err error) int {
    21  	if err == nil {
    22  		logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
    23  		return http.StatusInternalServerError
    24  	}
    25  
    26  	var statusCode int
    27  
    28  	// Stop right there
    29  	// Are you sure you should be adding a new error class here? Do one of the existing ones work?
    30  
    31  	// Note that the below functions are already checking the error causal chain for matches.
    32  	switch {
    33  	case errdefs.IsNotFound(err):
    34  		statusCode = http.StatusNotFound
    35  	case errdefs.IsInvalidParameter(err):
    36  		statusCode = http.StatusBadRequest
    37  	case errdefs.IsConflict(err):
    38  		statusCode = http.StatusConflict
    39  	case errdefs.IsUnauthorized(err):
    40  		statusCode = http.StatusUnauthorized
    41  	case errdefs.IsUnavailable(err):
    42  		statusCode = http.StatusServiceUnavailable
    43  	case errdefs.IsForbidden(err):
    44  		statusCode = http.StatusForbidden
    45  	case errdefs.IsNotModified(err):
    46  		statusCode = http.StatusNotModified
    47  	case errdefs.IsNotImplemented(err):
    48  		statusCode = http.StatusNotImplemented
    49  	case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
    50  		statusCode = http.StatusInternalServerError
    51  	default:
    52  		statusCode = statusCodeFromGRPCError(err)
    53  		if statusCode != http.StatusInternalServerError {
    54  			return statusCode
    55  		}
    56  		statusCode = statusCodeFromContainerdError(err)
    57  		if statusCode != http.StatusInternalServerError {
    58  			return statusCode
    59  		}
    60  		statusCode = statusCodeFromDistributionError(err)
    61  		if statusCode != http.StatusInternalServerError {
    62  			return statusCode
    63  		}
    64  		if e, ok := err.(causer); ok {
    65  			return FromError(e.Cause())
    66  		}
    67  
    68  		logrus.WithFields(logrus.Fields{
    69  			"module":     "api",
    70  			"error_type": fmt.Sprintf("%T", err),
    71  		}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
    72  	}
    73  
    74  	if statusCode == 0 {
    75  		statusCode = http.StatusInternalServerError
    76  	}
    77  
    78  	return statusCode
    79  }
    80  
    81  // statusCodeFromGRPCError returns status code according to gRPC error
    82  func statusCodeFromGRPCError(err error) int {
    83  	switch status.Code(err) {
    84  	case codes.InvalidArgument: // code 3
    85  		return http.StatusBadRequest
    86  	case codes.NotFound: // code 5
    87  		return http.StatusNotFound
    88  	case codes.AlreadyExists: // code 6
    89  		return http.StatusConflict
    90  	case codes.PermissionDenied: // code 7
    91  		return http.StatusForbidden
    92  	case codes.FailedPrecondition: // code 9
    93  		return http.StatusBadRequest
    94  	case codes.Unauthenticated: // code 16
    95  		return http.StatusUnauthorized
    96  	case codes.OutOfRange: // code 11
    97  		return http.StatusBadRequest
    98  	case codes.Unimplemented: // code 12
    99  		return http.StatusNotImplemented
   100  	case codes.Unavailable: // code 14
   101  		return http.StatusServiceUnavailable
   102  	default:
   103  		// codes.Canceled(1)
   104  		// codes.Unknown(2)
   105  		// codes.DeadlineExceeded(4)
   106  		// codes.ResourceExhausted(8)
   107  		// codes.Aborted(10)
   108  		// codes.Internal(13)
   109  		// codes.DataLoss(15)
   110  		return http.StatusInternalServerError
   111  	}
   112  }
   113  
   114  // statusCodeFromDistributionError returns status code according to registry errcode
   115  // code is loosely based on errcode.ServeJSON() in docker/distribution
   116  func statusCodeFromDistributionError(err error) int {
   117  	switch errs := err.(type) {
   118  	case errcode.Errors:
   119  		if len(errs) < 1 {
   120  			return http.StatusInternalServerError
   121  		}
   122  		if _, ok := errs[0].(errcode.ErrorCoder); ok {
   123  			return statusCodeFromDistributionError(errs[0])
   124  		}
   125  	case errcode.ErrorCoder:
   126  		return errs.ErrorCode().Descriptor().HTTPStatusCode
   127  	}
   128  	return http.StatusInternalServerError
   129  }
   130  
   131  // statusCodeFromContainerdError returns status code for containerd errors when
   132  // consumed directly (not through gRPC)
   133  func statusCodeFromContainerdError(err error) int {
   134  	switch {
   135  	case containerderrors.IsInvalidArgument(err):
   136  		return http.StatusBadRequest
   137  	case containerderrors.IsNotFound(err):
   138  		return http.StatusNotFound
   139  	case containerderrors.IsAlreadyExists(err):
   140  		return http.StatusConflict
   141  	case containerderrors.IsFailedPrecondition(err):
   142  		return http.StatusPreconditionFailed
   143  	case containerderrors.IsUnavailable(err):
   144  		return http.StatusServiceUnavailable
   145  	case containerderrors.IsNotImplemented(err):
   146  		return http.StatusNotImplemented
   147  	default:
   148  		return http.StatusInternalServerError
   149  	}
   150  }