errors.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package httputils
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/docker/docker/api/errdefs"
  6. "github.com/docker/docker/api/types"
  7. "github.com/docker/docker/api/types/versions"
  8. "github.com/gorilla/mux"
  9. "github.com/sirupsen/logrus"
  10. "google.golang.org/grpc"
  11. "google.golang.org/grpc/codes"
  12. )
  13. // httpStatusError is an interface
  14. // that errors with custom status codes
  15. // implement to tell the api layer
  16. // which response status to set.
  17. type httpStatusError interface {
  18. HTTPErrorStatusCode() int
  19. }
  20. type causer interface {
  21. Cause() error
  22. }
  23. // GetHTTPErrorStatusCode retrieves status code from error message.
  24. func GetHTTPErrorStatusCode(err error) int {
  25. if err == nil {
  26. logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
  27. return http.StatusInternalServerError
  28. }
  29. var statusCode int
  30. // Stop right there
  31. // Are you sure you should be adding a new error class here? Do one of the existing ones work?
  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):
  51. statusCode = http.StatusInternalServerError
  52. default:
  53. statusCode = statusCodeFromGRPCError(err)
  54. if statusCode != http.StatusInternalServerError {
  55. return statusCode
  56. }
  57. if e, ok := err.(causer); ok {
  58. return GetHTTPErrorStatusCode(e.Cause())
  59. }
  60. logrus.WithFields(logrus.Fields{
  61. "module": "api",
  62. "error_type": fmt.Sprintf("%T", err),
  63. }).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
  64. }
  65. if statusCode == 0 {
  66. statusCode = http.StatusInternalServerError
  67. }
  68. return statusCode
  69. }
  70. func apiVersionSupportsJSONErrors(version string) bool {
  71. const firstAPIVersionWithJSONErrors = "1.23"
  72. return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
  73. }
  74. // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
  75. // returns it in the response.
  76. func MakeErrorHandler(err error) http.HandlerFunc {
  77. return func(w http.ResponseWriter, r *http.Request) {
  78. statusCode := GetHTTPErrorStatusCode(err)
  79. vars := mux.Vars(r)
  80. if apiVersionSupportsJSONErrors(vars["version"]) {
  81. response := &types.ErrorResponse{
  82. Message: err.Error(),
  83. }
  84. WriteJSON(w, statusCode, response)
  85. } else {
  86. http.Error(w, grpc.ErrorDesc(err), statusCode)
  87. }
  88. }
  89. }
  90. // statusCodeFromGRPCError returns status code according to gRPC error
  91. func statusCodeFromGRPCError(err error) int {
  92. switch grpc.Code(err) {
  93. case codes.InvalidArgument: // code 3
  94. return http.StatusBadRequest
  95. case codes.NotFound: // code 5
  96. return http.StatusNotFound
  97. case codes.AlreadyExists: // code 6
  98. return http.StatusConflict
  99. case codes.PermissionDenied: // code 7
  100. return http.StatusForbidden
  101. case codes.FailedPrecondition: // code 9
  102. return http.StatusBadRequest
  103. case codes.Unauthenticated: // code 16
  104. return http.StatusUnauthorized
  105. case codes.OutOfRange: // code 11
  106. return http.StatusBadRequest
  107. case codes.Unimplemented: // code 12
  108. return http.StatusNotImplemented
  109. case codes.Unavailable: // code 14
  110. return http.StatusServiceUnavailable
  111. default:
  112. if e, ok := err.(causer); ok {
  113. return statusCodeFromGRPCError(e.Cause())
  114. }
  115. // codes.Canceled(1)
  116. // codes.Unknown(2)
  117. // codes.DeadlineExceeded(4)
  118. // codes.ResourceExhausted(8)
  119. // codes.Aborted(10)
  120. // codes.Internal(13)
  121. // codes.DataLoss(15)
  122. return http.StatusInternalServerError
  123. }
  124. }