errors.go 2.6 KB

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