github.com/portworx/docker@v1.12.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/engine-api/types"
     9  	"github.com/docker/engine-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  // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
    79  // returns it in the response.
    80  func MakeErrorHandler(err error) http.HandlerFunc {
    81  	return func(w http.ResponseWriter, r *http.Request) {
    82  		statusCode := GetHTTPErrorStatusCode(err)
    83  		vars := mux.Vars(r)
    84  		if vars["version"] == "" || versions.GreaterThan(vars["version"], "1.23") {
    85  			response := &types.ErrorResponse{
    86  				Message: err.Error(),
    87  			}
    88  			WriteJSON(w, statusCode, response)
    89  		} else {
    90  			http.Error(w, grpc.ErrorDesc(err), statusCode)
    91  		}
    92  	}
    93  }