github.com/getong/docker@v1.13.1/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 retrieves 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 _, status := range []struct { 53 keyword string 54 code int 55 }{ 56 {"not found", http.StatusNotFound}, 57 {"no such", http.StatusNotFound}, 58 {"bad parameter", http.StatusBadRequest}, 59 {"no command", http.StatusBadRequest}, 60 {"conflict", http.StatusConflict}, 61 {"impossible", http.StatusNotAcceptable}, 62 {"wrong login/password", http.StatusUnauthorized}, 63 {"unauthorized", http.StatusUnauthorized}, 64 {"hasn't been activated", http.StatusForbidden}, 65 {"this node", http.StatusServiceUnavailable}, 66 } { 67 if strings.Contains(errStr, status.keyword) { 68 statusCode = status.code 69 break 70 } 71 } 72 } 73 74 if statusCode == 0 { 75 statusCode = http.StatusInternalServerError 76 } 77 78 return statusCode 79 } 80 81 func apiVersionSupportsJSONErrors(version string) bool { 82 const firstAPIVersionWithJSONErrors = "1.23" 83 return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors) 84 } 85 86 // MakeErrorHandler makes an HTTP handler that decodes a Docker error and 87 // returns it in the response. 88 func MakeErrorHandler(err error) http.HandlerFunc { 89 return func(w http.ResponseWriter, r *http.Request) { 90 statusCode := GetHTTPErrorStatusCode(err) 91 vars := mux.Vars(r) 92 if apiVersionSupportsJSONErrors(vars["version"]) { 93 response := &types.ErrorResponse{ 94 Message: err.Error(), 95 } 96 WriteJSON(w, statusCode, response) 97 } else { 98 http.Error(w, grpc.ErrorDesc(err), statusCode) 99 } 100 } 101 }