github.com/moby/docker@v26.1.3+incompatible/api/server/httpstatus/status.go (about) 1 package httpstatus // import "github.com/docker/docker/api/server/httpstatus" 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 cerrdefs "github.com/containerd/containerd/errdefs" 9 "github.com/containerd/log" 10 "github.com/docker/distribution/registry/api/errcode" 11 "github.com/docker/docker/errdefs" 12 "google.golang.org/grpc/codes" 13 "google.golang.org/grpc/status" 14 ) 15 16 type causer interface { 17 Cause() error 18 } 19 20 // FromError retrieves status code from error message. 21 func FromError(err error) int { 22 if err == nil { 23 log.G(context.TODO()).WithError(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) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err): 51 statusCode = http.StatusInternalServerError 52 default: 53 statusCode = statusCodeFromGRPCError(err) 54 if statusCode != http.StatusInternalServerError { 55 return statusCode 56 } 57 statusCode = statusCodeFromContainerdError(err) 58 if statusCode != http.StatusInternalServerError { 59 return statusCode 60 } 61 statusCode = statusCodeFromDistributionError(err) 62 if statusCode != http.StatusInternalServerError { 63 return statusCode 64 } 65 if e, ok := err.(causer); ok { 66 return FromError(e.Cause()) 67 } 68 69 log.G(context.TODO()).WithFields(log.Fields{ 70 "module": "api", 71 "error": err, 72 "error_type": fmt.Sprintf("%T", err), 73 }).Debug("FIXME: Got an API for which error does not match any expected type!!!") 74 } 75 76 if statusCode == 0 { 77 statusCode = http.StatusInternalServerError 78 } 79 80 return statusCode 81 } 82 83 // statusCodeFromGRPCError returns status code according to gRPC error 84 func statusCodeFromGRPCError(err error) int { 85 switch status.Code(err) { 86 case codes.InvalidArgument: // code 3 87 return http.StatusBadRequest 88 case codes.NotFound: // code 5 89 return http.StatusNotFound 90 case codes.AlreadyExists: // code 6 91 return http.StatusConflict 92 case codes.PermissionDenied: // code 7 93 return http.StatusForbidden 94 case codes.FailedPrecondition: // code 9 95 return http.StatusBadRequest 96 case codes.Unauthenticated: // code 16 97 return http.StatusUnauthorized 98 case codes.OutOfRange: // code 11 99 return http.StatusBadRequest 100 case codes.Unimplemented: // code 12 101 return http.StatusNotImplemented 102 case codes.Unavailable: // code 14 103 return http.StatusServiceUnavailable 104 default: 105 // codes.Canceled(1) 106 // codes.Unknown(2) 107 // codes.DeadlineExceeded(4) 108 // codes.ResourceExhausted(8) 109 // codes.Aborted(10) 110 // codes.Internal(13) 111 // codes.DataLoss(15) 112 return http.StatusInternalServerError 113 } 114 } 115 116 // statusCodeFromDistributionError returns status code according to registry errcode 117 // code is loosely based on errcode.ServeJSON() in docker/distribution 118 func statusCodeFromDistributionError(err error) int { 119 switch errs := err.(type) { 120 case errcode.Errors: 121 if len(errs) < 1 { 122 return http.StatusInternalServerError 123 } 124 if _, ok := errs[0].(errcode.ErrorCoder); ok { 125 return statusCodeFromDistributionError(errs[0]) 126 } 127 case errcode.ErrorCoder: 128 return errs.ErrorCode().Descriptor().HTTPStatusCode 129 } 130 return http.StatusInternalServerError 131 } 132 133 // statusCodeFromContainerdError returns status code for containerd errors when 134 // consumed directly (not through gRPC) 135 func statusCodeFromContainerdError(err error) int { 136 switch { 137 case cerrdefs.IsInvalidArgument(err): 138 return http.StatusBadRequest 139 case cerrdefs.IsNotFound(err): 140 return http.StatusNotFound 141 case cerrdefs.IsAlreadyExists(err): 142 return http.StatusConflict 143 case cerrdefs.IsFailedPrecondition(err): 144 return http.StatusPreconditionFailed 145 case cerrdefs.IsUnavailable(err): 146 return http.StatusServiceUnavailable 147 case cerrdefs.IsNotImplemented(err): 148 return http.StatusNotImplemented 149 default: 150 return http.StatusInternalServerError 151 } 152 }