浏览代码

Fix for issue 9922: private registry search with auth returns 401

Signed-off-by: Don Kjer <don.kjer@gmail.com>
Don Kjer 10 年之前
父节点
当前提交
6b2eeaf896

+ 1 - 1
api/client/attach.go

@@ -26,7 +26,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 	utils.ParseFlags(cmd, args, true)
 	utils.ParseFlags(cmd, args, true)
 	name := cmd.Arg(0)
 	name := cmd.Arg(0)
 
 
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/client/commit.go

@@ -66,7 +66,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
+	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/client/cp.go

@@ -32,7 +32,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
 	copyData.Set("Resource", info[1])
 	copyData.Set("Resource", info[1])
 	copyData.Set("HostPath", cmd.Arg(1))
 	copyData.Set("HostPath", cmd.Arg(1))
 
 
-	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, nil)
 	if stream != nil {
 	if stream != nil {
 		defer stream.Close()
 		defer stream.Close()
 	}
 	}

+ 2 - 2
api/client/create.go

@@ -92,7 +92,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
 	}
 	}
 
 
 	//create the container
 	//create the container
-	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
+	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
 	//if image not found try to pull it
 	//if image not found try to pull it
 	if statusCode == 404 {
 	if statusCode == 404 {
 		repo, tag := parsers.ParseRepositoryTag(config.Image)
 		repo, tag := parsers.ParseRepositoryTag(config.Image)
@@ -106,7 +106,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
 			return nil, err
 			return nil, err
 		}
 		}
 		// Retry
 		// Retry
-		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
+		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 	} else if err != nil {
 	} else if err != nil {

+ 1 - 1
api/client/diff.go

@@ -20,7 +20,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
 
 
 	utils.ParseFlags(cmd, args, true)
 	utils.ParseFlags(cmd, args, true)
 
 
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, nil))
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 2 - 2
api/client/exec.go

@@ -24,7 +24,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 		return &utils.StatusError{StatusCode: 1}
 		return &utils.StatusError{StatusCode: 1}
 	}
 	}
 
 
-	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
+	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -49,7 +49,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 			return err
 			return err
 		}
 		}
 	} else {
 	} else {
-		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
+		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, nil)); err != nil {
 			return err
 			return err
 		}
 		}
 		// For now don't print this - wait for when we support exec wait()
 		// For now don't print this - wait for when we support exec wait()

+ 1 - 1
api/client/history.go

@@ -23,7 +23,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 
 
 	utils.ParseFlags(cmd, args, true)
 	utils.ParseFlags(cmd, args, true)
 
 
-	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 2 - 2
api/client/images.go

