github.com/afbjorklund/moby@v20.10.5+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 }