errors.go 2.2 KB

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