@@ -129,7 +129,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			v.Set("filters", filterJSON)
 			v.Set("filters", filterJSON)
 		}
 		}
 
 
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -207,7 +207,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			v.Set("all", "1")
 			v.Set("all", "1")
 		}
 		}
 
 
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
 
 
 		if err != nil {
 		if err != nil {
 			return err
 			return err

+ 1 - 1
api/client/info.go

@@ -20,7 +20,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 	utils.ParseFlags(cmd, args, false)
 	utils.ParseFlags(cmd, args, false)
 
 
-	body, _, err := readBody(cli.call("GET", "/info", nil, false))
+	body, _, err := readBody(cli.call("GET", "/info", nil, nil))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 2 - 2
api/client/inspect.go

@@ -37,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 	status := 0
 	status := 0
 
 
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil))
 		if err != nil {
 		if err != nil {
 			if strings.Contains(err.Error(), "Too many") {
 			if strings.Contains(err.Error(), "Too many") {
 				fmt.Fprintf(cli.err, "Error: %v", err)
 				fmt.Fprintf(cli.err, "Error: %v", err)
@@ -45,7 +45,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 				continue
 				continue
 			}
 			}
 
 
-			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
 			if err != nil {
 			if err != nil {
 				if strings.Contains(err.Error(), "No such") {
 				if strings.Contains(err.Error(), "No such") {
 					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
 					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)

+ 1 - 1
api/client/kill.go

@@ -19,7 +19,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
 			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
 		} else {
 		} else {

+ 1 - 1
api/client/login.go

@@ -116,7 +116,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	authconfig.ServerAddress = serverAddress
 	authconfig.ServerAddress = serverAddress
 	cli.configFile.Configs[serverAddress] = authconfig
 	cli.configFile.Configs[serverAddress] = authconfig
 
 
-	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
+	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], nil)
 	if statusCode == 401 {
 	if statusCode == 401 {
 		delete(cli.configFile.Configs, serverAddress)
 		delete(cli.configFile.Configs, serverAddress)
 		registry.SaveConfig(cli.configFile)
 		registry.SaveConfig(cli.configFile)

+ 1 - 1
api/client/logs.go

@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 
 
 	name := cmd.Arg(0)
 	name := cmd.Arg(0)
 
 
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/client/pause.go

@@ -17,7 +17,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
 			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
 		} else {
 		} else {

+ 1 - 1
api/client/port.go

@@ -19,7 +19,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 	utils.ParseFlags(cmd, args, true)
 	utils.ParseFlags(cmd, args, true)
 
 
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/client/ps.go

@@ -85,7 +85,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		v.Set("filters", filterJSON)
 		v.Set("filters", filterJSON)
 	}
 	}
 
 
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 2 - 33
api/client/pull.go

@@ -1,11 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
-	"strings"
 
 
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/graph"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -47,34 +44,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 
 
 	cli.LoadConfigFile()
 	cli.LoadConfigFile()
 
 
-	// Resolve the Auth config relevant for this server
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
-
-	pull := func(authConfig registry.AuthConfig) error {
-		buf, err := json.Marshal(authConfig)
-		if err != nil {
-			return err
-		}
-		registryAuthHeader := []string{
-			base64.URLEncoding.EncodeToString(buf),
-		}
-
-		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
-			"X-Registry-Auth": registryAuthHeader,
-		})
-	}
-
-	if err := pull(authConfig); err != nil {
-		if strings.Contains(err.Error(), "Status 401") {
-			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
-				return err
-			}
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
-			return pull(authConfig)
-		}
-		return err
-	}
-
-	return nil
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
+	return err
 }
 }

+ 2 - 29
api/client/push.go

@@ -1,11 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
-	"strings"
 
 
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
@@ -50,30 +47,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("tag", tag)
 	v.Set("tag", tag)
 
 
-	push := func(authConfig registry.AuthConfig) error {
-		buf, err := json.Marshal(authConfig)
-		if err != nil {
-			return err
-		}
-		registryAuthHeader := []string{
-			base64.URLEncoding.EncodeToString(buf),
-		}
-
-		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
-			"X-Registry-Auth": registryAuthHeader,
-		})
-	}
-
-	if err := push(authConfig); err != nil {
-		if strings.Contains(err.Error(), "Status 401") {
-			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
-				return err
-			}
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
-			return push(authConfig)
-		}
-		return err
-	}
-	return nil
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
+	return err
 }
 }

+ 1 - 1
api/client/rename.go

@@ -18,7 +18,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
 	oldName := cmd.Arg(0)
 	oldName := cmd.Arg(0)
 	newName := cmd.Arg(1)
 	newName := cmd.Arg(1)
 
 
-	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName), nil, false)); err != nil {
+	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName), nil, nil)); err != nil {
 		fmt.Fprintf(cli.err, "%s\n", err)
 		fmt.Fprintf(cli.err, "%s\n", err)
 		return fmt.Errorf("Error: failed to rename container named %s", oldName)
 		return fmt.Errorf("Error: failed to rename container named %s", oldName)
 	}
 	}

+ 1 - 1
api/client/restart.go

@@ -24,7 +24,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil))
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
 			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")

+ 1 - 1
api/client/rm.go

@@ -31,7 +31,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil))
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
 			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")

+ 1 - 1
api/client/rmi.go

@@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil))
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")

+ 3 - 3
api/client/run.go

@@ -183,14 +183,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 
 
 	defer func() {
 	defer func() {
 		if *flAutoRemove {
 		if *flAutoRemove {
-			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
+			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, nil)); err != nil {
 				log.Errorf("Error deleting container: %s", err)
 				log.Errorf("Error deleting container: %s", err)
 			}
 			}
 		}
 		}
 	}()
 	}()
 
 
 	//start the container
 	//start the container
