github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/errdefs/http_helpers.go (about)

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