http_helpers.go 5.7 KB

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