-	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
+	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, nil)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -220,7 +220,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	if *flAutoRemove {
 	if *flAutoRemove {
 		// Autoremove: wait for the container to finish, retrieve
 		// Autoremove: wait for the container to finish, retrieve
 		// the exit code and remove the container
 		// the exit code and remove the container
-		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
+		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, nil)); err != nil {
 			return err
 			return err
 		}
 		}
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {

+ 16 - 3
api/client/search.go

@@ -8,6 +8,8 @@ import (
 
 
 	"github.com/docker/docker/engine"
 	"github.com/docker/docker/engine"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/parsers"
+	"github.com/docker/docker/registry"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
 
 
@@ -24,16 +26,27 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 
 
 	utils.ParseFlags(cmd, args, true)
 	utils.ParseFlags(cmd, args, true)
 
 
+	name := cmd.Arg(0)
 	v := url.Values{}
 	v := url.Values{}
-	v.Set("term", cmd.Arg(0))
+	v.Set("term", name)
 
 
-	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
+	// Resolve the Repository name from fqn to hostname + name
+	taglessRemote, _ := parsers.ParseRepositoryTag(name)
+	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
+	if err != nil {
+		return err
+	}
 
 
+	cli.LoadConfigFile()
+
+	body, statusCode, errReq := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, repoInfo.Index, "search")
+	rawBody, _, err := readBody(body, statusCode, errReq)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
 	outs := engine.NewTable("star_count", 0)
 	outs := engine.NewTable("star_count", 0)
-	if _, err := outs.ReadListFrom(body); err != nil {
+	if _, err := outs.ReadListFrom(rawBody); err != nil {
 		return err
 		return err
 	}
 	}
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)

+ 3 - 3
api/client/start.go

@@ -32,7 +32,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
 			if sig == "" {
 			if sig == "" {
 				log.Errorf("Unsupported signal: %v. Discarding.", s)
 				log.Errorf("Unsupported signal: %v. Discarding.", s)
 			}
 			}
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil {
 				log.Debugf("Error sending signal: %s", err)
 				log.Debugf("Error sending signal: %s", err)
 			}
 			}
 		}
 		}
@@ -61,7 +61,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
 		}
 		}
 
 
-		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
+		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -121,7 +121,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
 		if err != nil {
 		if err != nil {
 			if !*attach && !*openStdin {
 			if !*attach && !*openStdin {
 				// attach and openStdin is false means it could be starting multiple containers
 				// attach and openStdin is false means it could be starting multiple containers

+ 1 - 1
api/client/stats.go

@@ -29,7 +29,7 @@ type containerStats struct {
 }
 }
 
 
 func (s *containerStats) Collect(cli *DockerCli) {
 func (s *containerStats) Collect(cli *DockerCli) {
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
 	if err != nil {
 	if err != nil {
 		s.err = err
 		s.err = err
 		return
 		return

+ 1 - 1
api/client/stop.go

@@ -26,7 +26,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil))
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
 			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")

+ 1 - 1
api/client/tag.go

@@ -35,7 +35,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
 		v.Set("force", "1")
 		v.Set("force", "1")
 	}
 	}
 
 
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, nil)); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil

+ 1 - 1
api/client/top.go

@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
 		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
 		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
 	}
 	}
 
 
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/client/unpause.go

@@ -17,7 +17,7 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
 
 
 	var encounteredError error
 	var encounteredError error
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
 			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
 		} else {
 		} else {

+ 100 - 78
api/client/utils.go

@@ -54,124 +54,146 @@ func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
 	return params, nil
 	return params, nil
 }
 }
 
 
-func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
-	params, err := cli.encodeData(data)
-	if err != nil {
-		return nil, -1, err
+func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, string, int, error) {
+	expectedPayload := (method == "POST" || method == "PUT")
+	if expectedPayload && in == nil {
+		in = bytes.NewReader([]byte{})
 	}
 	}
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
 	if err != nil {
 	if err != nil {
-		return nil, -1, err
-	}
-	if passAuthInfo {
-		cli.LoadConfigFile()
-		// Resolve the Auth config relevant for this server
-		authConfig := cli.configFile.Configs[registry.IndexServerAddress()]
-		getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
-			buf, err := json.Marshal(authConfig)
-			if err != nil {
-				return nil, err
-			}
-			registryAuthHeader := []string{
-				base64.URLEncoding.EncodeToString(buf),
-			}
-			return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
-		}
-		if headers, err := getHeaders(authConfig); err == nil && headers != nil {
-			for k, v := range headers {
-				req.Header[k] = v
-			}
-		}
+		return nil, "", -1, err
 	}
 	}
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
 	req.URL.Host = cli.addr
 	req.URL.Host = cli.addr
 	req.URL.Scheme = cli.scheme
 	req.URL.Scheme = cli.scheme
