Move httputils error helpers to errdefs package
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
ae875d4069
commit
2a9c987e5a
8 changed files with 86 additions and 81 deletions
9
api/server/httputils/errors_deprecated.go
Normal file
9
api/server/httputils/errors_deprecated.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
import "github.com/docker/docker/errdefs"
|
||||
|
||||
// GetHTTPErrorStatusCode retrieves status code from error message.
|
||||
//
|
||||
// Deprecated: use errdefs.GetHTTPErrorStatusCode
|
||||
func GetHTTPErrorStatusCode(err error) int {
|
||||
return errdefs.GetHTTPErrorStatusCode(err)
|
||||
}
|
|
@ -7,9 +7,13 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// APIVersionKey is the client's requested API version.
|
||||
|
@ -88,6 +92,28 @@ func VersionFromContext(ctx context.Context) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and
|
||||
// returns it in the response.
|
||||
func MakeErrorHandler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
statusCode := errdefs.GetHTTPErrorStatusCode(err)
|
||||
vars := mux.Vars(r)
|
||||
if apiVersionSupportsJSONErrors(vars["version"]) {
|
||||
response := &types.ErrorResponse{
|
||||
Message: err.Error(),
|
||||
}
|
||||
WriteJSON(w, statusCode, response)
|
||||
} else {
|
||||
http.Error(w, status.Convert(err).Message(), statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiVersionSupportsJSONErrors(version string) bool {
|
||||
const firstAPIVersionWithJSONErrors = "1.23"
|
||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||
}
|
||||
|
||||
// matchesContentType validates the content type against the expected one
|
||||
func matchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
|
|
|
@ -589,7 +589,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
|||
// Remember to close stream if error happens
|
||||
conn, _, errHijack := hijacker.Hijack()
|
||||
if errHijack == nil {
|
||||
statusCode := httputils.GetHTTPErrorStatusCode(err)
|
||||
statusCode := errdefs.GetHTTPErrorStatusCode(err)
|
||||
statusText := http.StatusText(statusCode)
|
||||
fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
|
||||
httputils.CloseStreams(conn)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/server/router/debug"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -139,7 +140,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
|||
}
|
||||
|
||||
if err := handlerFunc(ctx, w, r, vars); err != nil {
|
||||
statusCode := httputils.GetHTTPErrorStatusCode(err)
|
||||
statusCode := errdefs.GetHTTPErrorStatusCode(err)
|
||||
if statusCode >= 500 {
|
||||
logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/errdefs"
|
||||
)
|
||||
|
||||
// Ping pings the server and returns the value of the "Docker-Experimental",
|
||||
|
@ -49,7 +49,7 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
|
|||
var ping types.Ping
|
||||
if resp.header == nil {
|
||||
err := cli.checkResponseErr(resp)
|
||||
return ping, httputils.FromStatusCode(err, resp.statusCode)
|
||||
return ping, errdefs.FromStatusCode(err, resp.statusCode)
|
||||
}
|
||||
ping.APIVersion = resp.header.Get("API-Version")
|
||||
ping.OSType = resp.header.Get("OSType")
|
||||
|
@ -60,5 +60,5 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
|
|||
ping.BuilderVersion = types.BuilderVersion(bv)
|
||||
}
|
||||
err := cli.checkResponseErr(resp)
|
||||
return ping, httputils.FromStatusCode(err, resp.statusCode)
|
||||
return ping, errdefs.FromStatusCode(err, resp.statusCode)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -121,10 +121,10 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u
|
|||
}
|
||||
resp, err := cli.doRequest(ctx, req)
|
||||
if err != nil {
|
||||
return resp, httputils.FromStatusCode(err, resp.statusCode)
|
||||
return resp, errdefs.FromStatusCode(err, resp.statusCode)
|
||||
}
|
||||
err = cli.checkResponseErr(resp)
|
||||
return resp, httputils.FromStatusCode(err, resp.statusCode)
|
||||
return resp, errdefs.FromStatusCode(err, resp.statusCode)
|
||||
}
|
||||
|
||||
func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
package errdefs // import "github.com/docker/docker/errdefs"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// GetHTTPErrorStatusCode retrieves status code from error message.
|
||||
func GetHTTPErrorStatusCode(err error) int {
|
||||
if err == nil {
|
||||
|
@ -32,23 +24,23 @@ func GetHTTPErrorStatusCode(err error) int {
|
|||
|
||||
// Note that the below functions are already checking the error causal chain for matches.
|
||||
switch {
|
||||
case errdefs.IsNotFound(err):
|
||||
case IsNotFound(err):
|
||||
statusCode = http.StatusNotFound
|
||||
case errdefs.IsInvalidParameter(err):
|
||||
case IsInvalidParameter(err):
|
||||
statusCode = http.StatusBadRequest
|
||||
case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err):
|
||||
case IsConflict(err) || IsAlreadyExists(err):
|
||||
statusCode = http.StatusConflict
|
||||
case errdefs.IsUnauthorized(err):
|
||||
case IsUnauthorized(err):
|
||||
statusCode = http.StatusUnauthorized
|
||||
case errdefs.IsUnavailable(err):
|
||||
case IsUnavailable(err):
|
||||
statusCode = http.StatusServiceUnavailable
|
||||
case errdefs.IsForbidden(err):
|
||||
case IsForbidden(err):
|
||||
statusCode = http.StatusForbidden
|
||||
case errdefs.IsNotModified(err):
|
||||
case IsNotModified(err):
|
||||
statusCode = http.StatusNotModified
|
||||
case errdefs.IsNotImplemented(err):
|
||||
case IsNotImplemented(err):
|
||||
statusCode = http.StatusNotImplemented
|
||||
case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
|
||||
case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err):
|
||||
statusCode = http.StatusInternalServerError
|
||||
default:
|
||||
statusCode = statusCodeFromGRPCError(err)
|
||||
|
@ -76,31 +68,31 @@ func GetHTTPErrorStatusCode(err error) int {
|
|||
return statusCode
|
||||
}
|
||||
|
||||
// FromStatusCode creates an errdef error, based on the provided status-code
|
||||
// FromStatusCode creates an errdef error, based on the provided HTTP status-code
|
||||
func FromStatusCode(err error, statusCode int) error {
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
switch statusCode {
|
||||
case http.StatusNotFound:
|
||||
err = errdefs.NotFound(err)
|
||||
err = NotFound(err)
|
||||
case http.StatusBadRequest:
|
||||
err = errdefs.InvalidParameter(err)
|
||||
err = InvalidParameter(err)
|
||||
case http.StatusConflict:
|
||||
err = errdefs.Conflict(err)
|
||||
err = Conflict(err)
|
||||
case http.StatusUnauthorized:
|
||||
err = errdefs.Unauthorized(err)
|
||||
err = Unauthorized(err)
|
||||
case http.StatusServiceUnavailable:
|
||||
err = errdefs.Unavailable(err)
|
||||
err = Unavailable(err)
|
||||
case http.StatusForbidden:
|
||||
err = errdefs.Forbidden(err)
|
||||
err = Forbidden(err)
|
||||
case http.StatusNotModified:
|
||||
err = errdefs.NotModified(err)
|
||||
err = NotModified(err)
|
||||
case http.StatusNotImplemented:
|
||||
err = errdefs.NotImplemented(err)
|
||||
err = NotImplemented(err)
|
||||
case http.StatusInternalServerError:
|
||||
if !errdefs.IsSystem(err) && !errdefs.IsUnknown(err) && !errdefs.IsDataLoss(err) && !errdefs.IsDeadline(err) && !errdefs.IsCancelled(err) {
|
||||
err = errdefs.System(err)
|
||||
if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) {
|
||||
err = System(err)
|
||||
}
|
||||
default:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
|
@ -112,38 +104,16 @@ func FromStatusCode(err error, statusCode int) error {
|
|||
case statusCode >= 200 && statusCode < 400:
|
||||
// it's a client error
|
||||
case statusCode >= 400 && statusCode < 500:
|
||||
err = errdefs.InvalidParameter(err)
|
||||
err = InvalidParameter(err)
|
||||
case statusCode >= 500 && statusCode < 600:
|
||||
err = errdefs.System(err)
|
||||
err = System(err)
|
||||
default:
|
||||
err = errdefs.Unknown(err)
|
||||
err = Unknown(err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func apiVersionSupportsJSONErrors(version string) bool {
|
||||
const firstAPIVersionWithJSONErrors = "1.23"
|
||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||
}
|
||||
|
||||
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and
|
||||
// returns it in the response.
|
||||
func MakeErrorHandler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
statusCode := GetHTTPErrorStatusCode(err)
|
||||
vars := mux.Vars(r)
|
||||
if apiVersionSupportsJSONErrors(vars["version"]) {
|
||||
response := &types.ErrorResponse{
|
||||
Message: err.Error(),
|
||||
}
|
||||
WriteJSON(w, statusCode, response)
|
||||
} else {
|
||||
http.Error(w, status.Convert(err).Message(), statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// statusCodeFromGRPCError returns status code according to gRPC error
|
||||
func statusCodeFromGRPCError(err error) int {
|
||||
switch status.Code(err) {
|
|
@ -1,11 +1,10 @@
|
|||
package httputils
|
||||
package errdefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
|
@ -20,67 +19,67 @@ func TestFromStatusCode(t *testing.T) {
|
|||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotFound,
|
||||
check: errdefs.IsNotFound,
|
||||
check: IsNotFound,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusBadRequest,
|
||||
check: errdefs.IsInvalidParameter,
|
||||
check: IsInvalidParameter,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusConflict,
|
||||
check: errdefs.IsConflict,
|
||||
check: IsConflict,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusUnauthorized,
|
||||
check: errdefs.IsUnauthorized,
|
||||
check: IsUnauthorized,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusServiceUnavailable,
|
||||
check: errdefs.IsUnavailable,
|
||||
check: IsUnavailable,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusForbidden,
|
||||
check: errdefs.IsForbidden,
|
||||
check: IsForbidden,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotModified,
|
||||
check: errdefs.IsNotModified,
|
||||
check: IsNotModified,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotImplemented,
|
||||
check: errdefs.IsNotImplemented,
|
||||
check: IsNotImplemented,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsSystem,
|
||||
check: IsSystem,
|
||||
},
|
||||
{
|
||||
err: errdefs.Unknown(testErr),
|
||||
err: Unknown(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsUnknown,
|
||||
check: IsUnknown,
|
||||
},
|
||||
{
|
||||
err: errdefs.DataLoss(testErr),
|
||||
err: DataLoss(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsDataLoss,
|
||||
check: IsDataLoss,
|
||||
},
|
||||
{
|
||||
err: errdefs.Deadline(testErr),
|
||||
err: Deadline(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsDeadline,
|
||||
check: IsDeadline,
|
||||
},
|
||||
{
|
||||
err: errdefs.Cancelled(testErr),
|
||||
err: Cancelled(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsCancelled,
|
||||
check: IsCancelled,
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in a new issue