Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty body in POST /commit again #45550

Merged
merged 2 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion api/server/router/container/container_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
}

config, _, _, err := s.decoder.DecodeConfig(r.Body)
if err != nil && err != io.EOF { // Do not fail if body is empty.
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
return err
}

Expand Down Expand Up @@ -486,6 +486,9 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo

config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
if err != nil {
if errors.Is(err, io.EOF) {
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
}
return err
}
version := httputils.VersionFromContext(ctx)
Expand Down
22 changes: 15 additions & 7 deletions client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"os"
"reflect"
"strings"

"github.com/docker/docker/api/types"
Expand Down Expand Up @@ -54,11 +55,17 @@ func (cli *Client) put(ctx context.Context, path string, query url.Values, obj i
if err != nil {
return serverResponse{}, err
}
return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers)
return cli.putRaw(ctx, path, query, body, headers)
}

// putRaw sends an http request to the docker API using the method PUT.
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
// PUT requests are expected to always have a body (apparently)
// so explicitly pass an empty body to sendRequest to signal that
// it should set the Content-Type header if not already present.
if body == nil {
body = http.NoBody
}
return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers)
}

Expand All @@ -73,6 +80,12 @@ func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) {
if obj == nil {
return nil, headers, nil
}
// encoding/json encodes a nil pointer as the JSON document `null`,
// irrespective of whether the type implements json.Marshaler or encoding.TextMarshaler.
// That is almost certainly not what the caller intended as the request body.
if reflect.TypeOf(obj).Kind() == reflect.Ptr && reflect.ValueOf(obj).IsNil() {
return nil, headers, nil
}

body, err := encodeData(obj)
if err != nil {
Expand All @@ -86,11 +99,6 @@ func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) {
}

func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) {
expectedPayload := (method == http.MethodPost || method == http.MethodPut)
if expectedPayload && body == nil {
body = bytes.NewReader([]byte{})
}

req, err := http.NewRequest(method, path, body)
if err != nil {
return nil, err
Expand All @@ -106,7 +114,7 @@ func (cli *Client) buildRequest(method, path string, body io.Reader, headers hea
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme

if expectedPayload && req.Header.Get("Content-Type") == "" {
if body != nil && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "text/plain")
}
return req, nil
Expand Down
5 changes: 1 addition & 4 deletions runconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Confi
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())
return invalidJSONError{Err: err}
}
if dec.More() {
return validationError("unexpected content after JSON")
Expand Down
14 changes: 14 additions & 0 deletions runconfig/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,17 @@ func (e validationError) Error() string {
}

func (e validationError) InvalidParameter() {}

type invalidJSONError struct {
Err error
}

func (e invalidJSONError) Error() string {
return "invalid JSON: " + e.Err.Error()
}

func (e invalidJSONError) Unwrap() error {
return e.Err
}

func (e invalidJSONError) InvalidParameter() {}