-	if data != nil {
-		req.Header.Set("Content-Type", "application/json")
-	} else if method == "POST" {
+
+	if headers != nil {
+		for k, v := range headers {
+			req.Header[k] = v
+		}
+	}
+
+	if expectedPayload && req.Header.Get("Content-Type") == "" {
 		req.Header.Set("Content-Type", "text/plain")
 		req.Header.Set("Content-Type", "text/plain")
 	}
 	}
+
 	resp, err := cli.HTTPClient().Do(req)
 	resp, err := cli.HTTPClient().Do(req)
+	statusCode := -1
+	if resp != nil {
+		statusCode = resp.StatusCode
+	}
 	if err != nil {
 	if err != nil {
 		if strings.Contains(err.Error(), "connection refused") {
 		if strings.Contains(err.Error(), "connection refused") {
-			return nil, -1, ErrConnectionRefused
+			return nil, "", statusCode, ErrConnectionRefused
 		}
 		}
 
 
 		if cli.tlsConfig == nil {
 		if cli.tlsConfig == nil {
-			return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
+			return nil, "", statusCode, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
 		}
 		}
-		return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err)
-
+		return nil, "", statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
 	}
 	}
 
 
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
+	if statusCode < 200 || statusCode >= 400 {
 		body, err := ioutil.ReadAll(resp.Body)
 		body, err := ioutil.ReadAll(resp.Body)
 		if err != nil {
 		if err != nil {
-			return nil, -1, err
+			return nil, "", statusCode, err
 		}
 		}
 		if len(body) == 0 {
 		if len(body) == 0 {
-			return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
+			return nil, "", statusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(statusCode), req.URL)
 		}
 		}
-		return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
+		return nil, "", statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
 	}
 	}
 
 
-	return resp.Body, resp.StatusCode, nil
+	return resp.Body, resp.Header.Get("Content-Type"), statusCode, nil
 }
 }
 
 
-func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
-	return cli.streamHelper(method, path, true, in, out, nil, headers)
-}
+func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
+	cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
+		buf, err := json.Marshal(authConfig)
+		if err != nil {
+			return nil, -1, err
+		}
+		registryAuthHeader := []string{
+			base64.URLEncoding.EncodeToString(buf),
+		}
 
 
-func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
-	if (method == "POST" || method == "PUT") && in == nil {
-		in = bytes.NewReader([]byte{})
+		// begin the request
+		body, contentType, statusCode, err := cli.clientRequest(method, path, in, map[string][]string{
+			"X-Registry-Auth": registryAuthHeader,
+		})
+		if err == nil && out != nil {
+			// If we are streaming output, complete the stream since
+			// errors may not appear until later.
+			err = cli.streamBody(body, contentType, true, out, nil)
+		}
+		if err != nil {
+			// Since errors in a stream appear after status 200 has been written,
+			// we may need to change the status code.
+			if strings.Contains(err.Error(), "Authentication is required") ||
+				strings.Contains(err.Error(), "Status 401") ||
+				strings.Contains(err.Error(), "status code 401") {
+				statusCode = http.StatusUnauthorized
+			}
+		}
+		return body, statusCode, err
 	}
 	}
 
 
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
-	if err != nil {
-		return err
+	// Resolve the Auth config relevant for this server
+	authConfig := cli.configFile.ResolveAuthConfig(index)
+	body, statusCode, err := cmdAttempt(authConfig)
+	if statusCode == http.StatusUnauthorized {
+		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
+		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
+			return nil, -1, err
+		}
+		authConfig = cli.configFile.ResolveAuthConfig(index)
+		return cmdAttempt(authConfig)
 	}
 	}
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
-	req.URL.Host = cli.addr
-	req.URL.Scheme = cli.scheme
-	if method == "POST" {
-		req.Header.Set("Content-Type", "text/plain")
+	return body, statusCode, err
+}
+
+func (cli *DockerCli) call(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
+	params, err := cli.encodeData(data)
+	if err != nil {
+		return nil, -1, err
 	}
 	}
 
 
