github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/errdefs/http_helpers.go (about)

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