errors.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package httputils
  2. import (
  3. "net/http"
  4. "strings"
  5. "github.com/Sirupsen/logrus"
  6. "github.com/docker/engine-api/types"
  7. "github.com/docker/engine-api/types/versions"
  8. "github.com/gorilla/mux"
  9. "google.golang.org/grpc"
  10. )
  11. // httpStatusError is an interface
  12. // that errors with custom status codes
  13. // implement to tell the api layer
  14. // which response status to set.
  15. type httpStatusError interface {
  16. HTTPErrorStatusCode() int
  17. }
  18. // inputValidationError is an interface
  19. // that errors generated by invalid
  20. // inputs can implement to tell the
  21. // api layer to set a 400 status code
  22. // in the response.
  23. type inputValidationError interface {
  24. IsValidationError() bool
  25. }
  26. // GetHTTPErrorStatusCode retrieve status code from error message
  27. func GetHTTPErrorStatusCode(err error) int {
  28. if err == nil {
  29. logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
  30. return http.StatusInternalServerError
  31. }
  32. var statusCode int
  33. errMsg := err.Error()
  34. switch e := err.(type) {
  35. case httpStatusError:
  36. statusCode = e.HTTPErrorStatusCode()
  37. case inputValidationError:
  38. statusCode = http.StatusBadRequest
  39. default:
  40. // FIXME: this is brittle and should not be necessary, but we still need to identify if
  41. // there are errors falling back into this logic.
  42. // If we need to differentiate between different possible error types,
  43. // we should create appropriate error types that implement the httpStatusError interface.
  44. errStr := strings.ToLower(errMsg)
  45. for keyword, status := range map[string]int{
  46. "not found": http.StatusNotFound,
  47. "no such": http.StatusNotFound,
  48. "bad parameter": http.StatusBadRequest,
  49. "no command": http.StatusBadRequest,
  50. "conflict": http.StatusConflict,
  51. "impossible": http.StatusNotAcceptable,
  52. "wrong login/password": http.StatusUnauthorized,
  53. "unauthorized": http.StatusUnauthorized,
  54. "hasn't been activated": http.StatusForbidden,
  55. "this node": http.StatusNotAcceptable,
  56. } {
  57. if strings.Contains(errStr, keyword) {
  58. statusCode = status
  59. break
  60. }
  61. }
  62. }
  63. if statusCode == 0 {
  64. statusCode = http.StatusInternalServerError
  65. }
  66. return statusCode
  67. }
  68. // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
  69. // returns it in the response.
  70. func MakeErrorHandler(err error) http.HandlerFunc {
  71. return func(w http.ResponseWriter, r *http.Request) {
  72. statusCode := GetHTTPErrorStatusCode(err)
  73. vars := mux.Vars(r)
  74. if vars["version"] == "" || versions.GreaterThan(vars["version"], "1.23") {
  75. response := &types.ErrorResponse{
  76. Message: err.Error(),
  77. }
  78. WriteJSON(w, statusCode, response)
  79. } else {
  80. http.Error(w, grpc.ErrorDesc(err), statusCode)
  81. }
  82. }
  83. }