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