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 }