errors.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package httputils // import "github.com/docker/docker/api/server/httputils"
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/docker/docker/api/types"
  6. "github.com/docker/docker/api/types/versions"
  7. "github.com/docker/docker/errdefs"
  8. "github.com/gorilla/mux"
  9. "github.com/sirupsen/logrus"
  10. "google.golang.org/grpc"
  11. "google.golang.org/grpc/codes"
  12. )
  13. type causer interface {
  14. Cause() error
  15. }
  16. // GetHTTPErrorStatusCode retrieves status code from error message.
  17. func GetHTTPErrorStatusCode(err error) int {
  18. if err == nil {
  19. logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
  20. return http.StatusInternalServerError
  21. }
  22. var statusCode int
  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. // Note that the below functions are already checking the error causal chain for matches.
  26. switch {
  27. case errdefs.IsNotFound(err):
  28. statusCode = http.StatusNotFound
  29. case errdefs.IsInvalidParameter(err):
  30. statusCode = http.StatusBadRequest
  31. case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err):
  32. statusCode = http.StatusConflict
  33. case errdefs.IsUnauthorized(err):
  34. statusCode = http.StatusUnauthorized
  35. case errdefs.IsUnavailable(err):
  36. statusCode = http.StatusServiceUnavailable
  37. case errdefs.IsForbidden(err):
  38. statusCode = http.StatusForbidden
  39. case errdefs.IsNotModified(err):
  40. statusCode = http.StatusNotModified
  41. case errdefs.IsNotImplemented(err):
  42. statusCode = http.StatusNotImplemented
  43. case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
  44. statusCode = http.StatusInternalServerError
  45. default:
  46. statusCode = statusCodeFromGRPCError(err)
  47. if statusCode != http.StatusInternalServerError {
  48. return statusCode
  49. }
  50. if e, ok := err.(causer); ok {
  51. return GetHTTPErrorStatusCode(e.Cause())
  52. }
  53. logrus.WithFields(logrus.Fields{
  54. "module": "api",
  55. "error_type": fmt.Sprintf("%T", err),
  56. }).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
  57. }
  58. if statusCode == 0 {
  59. statusCode = http.StatusInternalServerError
  60. }
  61. return statusCode
  62. }
  63. func apiVersionSupportsJSONErrors(version string) bool {
  64. const firstAPIVersionWithJSONErrors = "1.23"
  65. return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
  66. }
  67. // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
  68. // returns it in the response.
  69. func MakeErrorHandler(err error) http.HandlerFunc {
  70. return func(w http.ResponseWriter, r *http.Request) {
  71. statusCode := GetHTTPErrorStatusCode(err)
  72. vars := mux.Vars(r)
  73. if apiVersionSupportsJSONErrors(vars["version"]) {
  74. response := &types.ErrorResponse{
  75. Message: err.Error(),
  76. }
  77. WriteJSON(w, statusCode, response)
  78. } else {
  79. http.Error(w, grpc.ErrorDesc(err), statusCode)
  80. }
  81. }
  82. }
  83. // statusCodeFromGRPCError returns status code according to gRPC error
  84. func statusCodeFromGRPCError(err error) int {
  85. switch grpc.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. if e, ok := err.(causer); ok {
  106. return statusCodeFromGRPCError(e.Cause())
  107. }
  108. // codes.Canceled(1)
  109. // codes.Unknown(2)
  110. // codes.DeadlineExceeded(4)
  111. // codes.ResourceExhausted(8)
  112. // codes.Aborted(10)
  113. // codes.Internal(13)
  114. // codes.DataLoss(15)
  115. return http.StatusInternalServerError
  116. }
  117. }