mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #43463 from thaJeztah/httputils_readjson
api/server/httputils: add ReadJSON() utility and fix handling of invalid JSON
This commit is contained in:
commit
bca8d9f2ce
20 changed files with 432 additions and 267 deletions
|
@ -2,6 +2,7 @@ package httputils // import "github.com/docker/docker/api/server/httputils"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
|
@ -9,7 +10,6 @@ import (
|
|||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// APIVersionKey is the client's requested API version.
|
||||
|
@ -49,17 +49,50 @@ func CheckForJSON(r *http.Request) error {
|
|||
ct := r.Header.Get("Content-Type")
|
||||
|
||||
// No Content-Type header is ok as long as there's no Body
|
||||
if ct == "" {
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
return nil
|
||||
}
|
||||
if ct == "" && (r.Body == nil || r.ContentLength == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise it better be json
|
||||
if matchesContentType(ct, "application/json") {
|
||||
return matchesContentType(ct, "application/json")
|
||||
}
|
||||
|
||||
// ReadJSON validates the request to have the correct content-type, and decodes
|
||||
// the request's Body into out.
|
||||
func ReadJSON(r *http.Request, out interface{}) error {
|
||||
err := CheckForJSON(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
// an empty body is not invalid, so don't return an error; see
|
||||
// https://lists.w3.org/Archives/Public/ietf-http-wg/2010JulSep/0272.html
|
||||
return nil
|
||||
}
|
||||
return errdefs.InvalidParameter(errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct))
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
err = dec.Decode(out)
|
||||
defer r.Body.Close()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(errors.Wrap(err, "invalid JSON"))
|
||||
}
|
||||
|
||||
if dec.More() {
|
||||
return errdefs.InvalidParameter(errors.New("unexpected content after JSON"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc.Encode(v)
|
||||
}
|
||||
|
||||
// ParseForm ensures the request form is parsed even with invalid content types.
|
||||
|
@ -89,10 +122,13 @@ func VersionFromContext(ctx context.Context) string {
|
|||
}
|
||||
|
||||
// matchesContentType validates the content type against the expected one
|
||||
func matchesContentType(contentType, expectedType string) bool {
|
||||
func matchesContentType(contentType, expectedType string) error {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing media type: %s error: %v", contentType, err)
|
||||
return errdefs.InvalidParameter(errors.Wrapf(err, "malformed Content-Type header (%s)", contentType))
|
||||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
if mimetype != expectedType {
|
||||
return errdefs.InvalidParameter(errors.Errorf("unsupported Content-Type header (%s): must be '%s'", contentType, expectedType))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,18 +1,130 @@
|
|||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// matchesContentType
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
if !matchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
err := matchesContentType("application/json", "application/json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !matchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
err = matchesContentType("application/json; charset=utf-8", "application/json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if matchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
expected := "unsupported Content-Type header (dockerapplication/json): must be 'application/json'"
|
||||
err = matchesContentType("dockerapplication/json", "application/json")
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
||||
}
|
||||
|
||||
expected = "malformed Content-Type header (foo;;;bar): mime: invalid media parameter"
|
||||
err = matchesContentType("foo;;;bar", "application/json")
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadJSON(t *testing.T) {
|
||||
t.Run("nil body", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
foo := struct{}{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", strings.NewReader(""))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "" {
|
||||
t.Errorf("expected: '', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with valid request", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"}`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "some value" {
|
||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
t.Run("with whitespace", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", strings.NewReader(`
|
||||
|
||||
{"SomeField":"some value"}
|
||||
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "some value" {
|
||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with extra content", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"} and more content`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err == nil {
|
||||
t.Error("expected an error, got none")
|
||||
}
|
||||
expected := "unexpected content after JSON"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
req, err := http.NewRequest("POST", "https://example.com/some/path", strings.NewReader(`{invalid json`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err == nil {
|
||||
t.Error("expected an error, got none")
|
||||
}
|
||||
expected := "invalid JSON: invalid character 'i' looking for beginning of object key string"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc.Encode(v)
|
||||
}
|
|
@ -2,7 +2,6 @@ package checkpoint // import "github.com/docker/docker/api/server/router/checkpo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
|
@ -15,9 +14,7 @@ func (s *checkpointRouter) postContainerCheckpoint(ctx context.Context, w http.R
|
|||
}
|
||||
|
||||
var options types.CheckpointCreateOptions
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&options); err != nil {
|
||||
if err := httputils.ReadJSON(r, &options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func (r *containerRouter) initRoutes() {
|
|||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8, Errors out since 1.12
|
||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
||||
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
|
|
|
@ -427,14 +427,9 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
|||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var updateConfig container.UpdateConfig
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&updateConfig); err != nil {
|
||||
if err := httputils.ReadJSON(r, &updateConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.40") {
|
||||
|
|
|
@ -6,14 +6,12 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
gddohttputil "github.com/golang/gddo/httputil"
|
||||
)
|
||||
|
||||
|
@ -26,23 +24,18 @@ func (pathError) Error() string {
|
|||
func (pathError) InvalidParameter() {}
|
||||
|
||||
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||
//
|
||||
// Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
||||
func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// Deprecated since 1.8, Errors out since 1.12
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := types.CopyConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.Resource == "" {
|
||||
|
|
|
@ -2,8 +2,6 @@ package container // import "github.com/docker/docker/api/server/router/containe
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -38,17 +36,10 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
|||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
execConfig := &types.ExecConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, execConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(execConfig.Cmd) == 0 {
|
||||
|
@ -56,9 +47,9 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
|||
}
|
||||
|
||||
// Register an instance of Exec in container.
|
||||
id, err := s.backend.ContainerExecCreate(name, execConfig)
|
||||
id, err := s.backend.ContainerExecCreate(vars["name"], execConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", vars["name"], err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -74,9 +65,11 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
|||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThan(version, "1.21") {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
if versions.LessThan(version, "1.22") {
|
||||
// API versions before 1.22 did not enforce application/json content-type.
|
||||
// Allow older clients to work by patching the content-type.
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,11 +80,8 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
|||
)
|
||||
|
||||
execStartCheck := &types.ExecStartCheck{}
|
||||
if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, execStartCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists, err := s.backend.ExecExists(execName); !exists {
|
||||
|
|
|
@ -2,8 +2,6 @@ package network // import "github.com/docker/docker/api/server/router/network"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -205,23 +203,15 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
|
|||
}
|
||||
|
||||
func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var create types.NetworkCreateRequest
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
var create types.NetworkCreateRequest
|
||||
if err := httputils.ReadJSON(r, &create); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
|
||||
return nameConflict(create.Name)
|
||||
}
|
||||
|
@ -255,22 +245,15 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
|
|||
}
|
||||
|
||||
func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var connect types.NetworkConnect
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
var connect types.NetworkConnect
|
||||
if err := httputils.ReadJSON(r, &connect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
// Unlike other operations, we does not check ambiguity of the name/ID here.
|
||||
// The reason is that, In case of attachable network in swarm scope, the actual local network
|
||||
// may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork`
|
||||
|
@ -279,22 +262,15 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
|
|||
}
|
||||
|
||||
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var disconnect types.NetworkDisconnect
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
var disconnect types.NetworkDisconnect
|
||||
if err := httputils.ReadJSON(r, &disconnect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -13,7 +12,6 @@ import (
|
|||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -96,12 +94,8 @@ func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter
|
|||
}
|
||||
|
||||
var privileges types.PluginPrivileges
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&privileges); err != nil {
|
||||
return errors.Wrap(err, "failed to parse privileges")
|
||||
}
|
||||
if dec.More() {
|
||||
return errors.New("invalid privileges")
|
||||
if err := httputils.ReadJSON(r, &privileges); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metaHeaders, authConfig := parseHeaders(r.Header)
|
||||
|
@ -135,12 +129,8 @@ func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r
|
|||
}
|
||||
|
||||
var privileges types.PluginPrivileges
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&privileges); err != nil {
|
||||
return errors.Wrap(err, "failed to parse privileges")
|
||||
}
|
||||
if dec.More() {
|
||||
return errors.New("invalid privileges")
|
||||
if err := httputils.ReadJSON(r, &privileges); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metaHeaders, authConfig := parseHeaders(r.Header)
|
||||
|
@ -277,11 +267,8 @@ func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r
|
|||
|
||||
func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var args []string
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &args); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pr.backend.Set(vars["name"], args); err != nil {
|
||||
return err
|
||||
|
|
|
@ -2,9 +2,7 @@ package swarm // import "github.com/docker/docker/api/server/router/swarm"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
|
@ -21,11 +19,8 @@ import (
|
|||
|
||||
func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.InitRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
|
@ -48,11 +43,8 @@ func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r
|
|||
|
||||
func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.JoinRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
return sr.backend.Join(req)
|
||||
}
|
||||
|
@ -78,11 +70,8 @@ func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter
|
|||
|
||||
func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var swarm types.Spec
|
||||
if err := json.NewDecoder(r.Body).Decode(&swarm); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &swarm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
@ -132,11 +121,8 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
|
|||
|
||||
func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.UnlockRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sr.backend.UnlockSwarm(req); err != nil {
|
||||
|
@ -216,11 +202,8 @@ func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r
|
|||
|
||||
func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var service types.ServiceSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns "" if the header does not exist
|
||||
|
@ -243,11 +226,8 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
|
|||
|
||||
func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var service types.ServiceSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
@ -341,11 +321,8 @@ func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *ht
|
|||
|
||||
func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var node types.NodeSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&node); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
@ -423,11 +400,8 @@ func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r
|
|||
|
||||
func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var secret types.SecretSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if secret.Templating != nil && versions.LessThan(version, "1.37") {
|
||||
|
@ -464,11 +438,8 @@ func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *
|
|||
|
||||
func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var secret types.SecretSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
@ -500,11 +471,8 @@ func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r
|
|||
|
||||
func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
@ -542,11 +510,8 @@ func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *
|
|||
|
||||
func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
|
|
@ -2,14 +2,11 @@ package volume // import "github.com/docker/docker/api/server/router/volume"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
volumetypes "github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/volume/service/opts"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -47,16 +44,9 @@ func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWri
|
|||
return err
|
||||
}
|
||||
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req volumetypes.VolumeCreateBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(err)
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
volume, err := v.backend.Create(ctx, req.Name, req.Driver, opts.WithCreateOptions(req.DriverOpts), opts.WithCreateLabels(req.Labels))
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/testutil/request"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/poll"
|
||||
)
|
||||
|
||||
|
@ -56,7 +57,7 @@ func (s *DockerSuite) TestExecAPICreateNoValidContentType(c *testing.T) {
|
|||
}
|
||||
b, err := request.ReadBody(body)
|
||||
assert.NilError(c, err)
|
||||
assert.Assert(c, strings.Contains(getErrorMessage(c, b), "Content-Type specified"), "Expected message when creating exec command with invalid Content-Type specified")
|
||||
assert.Assert(c, is.Contains(getErrorMessage(c, b), "unsupported Content-Type header (test/plain): must be 'application/json'"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestExecAPICreateContainerPaused(c *testing.T) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package container // import "github.com/docker/docker/integration/container"
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/testutil/request"
|
||||
|
@ -9,34 +10,72 @@ import (
|
|||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
// TestContainerInvalidJSON tests that POST endpoints that expect a body return
|
||||
// the correct error when sending invalid JSON requests.
|
||||
func TestContainerInvalidJSON(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
// POST endpoints that accept / expect a JSON body;
|
||||
endpoints := []string{
|
||||
"/commit",
|
||||
"/containers/create",
|
||||
"/containers/foobar/exec",
|
||||
"/containers/foobar/update",
|
||||
"/exec/foobar/start",
|
||||
}
|
||||
|
||||
// windows doesnt support API < v1.24
|
||||
if runtime.GOOS != "windows" {
|
||||
endpoints = append(
|
||||
endpoints,
|
||||
"/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
||||
"/v1.23/containers/foobar/start", // accepts a body on API < v1.24
|
||||
)
|
||||
}
|
||||
|
||||
for _, ep := range endpoints {
|
||||
ep := ep
|
||||
t.Run(ep, func(t *testing.T) {
|
||||
t.Run(ep[1:], func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid content type", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{}"), request.ContentType("text/plain"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'"))
|
||||
})
|
||||
|
||||
res, body, err = request.Post(ep, request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err = request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string"))
|
||||
})
|
||||
|
||||
t.Run("extra content after JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString(`{} trailing content`), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unexpected content after JSON"))
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
// empty body should not produce an 500 internal server error, or
|
||||
// any 5XX error (this is assuming the request does not produce
|
||||
// an internal server error for another reason, but it shouldn't)
|
||||
res, _, err := request.Post(ep, request.RawString(``), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, res.StatusCode < http.StatusInternalServerError)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,12 @@ func TestRunContainerWithBridgeNone(t *testing.T) {
|
|||
assert.Check(t, is.Equal(stdout.String(), result.Combined()), "The network namespace of container should be the same with host when --net=host and bridge network is disabled")
|
||||
}
|
||||
|
||||
// TestNetworkInvalidJSON tests that POST endpoints that expect a body return
|
||||
// the correct error when sending invalid JSON requests.
|
||||
func TestNetworkInvalidJSON(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
// POST endpoints that accept / expect a JSON body;
|
||||
endpoints := []string{
|
||||
"/networks/create",
|
||||
"/networks/bridge/connect",
|
||||
|
@ -73,24 +76,47 @@ func TestNetworkInvalidJSON(t *testing.T) {
|
|||
|
||||
for _, ep := range endpoints {
|
||||
ep := ep
|
||||
t.Run(ep, func(t *testing.T) {
|
||||
t.Run(ep[1:], func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid content type", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{}"), request.ContentType("text/plain"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'"))
|
||||
})
|
||||
|
||||
res, body, err = request.Post(ep, request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err = request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string"))
|
||||
})
|
||||
|
||||
t.Run("extra content after JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString(`{} trailing content`), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unexpected content after JSON"))
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
// empty body should not produce an 500 internal server error, or
|
||||
// any 5XX error (this is assuming the request does not produce
|
||||
// an internal server error for another reason, but it shouldn't)
|
||||
res, _, err := request.Post(ep, request.RawString(``), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, res.StatusCode < http.StatusInternalServerError)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,31 +29,61 @@ import (
|
|||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// TestPluginInvalidJSON tests that POST endpoints that expect a body return
|
||||
// the correct error when sending invalid JSON requests.
|
||||
func TestPluginInvalidJSON(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
endpoints := []string{"/plugins/foobar/set"}
|
||||
// POST endpoints that accept / expect a JSON body;
|
||||
endpoints := []string{
|
||||
"/plugins/foobar/set",
|
||||
"/plugins/foobar/upgrade",
|
||||
"/plugins/pull",
|
||||
}
|
||||
|
||||
for _, ep := range endpoints {
|
||||
ep := ep
|
||||
t.Run(ep, func(t *testing.T) {
|
||||
t.Run(ep[1:], func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid content type", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("[]"), request.ContentType("text/plain"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'"))
|
||||
})
|
||||
|
||||
res, body, err = request.Post(ep, request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err = request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string"))
|
||||
})
|
||||
|
||||
t.Run("extra content after JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString(`[] trailing content`), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unexpected content after JSON"))
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
// empty body should not produce an 500 internal server error, or
|
||||
// any 5XX error (this is assuming the request does not produce
|
||||
// an internal server error for another reason, but it shouldn't)
|
||||
res, _, err := request.Post(ep, request.RawString(``), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, res.StatusCode < http.StatusInternalServerError)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,31 +104,57 @@ func TestVolumesInspect(t *testing.T) {
|
|||
assert.Check(t, createdAt.Unix()-now.Unix() < 60, "CreatedAt (%s) exceeds creation time (%s) 60s", createdAt, now)
|
||||
}
|
||||
|
||||
// TestVolumesInvalidJSON tests that POST endpoints that expect a body return
|
||||
// the correct error when sending invalid JSON requests.
|
||||
func TestVolumesInvalidJSON(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
// POST endpoints that accept / expect a JSON body;
|
||||
endpoints := []string{"/volumes/create"}
|
||||
|
||||
for _, ep := range endpoints {
|
||||
ep := ep
|
||||
t.Run(ep, func(t *testing.T) {
|
||||
t.Run(ep[1:], func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid content type", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{}"), request.ContentType("text/plain"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'"))
|
||||
})
|
||||
|
||||
res, body, err = request.Post(ep, request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err = request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string"))
|
||||
})
|
||||
|
||||
t.Run("extra content after JSON", func(t *testing.T) {
|
||||
res, body, err := request.Post(ep, request.RawString(`{} trailing content`), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(buf), "unexpected content after JSON"))
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
// empty body should not produce an 500 internal server error, or
|
||||
// any 5XX error (this is assuming the request does not produce
|
||||
// an internal server error for another reason, but it shouldn't)
|
||||
res, _, err := request.Post(ep, request.RawString(``), request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, res.StatusCode < http.StatusInternalServerError)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig
|
|||
// it's your business to do so
|
||||
func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
var w ContainerConfigWrapper
|
||||
if err := json.NewDecoder(src).Decode(&w); err != nil {
|
||||
if err := loadJSON(src, &w); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
|
@ -72,3 +72,18 @@ func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Confi
|
|||
}
|
||||
return w.Config, hc, w.NetworkingConfig, nil
|
||||
}
|
||||
|
||||
// loadJSON is similar to api/server/httputils.ReadJSON()
|
||||
func loadJSON(src io.Reader, out interface{}) error {
|
||||
dec := json.NewDecoder(src)
|
||||
if err := dec.Decode(&out); err != nil {
|
||||
if err == io.EOF {
|
||||
return validationError("invalid JSON: got EOF while reading request body")
|
||||
}
|
||||
return validationError("invalid JSON: " + err.Error())
|
||||
}
|
||||
if dec.More() {
|
||||
return validationError("unexpected content after JSON")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -42,27 +42,30 @@ func TestDecodeContainerConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
b, err := os.ReadFile(f.file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f := f
|
||||
t.Run(f.file, func(t *testing.T) {
|
||||
b, err := os.ReadFile(f.file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New())
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
||||
}
|
||||
c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Image != image {
|
||||
t.Fatalf("Expected %s image, found %s\n", image, c.Image)
|
||||
}
|
||||
if c.Image != image {
|
||||
t.Fatalf("Expected %s image, found %s", image, c.Image)
|
||||
}
|
||||
|
||||
if len(c.Entrypoint) != len(f.entrypoint) {
|
||||
t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint)
|
||||
}
|
||||
if len(c.Entrypoint) != len(f.entrypoint) {
|
||||
t.Fatalf("Expected %v, found %v", f.entrypoint, c.Entrypoint)
|
||||
}
|
||||
|
||||
if h != nil && h.Memory != 1000 {
|
||||
t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory)
|
||||
}
|
||||
if h != nil && h.Memory != 1000 {
|
||||
t.Fatalf("Expected memory to be 1000, found %d", h.Memory)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
|
@ -12,7 +11,7 @@ import (
|
|||
// It assumes the content of the reader will be JSON, and decodes it.
|
||||
func decodeHostConfig(src io.Reader) (*container.HostConfig, error) {
|
||||
var w ContainerConfigWrapper
|
||||
if err := json.NewDecoder(src).Decode(&w); err != nil {
|
||||
if err := loadJSON(src, &w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w.getHostConfig(), nil
|
||||
|
|
Loading…
Add table
Reference in a new issue