Преглед на файлове

Merge pull request #45550 from corhere/fix-empty-container-decode

Allow empty body in `POST /commit` again
Sebastiaan van Stijn преди 2 години
родител
ревизия
1b3c2743cc
променени са 4 файла, в които са добавени 34 реда и са изтрити 12 реда
  1. 4 1
      api/server/router/container/container_routes.go
  2. 15 7
      client/request.go
  3. 1 4
      runconfig/config.go
  4. 14 0
      runconfig/errors.go

+ 4 - 1
api/server/router/container/container_routes.go

@@ -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
 	}
 
@@ -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)

+ 15 - 7
client/request.go

@@ -10,6 +10,7 @@ import (
 	"net/http"
 	"net/url"
 	"os"
+	"reflect"
 	"strings"
 
 	"github.com/docker/docker/api/types"
@@ -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)
 }
 
@@ -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 {
@@ -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
@@ -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

+ 1 - 4
runconfig/config.go

@@ -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")

+ 14 - 0
runconfig/errors.go

@@ -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() {}