-	if headers != nil {
-		for k, v := range headers {
-			req.Header[k] = v
+	if data != nil {
+		if headers == nil {
+			headers = make(map[string][]string)
 		}
 		}
+		headers["Content-Type"] = []string{"application/json"}
 	}
 	}
-	resp, err := cli.HTTPClient().Do(req)
+
+	body, _, statusCode, err := cli.clientRequest(method, path, params, headers)
+	return body, statusCode, err
+}
+
+func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
+	return cli.streamHelper(method, path, true, in, out, nil, headers)
+}
+
+func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
+	body, contentType, _, err := cli.clientRequest(method, path, in, headers)
 	if err != nil {
 	if err != nil {
-		if strings.Contains(err.Error(), "connection refused") {
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
-		}
 		return err
 		return err
 	}
 	}
-	defer resp.Body.Close()
+	return cli.streamBody(body, contentType, setRawTerminal, stdout, stderr)
+}
 
 
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
-		body, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return err
-		}
-		if len(body) == 0 {
-			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
-		}
-		return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
-	}
+func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawTerminal bool, stdout, stderr io.Writer) error {
+	defer body.Close()
 
 
-	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
-		return jsonmessage.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut)
+	if api.MatchesContentType(contentType, "application/json") {
+		return jsonmessage.DisplayJSONMessagesStream(body, stdout, cli.outFd, cli.isTerminalOut)
 	}
 	}
 	if stdout != nil || stderr != nil {
 	if stdout != nil || stderr != nil {
 		// When TTY is ON, use regular copy
 		// When TTY is ON, use regular copy
+		var err error
 		if setRawTerminal {
 		if setRawTerminal {
-			_, err = io.Copy(stdout, resp.Body)
+			_, err = io.Copy(stdout, body)
 		} else {
 		} else {
-			_, err = stdcopy.StdCopy(stdout, stderr, resp.Body)
+			_, err = stdcopy.StdCopy(stdout, stderr, body)
 		}
 		}
 		log.Debugf("[stream] End of stdout")
 		log.Debugf("[stream] End of stdout")
 		return err
 		return err
@@ -195,13 +217,13 @@ func (cli *DockerCli) resizeTty(id string, isExec bool) {
 		path = "/exec/" + id + "/resize?"
 		path = "/exec/" + id + "/resize?"
 	}
 	}
 
 
-	if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil {
+	if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, nil)); err != nil {
 		log.Debugf("Error resize: %s", err)
 		log.Debugf("Error resize: %s", err)
 	}
 	}
 }
 }
 
 
 func waitForExit(cli *DockerCli, containerID string) (int, error) {
 func waitForExit(cli *DockerCli, containerID string) (int, error) {
-	stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, false)
+	stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, nil)
 	if err != nil {
 	if err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
@@ -216,7 +238,7 @@ func waitForExit(cli *DockerCli, containerID string) (int, error) {
 // getExitCode perform an inspect on the container. It returns
 // getExitCode perform an inspect on the container. It returns
 // the running state and the exit code.
 // the running state and the exit code.
 func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
 func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
-	stream, _, err := cli.call("GET", "/containers/"+containerID+"/json", nil, false)
+	stream, _, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
 	if err != nil {
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		// If we can't connect, then the daemon probably died.
 		if err != ErrConnectionRefused {
 		if err != ErrConnectionRefused {
@@ -237,7 +259,7 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
 // getExecExitCode perform an inspect on the exec command. It returns
 // getExecExitCode perform an inspect on the exec command. It returns
 // the running state and the exit code.
 // the running state and the exit code.
 func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
 func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
-	stream, _, err := cli.call("GET", "/exec/"+execID+"/json", nil, false)
+	stream, _, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
 	if err != nil {
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		// If we can't connect, then the daemon probably died.
 		if err != ErrConnectionRefused {
 		if err != ErrConnectionRefused {

+ 1 - 1
api/client/version.go

@@ -33,7 +33,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
 	}
 	}
 	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
 	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
 
 
-	body, _, err := readBody(cli.call("GET", "/version", nil, false))
+	body, _, err := readBody(cli.call("GET", "/version", nil, nil))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 7 - 44
registry/auth.go

@@ -1,7 +1,6 @@
 package registry
 package registry
 
 
 import (
 import (
-	"crypto/tls"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
@@ -71,21 +70,7 @@ func (auth *RequestAuthorization) getToken() (string, error) {
 		return auth.tokenCache, nil
 		return auth.tokenCache, nil
 	}
 	}
 
 
-	tlsConfig := tls.Config{
-		MinVersion: tls.VersionTLS10,
-	}
-	if !auth.registryEndpoint.IsSecure {
-		tlsConfig.InsecureSkipVerify = true
-	}
-
-	client := &http.Client{
-		Transport: &http.Transport{
-			DisableKeepAlives: true,
-			Proxy:             http.ProxyFromEnvironment,
-			TLSClientConfig:   &tlsConfig,
-		},
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
-	}
+	client := auth.registryEndpoint.HTTPClient()
 	factory := HTTPRequestFactory(nil)
 	factory := HTTPRequestFactory(nil)
 
 
 	for _, challenge := range auth.registryEndpoint.AuthChallenges {
 	for _, challenge := range auth.registryEndpoint.AuthChallenges {
@@ -252,16 +237,10 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HT
 // loginV1 tries to register/login to the v1 registry server.
 // loginV1 tries to register/login to the v1 registry server.
 func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
 func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
 	var (
 	var (
-		status  string
-		reqBody []byte
-		err     error
-		client  = &http.Client{
-			Transport: &http.Transport{
-				DisableKeepAlives: true,
-				Proxy:             http.ProxyFromEnvironment,
-			},
-			CheckRedirect: AddRequiredHeadersToRedirectedRequests,
-		}
+		status        string
+		reqBody       []byte
+		err           error
+		client        = registryEndpoint.HTTPClient()
 		reqStatusCode = 0
 		reqStatusCode = 0
 		serverAddress = authConfig.ServerAddress
 		serverAddress = authConfig.ServerAddress
 	)
 	)
@@ -285,7 +264,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
 
 
 	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
 	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
 	b := strings.NewReader(string(jsonBody))
 	b := strings.NewReader(string(jsonBody))
-	req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
+	req1, err := client.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("Server Error: %s", err)
 		return "", fmt.Errorf("Server Error: %s", err)
 	}
 	}
@@ -371,26 +350,10 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
 // is to be determined.
 // is to be determined.
 func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
 func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
 	log.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
 	log.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
-
-	tlsConfig := tls.Config{
-		MinVersion: tls.VersionTLS10,
-	}
-	if !registryEndpoint.IsSecure {
-		tlsConfig.InsecureSkipVerify = true
-	}
-
-	client := &http.Client{
-		Transport: &http.Transport{
-			DisableKeepAlives: true,
-			Proxy:             http.ProxyFromEnvironment,
-			TLSClientConfig:   &tlsConfig,
-		},
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
-	}
-
 	var (
 	var (
 		err       error
 		err       error
 		allErrors []error
 		allErrors []error
+		client    = registryEndpoint.HTTPClient()
 	)
 	)
 
 
 	for _, challenge := range registryEndpoint.AuthChallenges {
 	for _, challenge := range registryEndpoint.AuthChallenges {

+ 18 - 0
registry/endpoint.go

@@ -1,6 +1,7 @@
 package registry
 package registry
 
 
 import (
 import (
+	"crypto/tls"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
@@ -262,3 +263,20 @@ HeaderLoop:
 
 
 	return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode))
 	return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode))
 }
 }
+
+func (e *Endpoint) HTTPClient() *http.Client {
+	tlsConfig := tls.Config{
+		MinVersion: tls.VersionTLS10,
+	}
+	if !e.IsSecure {
+		tlsConfig.InsecureSkipVerify = true
+	}
+	return &http.Client{
+		Transport: &http.Transport{
+			DisableKeepAlives: true,
+			Proxy:             http.ProxyFromEnvironment,
+			TLSClientConfig:   &tlsConfig,
+		},
+		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
+	}
+}

+ 4 - 0
registry/session.go

@@ -511,6 +511,10 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
 
 
+	if res.StatusCode == 401 {
+		return nil, errLoginRequired
+	}
+
 	var tokens, endpoints []string
 	var tokens, endpoints []string
 	if !validate {
 	if !validate {
 		if res.StatusCode != 200 && res.StatusCode != 201 {
 		if res.StatusCode != 200 && res.StatusCode != 201 {