Files
moby/client
Sebastiaan van Stijn 4205776b85 client: always send (empty) body on push
Before ea29dffaa5, the image create endpoint
had a [fallback for very old client versions][1] that would send authentication
as body instead of through the `X-Registry-Auth` header.

However, the implementation of this fallback did not handle empty bodies,
resulting in an `io.EOF` error to be returned when trying to parse the
body as JSON.

In practice, this problem didn't happen when using the CLI, because even
if no authentication was present, `registry.EncodeAuthConfig()` (used by
the CLI to set the `X-Registry-Auth` header) would produce an empty JSON
document (`{}`), which would be encoded in base64 (`e30=`), so we would
never set an empty `X-Registry-Auth` (but other clients may have hit this
situation). That behavior was unexpected, because not all registries require
authentication, and omitting the `X-Registry-Auth` should be valid. We
also want to have more flexibility in authentication (and being able to
distinguish unauthenticated requests, so that we can fallback to
alternative paths).

Unfortunately, we can't change existing daemons, so must account for the
faulty fallback. Currently, omitting the `X-Registry-Auth` produces an
error, but we can avoid this by unconditionally sending a body, which
may be an empty JSON document (`{}`).

I explored possible options for this; we can either construct our own
empty JSON (`json.RawMessage("{}")`) to be explicit that we're sending
empty JSON, but [`encodeBody()`][2] is currently hard-coded to expect
JSON requests, and unconditionally calls [`encodeData`][3], which
encodes to JSON, so we may as well take advantage of `http.NoBody`,
which gets marshaled to an empty JSON document;
https://go.dev/play/p/QCw9dJ6LGQu

    package main

    import (
        "encoding/json"
        "fmt"
        "net/http"
    )

    func main() {
        body, _ := json.Marshal(http.NoBody)
        fmt.Println(string(body))
    }

Before this patch, a client omitting `X-Registry-Auth` (and no body)
would produce an error;

    docker pull -q busybox
    docker tag busybox 127.0.0.1:5001/myimage:latest

    docker run -d --name registry -p 127.0.0.1:5001:5000 registry:3
    docker push 127.0.0.1:5001/myimage:latest
    Error response from daemon: bad parameters and missing X-Registry-Auth: invalid X-Registry-Auth header: EOF

With this patch applied, no error is produced;

    docker pull -q busybox
    docker tag busybox 127.0.0.1:5001/myimage:latest

    docker run -d --name registry -p 127.0.0.1:5001:5000 registry:3
    docker push 127.0.0.1:5001/myimage:latest
    The push refers to repository [127.0.0.1:5001/myimage]
    189fdd150837: Pushed
    latest: digest: sha256:68a0d55a75c935e1101d16ded1c748babb7f96a9af43f7533ba83b87e2508b82 size: 610

[1]: 63fcf7d858/api/types/registry/authconfig_test.go (L109-L114)
[2]: 63fcf7d858/client/request.go (L67-L87)
[3]: 63fcf7d858/client/request.go (L296-L304)
[4]: ea29dffaa5

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit b1ce0c89f0)
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
2025-07-21 15:59:17 -07:00
..
2016-09-07 11:05:58 -07:00
2025-06-07 09:57:58 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-06-07 09:57:58 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-02-08 18:04:18 +11:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2023-11-14 12:27:35 +01:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-05-30 15:59:10 +02:00
2025-06-07 09:57:59 +02:00
2025-05-30 15:59:10 +02:00

Go client for the Docker Engine API

The docker command uses this package to communicate with the daemon. It can also be used by your own Go applications to do anything the command-line interface does running containers, pulling images, managing swarms, etc.

For example, to list all containers (the equivalent of docker ps --all):

package main

import (
	"context"
	"fmt"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
)

func main() {
	apiClient, err := client.NewClientWithOpts(client.FromEnv)
	if err != nil {
		panic(err)
	}
	defer apiClient.Close()

	containers, err := apiClient.ContainerList(context.Background(), container.ListOptions{All: true})
	if err != nil {
		panic(err)
	}

	for _, ctr := range containers {
		fmt.Printf("%s %s (status: %s)\n", ctr.ID, ctr.Image, ctr.Status)
	}
}

Full documentation is available on pkg.go.dev.