mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
c7b488fbc8
The API did not treat invalid JSON payloads as a 400 error, as a result returning a 500 error; Before this change, an invalid JSON body would return a 500 error; ```bash curl -v \ --unix-socket /var/run/docker.sock \ -X POST \ "http://localhost/v1.30/networks/create" \ -H "Content-Type: application/json" \ -d '{invalid json' ``` ``` > POST /v1.30/networks/create HTTP/1.1 > Host: localhost > User-Agent: curl/7.52.1 > Accept: */* > Content-Type: application/json > Content-Length: 13 > * upload completely sent off: 13 out of 13 bytes < HTTP/1.1 500 Internal Server Error < Api-Version: 1.40 < Content-Type: application/json < Docker-Experimental: false < Ostype: linux < Server: Docker/dev (linux) < Date: Mon, 05 Nov 2018 11:55:20 GMT < Content-Length: 79 < {"message":"invalid character 'i' looking for beginning of object key string"} ``` Empty request: ```bash curl -v \ --unix-socket /var/run/docker.sock \ -X POST \ "http://localhost/v1.30/networks/create" \ -H "Content-Type: application/json" ``` ``` > POST /v1.30/networks/create HTTP/1.1 > Host: localhost > User-Agent: curl/7.54.0 > Accept: */* > Content-Type: application/json > < HTTP/1.1 500 Internal Server Error < Api-Version: 1.38 < Content-Length: 18 < Content-Type: application/json < Date: Mon, 05 Nov 2018 12:00:18 GMT < Docker-Experimental: true < Ostype: linux < Server: Docker/18.06.1-ce (linux) < {"message":"EOF"} ``` After this change, a 400 is returned; ```bash curl -v \ --unix-socket /var/run/docker.sock \ -X POST \ "http://localhost/v1.30/networks/create" \ -H "Content-Type: application/json" \ -d '{invalid json' ``` ``` > POST /v1.30/networks/create HTTP/1.1 > Host: localhost > User-Agent: curl/7.52.1 > Accept: */* > Content-Type: application/json > Content-Length: 13 > * upload completely sent off: 13 out of 13 bytes < HTTP/1.1 400 Bad Request < Api-Version: 1.40 < Content-Type: application/json < Docker-Experimental: false < Ostype: linux < Server: Docker/dev (linux) < Date: Mon, 05 Nov 2018 11:57:15 GMT < Content-Length: 79 < {"message":"invalid character 'i' looking for beginning of object key string"} ``` Empty request: ```bash curl -v \ --unix-socket /var/run/docker.sock \ -X POST \ "http://localhost/v1.30/networks/create" \ -H "Content-Type: application/json" ``` ``` > POST /v1.30/networks/create HTTP/1.1 > Host: localhost > User-Agent: curl/7.52.1 > Accept: */* > Content-Type: application/json > < HTTP/1.1 400 Bad Request < Api-Version: 1.40 < Content-Type: application/json < Docker-Experimental: false < Ostype: linux < Server: Docker/dev (linux) < Date: Mon, 05 Nov 2018 11:59:22 GMT < Content-Length: 49 < {"message":"got EOF while reading request body"} ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
145 lines
3.7 KiB
Go
145 lines
3.7 KiB
Go
package container // import "github.com/docker/docker/api/server/router/container"
|
|
|
|
import (
|
|
"compress/flate"
|
|
"compress/gzip"
|
|
"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"
|
|
)
|
|
|
|
type pathError struct{}
|
|
|
|
func (pathError) Error() string {
|
|
return "Path cannot be empty"
|
|
}
|
|
|
|
func (pathError) InvalidParameter() {}
|
|
|
|
// postContainersCopy is deprecated in favor of getContainersArchive.
|
|
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 cfg.Resource == "" {
|
|
return pathError{}
|
|
}
|
|
|
|
data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer data.Close()
|
|
|
|
w.Header().Set("Content-Type", "application/x-tar")
|
|
_, err = io.Copy(w, data)
|
|
return err
|
|
}
|
|
|
|
// // Encode the stat to JSON, base64 encode, and place in a header.
|
|
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
|
statJSON, err := json.Marshal(stat)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
header.Set(
|
|
"X-Docker-Container-Path-Stat",
|
|
base64.StdEncoding.EncodeToString(statJSON),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *containerRouter) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
v, err := httputils.ArchiveFormValues(r, vars)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stat, err := s.backend.ContainerStatPath(v.Name, v.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return setContainerPathStatHeader(stat, w.Header())
|
|
}
|
|
|
|
func writeCompressedResponse(w http.ResponseWriter, r *http.Request, body io.Reader) error {
|
|
var cw io.Writer
|
|
switch gddohttputil.NegotiateContentEncoding(r, []string{"gzip", "deflate"}) {
|
|
case "gzip":
|
|
gw := gzip.NewWriter(w)
|
|
defer gw.Close()
|
|
cw = gw
|
|
w.Header().Set("Content-Encoding", "gzip")
|
|
case "deflate":
|
|
fw, err := flate.NewWriter(w, flate.DefaultCompression)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fw.Close()
|
|
cw = fw
|
|
w.Header().Set("Content-Encoding", "deflate")
|
|
default:
|
|
cw = w
|
|
}
|
|
_, err := io.Copy(cw, body)
|
|
return err
|
|
}
|
|
|
|
func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
v, err := httputils.ArchiveFormValues(r, vars)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tarArchive, stat, err := s.backend.ContainerArchivePath(v.Name, v.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tarArchive.Close()
|
|
|
|
if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
|
return err
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/x-tar")
|
|
return writeCompressedResponse(w, r, tarArchive)
|
|
}
|
|
|
|
func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
v, err := httputils.ArchiveFormValues(r, vars)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
|
|
copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
|
|
|
|
return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
|
|
}
|