Browse code

Merge pull request #19984 from calavera/vendor_engine_api_master

Vendor engine-api with client context changes.

Tibor Vass authored on 2016/02/05 06:20:06
Showing 54 changed files
... ...
@@ -14,6 +14,8 @@ import (
14 14
 	"runtime"
15 15
 	"strings"
16 16
 
17
+	"golang.org/x/net/context"
18
+
17 19
 	"github.com/docker/docker/api"
18 20
 	"github.com/docker/docker/builder/dockerignore"
19 21
 	Cli "github.com/docker/docker/cli"
... ...
@@ -77,8 +79,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
77 77
 	cmd.ParseFlags(args, true)
78 78
 
79 79
 	var (
80
-		context io.ReadCloser
81
-		err     error
80
+		ctx io.ReadCloser
81
+		err error
82 82
 	)
83 83
 
84 84
 	specifiedContext := cmd.Arg(0)
... ...
@@ -100,11 +102,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
100 100
 
101 101
 	switch {
102 102
 	case specifiedContext == "-":
103
-		context, relDockerfile, err = getContextFromReader(cli.in, *dockerfileName)
103
+		ctx, relDockerfile, err = getContextFromReader(cli.in, *dockerfileName)
104 104
 	case urlutil.IsGitURL(specifiedContext):
105 105
 		tempDir, relDockerfile, err = getContextFromGitURL(specifiedContext, *dockerfileName)
106 106
 	case urlutil.IsURL(specifiedContext):
107
-		context, relDockerfile, err = getContextFromURL(progBuff, specifiedContext, *dockerfileName)
107
+		ctx, relDockerfile, err = getContextFromURL(progBuff, specifiedContext, *dockerfileName)
108 108
 	default:
109 109
 		contextDir, relDockerfile, err = getContextFromLocalDir(specifiedContext, *dockerfileName)
110 110
 	}
... ...
@@ -121,7 +123,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
121 121
 		contextDir = tempDir
122 122
 	}
123 123
 
124
-	if context == nil {
124
+	if ctx == nil {
125 125
 		// And canonicalize dockerfile name to a platform-independent one
126 126
 		relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
127 127
 		if err != nil {
... ...
@@ -159,7 +161,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
159 159
 			includes = append(includes, ".dockerignore", relDockerfile)
160 160
 		}
161 161
 
162
-		context, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
162
+		ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
163 163
 			Compression:     archive.Uncompressed,
164 164
 			ExcludePatterns: excludes,
165 165
 			IncludeFiles:    includes,
... ...
@@ -173,13 +175,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
173 173
 	if isTrusted() {
174 174
 		// Wrap the tar archive to replace the Dockerfile entry with the rewritten
175 175
 		// Dockerfile which uses trusted pulls.
176
-		context = replaceDockerfileTarWrapper(context, relDockerfile, cli.trustedReference, &resolvedTags)
176
+		ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
177 177
 	}
178 178
 
179 179
 	// Setup an upload progress bar
180 180
 	progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
181 181
 
182
-	var body io.Reader = progress.NewProgressReader(context, progressOutput, 0, "", "Sending build context to Docker daemon")
182
+	var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
183 183
 
184 184
 	var memory int64
185 185
 	if *flMemoryString != "" {
... ...
@@ -235,7 +237,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
235 235
 		AuthConfigs:    cli.configFile.AuthConfigs,
236 236
 	}
237 237
 
238
-	response, err := cli.client.ImageBuild(options)
238
+	response, err := cli.client.ImageBuild(context.Background(), options)
239 239
 	if err != nil {
240 240
 		return err
241 241
 	}
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/docker/opts"
16 16
 	"github.com/docker/docker/pkg/term"
17 17
 	"github.com/docker/engine-api/client"
18
+	"github.com/docker/go-connections/sockets"
18 19
 	"github.com/docker/go-connections/tlsconfig"
19 20
 )
20 21
 
... ...
@@ -142,12 +143,12 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
142 142
 			verStr = tmpStr
143 143
 		}
144 144
 
145
-		clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions)
145
+		httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
146 146
 		if err != nil {
147 147
 			return err
148 148
 		}
149 149
 
150
-		client, err := client.NewClient(host, verStr, clientTransport, customHeaders)
150
+		client, err := client.NewClient(host, verStr, httpClient, customHeaders)
151 151
 		if err != nil {
152 152
 			return err
153 153
 		}
... ...
@@ -180,16 +181,27 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
180 180
 	return
181 181
 }
182 182
 
183
-func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) {
183
+func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
184 184
 	if tlsOptions == nil {
185
-		return &http.Transport{}, nil
185
+		// let the api client configure the default transport.
186
+		return nil, nil
186 187
 	}
187 188
 
188 189
 	config, err := tlsconfig.Client(*tlsOptions)
189 190
 	if err != nil {
190 191
 		return nil, err
191 192
 	}
192
-	return &http.Transport{
193
+	tr := &http.Transport{
193 194
 		TLSClientConfig: config,
195
+	}
196
+	proto, addr, _, err := client.ParseHost(host)
197
+	if err != nil {
198
+		return nil, err
199
+	}
200
+
201
+	sockets.ConfigureTransport(tr, proto, addr)
202
+
203
+	return &http.Client{
204
+		Transport: tr,
194 205
 	}, nil
195 206
 }
... ...
@@ -7,6 +7,8 @@ import (
7 7
 	"path/filepath"
8 8
 	"strings"
9 9
 
10
+	"golang.org/x/net/context"
11
+
10 12
 	Cli "github.com/docker/docker/cli"
11 13
 	"github.com/docker/docker/pkg/archive"
12 14
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -165,7 +167,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
165 165
 
166 166
 	}
167 167
 
168
-	content, stat, err := cli.client.CopyFromContainer(srcContainer, srcPath)
168
+	content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
169 169
 	if err != nil {
170 170
 		return err
171 171
 	}
... ...
@@ -292,5 +294,5 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
292 292
 		AllowOverwriteDirWithFile: false,
293 293
 	}
294 294
 
295
-	return cli.client.CopyToContainer(options)
295
+	return cli.client.CopyToContainer(context.Background(), options)
296 296
 }
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"io"
6 6
 	"os"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	Cli "github.com/docker/docker/cli"
9 11
 	"github.com/docker/docker/pkg/jsonmessage"
10 12
 	"github.com/docker/docker/reference"
... ...
@@ -52,7 +54,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
52 52
 		RegistryAuth: encodedAuth,
53 53
 	}
54 54
 
55
-	responseBody, err := cli.client.ImageCreate(options)
55
+	responseBody, err := cli.client.ImageCreate(context.Background(), options)
56 56
 	if err != nil {
57 57
 		return err
58 58
 	}
... ...
@@ -8,6 +8,8 @@ import (
8 8
 	"strings"
9 9
 	"time"
10 10
 
11
+	"golang.org/x/net/context"
12
+
11 13
 	Cli "github.com/docker/docker/cli"
12 14
 	"github.com/docker/docker/opts"
13 15
 	"github.com/docker/docker/pkg/jsonlog"
... ...
@@ -48,7 +50,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
48 48
 		Filters: eventFilterArgs,
49 49
 	}
50 50
 
51
-	responseBody, err := cli.client.Events(options)
51
+	responseBody, err := cli.client.Events(context.Background(), options)
52 52
 	if err != nil {
53 53
 		return err
54 54
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"errors"
5 5
 	"io"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	flag "github.com/docker/docker/pkg/mflag"
9 11
 )
... ...
@@ -24,7 +26,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
24 24
 		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
25 25
 	}
26 26
 
27
-	responseBody, err := cli.client.ContainerExport(cmd.Arg(0))
27
+	responseBody, err := cli.client.ContainerExport(context.Background(), cmd.Arg(0))
28 28
 	if err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"io"
6 6
 	"os"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	Cli "github.com/docker/docker/cli"
9 11
 	"github.com/docker/docker/opts"
10 12
 	"github.com/docker/docker/pkg/jsonmessage"
... ...
@@ -70,7 +72,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
70 70
 		Changes:        changes,
71 71
 	}
72 72
 
73
-	responseBody, err := cli.client.ImageImport(options)
73
+	responseBody, err := cli.client.ImageImport(context.Background(), options)
74 74
 	if err != nil {
75 75
 		return err
76 76
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"io"
5 5
 	"os"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	"github.com/docker/docker/pkg/jsonmessage"
9 11
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -30,7 +32,7 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
30 30
 		input = file
31 31
 	}
32 32
 
33
-	response, err := cli.client.ImageLoad(input)
33
+	response, err := cli.client.ImageLoad(context.Background(), input, true)
34 34
 	if err != nil {
35 35
 		return err
36 36
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	flag "github.com/docker/docker/pkg/mflag"
9 11
 	"github.com/docker/docker/pkg/stdcopy"
... ...
@@ -48,7 +50,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
48 48
 		Follow:      *follow,
49 49
 		Tail:        *tail,
50 50
 	}
51
-	responseBody, err := cli.client.ContainerLogs(options)
51
+	responseBody, err := cli.client.ContainerLogs(context.Background(), options)
52 52
 	if err != nil {
53 53
 		return err
54 54
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"errors"
5 5
 	"fmt"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	"github.com/docker/docker/pkg/jsonmessage"
9 11
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -77,7 +79,7 @@ func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, imageID,
77 77
 		RegistryAuth: encodedAuth,
78 78
 	}
79 79
 
80
-	responseBody, err := cli.client.ImagePull(options, requestPrivilege)
80
+	responseBody, err := cli.client.ImagePull(context.Background(), options, requestPrivilege)
81 81
 	if err != nil {
82 82
 		return err
83 83
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"errors"
5 5
 	"io"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	"github.com/docker/docker/pkg/jsonmessage"
9 11
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -70,5 +72,5 @@ func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, imageID,
70 70
 		RegistryAuth: encodedAuth,
71 71
 	}
72 72
 
73
-	return cli.client.ImagePush(options, requestPrivilege)
73
+	return cli.client.ImagePush(context.Background(), options, requestPrivilege)
74 74
 }
... ...
@@ -7,6 +7,8 @@ import (
7 7
 	"runtime"
8 8
 	"strings"
9 9
 
10
+	"golang.org/x/net/context"
11
+
10 12
 	"github.com/Sirupsen/logrus"
11 13
 	Cli "github.com/docker/docker/cli"
12 14
 	derr "github.com/docker/docker/errors"
... ...
@@ -269,7 +271,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
269 269
 
270 270
 		// Autoremove: wait for the container to finish, retrieve
271 271
 		// the exit code and remove the container
272
-		if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
272
+		if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
273 273
 			return runStartContainerErr(err)
274 274
 		}
275 275
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
... ...
@@ -279,7 +281,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
279 279
 		// No Autoremove: Simply retrieve the exit code
280 280
 		if !config.Tty {
281 281
 			// In non-TTY mode, we can't detach, so we must wait for container exit
282
-			if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
282
+			if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
283 283
 				return err
284 284
 			}
285 285
 		} else {
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"errors"
5 5
 	"io"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	flag "github.com/docker/docker/pkg/mflag"
9 11
 )
... ...
@@ -24,7 +26,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
24 24
 		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
25 25
 	}
26 26
 
27
-	responseBody, err := cli.client.ImageSave(cmd.Args())
27
+	responseBody, err := cli.client.ImageSave(context.Background(), cmd.Args())
28 28
 	if err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -10,6 +10,8 @@ import (
10 10
 	"text/tabwriter"
11 11
 	"time"
12 12
 
13
+	"golang.org/x/net/context"
14
+
13 15
 	Cli "github.com/docker/docker/cli"
14 16
 	"github.com/docker/engine-api/types"
15 17
 	"github.com/docker/engine-api/types/events"
... ...
@@ -37,7 +39,7 @@ type stats struct {
37 37
 }
38 38
 
39 39
 func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
40
-	responseBody, err := cli.client.ContainerStats(s.Name, streamStats)
40
+	responseBody, err := cli.client.ContainerStats(context.Background(), s.Name, streamStats)
41 41
 	if err != nil {
42 42
 		s.mu.Lock()
43 43
 		s.err = err
... ...
@@ -195,7 +197,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
195 195
 			options := types.EventsOptions{
196 196
 				Filters: f,
197 197
 			}
198
-			resBody, err := cli.client.Events(options)
198
+			resBody, err := cli.client.Events(context.Background(), options)
199 199
 			if err != nil {
200 200
 				c <- watch{err: err}
201 201
 				return
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	flag "github.com/docker/docker/pkg/mflag"
9 11
 )
... ...
@@ -21,7 +23,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
21 21
 
22 22
 	var errs []string
23 23
 	for _, name := range cmd.Args() {
24
-		status, err := cli.client.ContainerWait(name)
24
+		status, err := cli.client.ContainerWait(context.Background(), name)
25 25
 		if err != nil {
26 26
 			errs = append(errs, err.Error())
27 27
 		} else {
... ...
@@ -7,7 +7,7 @@ source 'hack/.vendor-helpers.sh'
7 7
 
8 8
 # the following lines are in sorted order, FYI
9 9
 clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
10
-clone git github.com/Microsoft/go-winio 2b085935f02c272e7a1855df6f8fe03029ffcadd
10
+clone git github.com/Microsoft/go-winio eb176a9831c54b88eaf9eb4fbc24b94080d910ad
11 11
 clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps
12 12
 clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
13 13
 clone git github.com/go-check/check 11d3bc7aa68e238947792f30573146a3231fc0f1
... ...
@@ -23,8 +23,8 @@ clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b
23 23
 clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://github.com/golang/net.git
24 24
 clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
25 25
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
26
-clone git github.com/docker/go-connections v0.1.2
27
-clone git github.com/docker/engine-api bdbab71ec21209ef56dffdbe42c9d21843c30862
26
+clone git github.com/docker/go-connections v0.1.3
27
+clone git github.com/docker/engine-api 9a940e4ead265e18d4feb9e3c515428966a08278
28 28
 clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
29 29
 clone git github.com/imdario/mergo 0.2.1
30 30
 
... ...
@@ -194,7 +194,7 @@ func (d *Daemon) getClientConfig() (*clientConfig, error) {
194 194
 		transport = &http.Transport{}
195 195
 	}
196 196
 
197
-	sockets.ConfigureTCPTransport(transport, proto, addr)
197
+	sockets.ConfigureTransport(transport, proto, addr)
198 198
 
199 199
 	return &clientConfig{
200 200
 		transport: transport,
... ...
@@ -30,7 +30,7 @@ func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
30 30
 	tr.TLSClientConfig = c
31 31
 
32 32
 	protoAndAddr := strings.Split(addr, "://")
33
-	sockets.ConfigureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1])
33
+	sockets.ConfigureTransport(tr, protoAndAddr[0], protoAndAddr[1])
34 34
 
35 35
 	scheme := protoAndAddr[0]
36 36
 	if scheme != "https" {
... ...
@@ -65,7 +65,7 @@ func LookupSidByName(name string) (sid string, err error) {
65 65
 	if err != nil {
66 66
 		return "", &AccountLookupError{name, err}
67 67
 	}
68
-	sid = syscall.UTF16ToString((*[1 << 30]uint16)(unsafe.Pointer(strBuffer))[:])
68
+	sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
69 69
 	localFree(uintptr(unsafe.Pointer(strBuffer)))
70 70
 	return sid, nil
71 71
 }
... ...
@@ -1,16 +1,14 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"crypto/tls"
5 4
 	"fmt"
6
-	"net"
7 5
 	"net/http"
8 6
 	"net/url"
9 7
 	"os"
10 8
 	"path/filepath"
11 9
 	"strings"
12
-	"time"
13 10
 
11
+	"github.com/docker/engine-api/client/transport"
14 12
 	"github.com/docker/go-connections/tlsconfig"
15 13
 )
16 14
 
... ...
@@ -21,17 +19,13 @@ type Client struct {
21 21
 	proto string
22 22
 	// addr holds the client address.
23 23
 	addr string
24
-	// basePath holds the path to prepend to the requests
24
+	// basePath holds the path to prepend to the requests.
25 25
 	basePath string
26
-	// scheme holds the scheme of the client i.e. https.
27
-	scheme string
28
-	// tlsConfig holds the tls configuration to use in hijacked requests.
29
-	tlsConfig *tls.Config
30
-	// httpClient holds the client transport instance. Exported to keep the old code running.
31
-	httpClient *http.Client
26
+	// transport is the interface to sends request with, it implements transport.Client.
27
+	transport transport.Client
32 28
 	// version of the server to talk to.
33 29
 	version string
34
-	// custom http headers configured by users
30
+	// custom http headers configured by users.
35 31
 	customHTTPHeaders map[string]string
36 32
 }
37 33
 
... ...
@@ -41,7 +35,7 @@ type Client struct {
41 41
 // Use DOCKER_CERT_PATH to load the tls certificates from.
42 42
 // Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
43 43
 func NewEnvClient() (*Client, error) {
44
-	var transport *http.Transport
44
+	var client *http.Client
45 45
 	if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
46 46
 		options := tlsconfig.Options{
47 47
 			CAFile:             filepath.Join(dockerCertPath, "ca.pem"),
... ...
@@ -54,8 +48,10 @@ func NewEnvClient() (*Client, error) {
54 54
 			return nil, err
55 55
 		}
56 56
 
57
-		transport = &http.Transport{
58
-			TLSClientConfig: tlsc,
57
+		client = &http.Client{
58
+			Transport: &http.Transport{
59
+				TLSClientConfig: tlsc,
60
+			},
59 61
 		}
60 62
 	}
61 63
 
... ...
@@ -63,42 +59,29 @@ func NewEnvClient() (*Client, error) {
63 63
 	if host == "" {
64 64
 		host = DefaultDockerHost
65 65
 	}
66
-	return NewClient(host, os.Getenv("DOCKER_API_VERSION"), transport, nil)
66
+	return NewClient(host, os.Getenv("DOCKER_API_VERSION"), client, nil)
67 67
 }
68 68
 
69 69
 // NewClient initializes a new API client for the given host and API version.
70 70
 // It won't send any version information if the version number is empty.
71
-// It uses the transport to create a new http client.
71
+// It uses the given http client as transport.
72 72
 // It also initializes the custom http headers to add to each request.
73
-func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
74
-	var (
75
-		basePath       string
76
-		scheme         = "http"
77
-		protoAddrParts = strings.SplitN(host, "://", 2)
78
-		proto, addr    = protoAddrParts[0], protoAddrParts[1]
79
-	)
80
-
81
-	if proto == "tcp" {
82
-		parsed, err := url.Parse("tcp://" + addr)
83
-		if err != nil {
84
-			return nil, err
85
-		}
86
-		addr = parsed.Host
87
-		basePath = parsed.Path
73
+func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
74
+	proto, addr, basePath, err := ParseHost(host)
75
+	if err != nil {
76
+		return nil, err
88 77
 	}
89 78
 
90
-	transport = configureTransport(transport, proto, addr)
91
-	if transport.TLSClientConfig != nil {
92
-		scheme = "https"
79
+	transport, err := transport.NewTransportWithHTTP(proto, addr, client)
80
+	if err != nil {
81
+		return nil, err
93 82
 	}
94 83
 
95 84
 	return &Client{
96 85
 		proto:             proto,
97 86
 		addr:              addr,
98 87
 		basePath:          basePath,
99
-		scheme:            scheme,
100
-		tlsConfig:         transport.TLSClientConfig,
101
-		httpClient:        &http.Client{Transport: transport},
88
+		transport:         transport,
102 89
 		version:           version,
103 90
 		customHTTPHeaders: httpHeaders,
104 91
 	}, nil
... ...
@@ -127,23 +110,22 @@ func (cli *Client) ClientVersion() string {
127 127
 	return cli.version
128 128
 }
129 129
 
130
-func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
131
-	if tr == nil {
132
-		tr = &http.Transport{}
130
+// ParseHost verifies that the given host strings is valid.
131
+func ParseHost(host string) (string, string, string, error) {
132
+	protoAddrParts := strings.SplitN(host, "://", 2)
133
+	if len(protoAddrParts) == 1 {
134
+		return "", "", "", fmt.Errorf("unable to parse docker host `%s`", host)
133 135
 	}
134 136
 
135
-	// Why 32? See https://github.com/docker/docker/pull/8035.
136
-	timeout := 32 * time.Second
137
-	if proto == "unix" {
138
-		// No need for compression in local communications.
139
-		tr.DisableCompression = true
140
-		tr.Dial = func(_, _ string) (net.Conn, error) {
141
-			return net.DialTimeout(proto, addr, timeout)
137
+	var basePath string
138
+	proto, addr := protoAddrParts[0], protoAddrParts[1]
139
+	if proto == "tcp" {
140
+		parsed, err := url.Parse("tcp://" + addr)
141
+		if err != nil {
142
+			return "", "", "", err
142 143
 		}
143
-	} else {
144
-		tr.Proxy = http.ProxyFromEnvironment
145
-		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
144
+		addr = parsed.Host
145
+		basePath = parsed.Path
146 146
 	}
147
-
148
-	return tr
147
+	return proto, addr, basePath, nil
149 148
 }
... ...
@@ -1,4 +1,4 @@
1
-// +build linux freebsd
1
+// +build linux freebsd solaris
2 2
 
3 3
 package client
4 4
 
... ...
@@ -3,18 +3,20 @@ package client
3 3
 import (
4 4
 	"io"
5 5
 	"net/url"
6
+
7
+	"golang.org/x/net/context"
6 8
 )
7 9
 
8 10
 // ContainerStats returns near realtime stats for a given container.
9 11
 // It's up to the caller to close the io.ReadCloser returned.
10
-func (cli *Client) ContainerStats(containerID string, stream bool) (io.ReadCloser, error) {
12
+func (cli *Client) ContainerStats(ctx context.Context, containerID string, stream bool) (io.ReadCloser, error) {
11 13
 	query := url.Values{}
12 14
 	query.Set("stream", "0")
13 15
 	if stream {
14 16
 		query.Set("stream", "1")
15 17
 	}
16 18
 
17
-	resp, err := cli.get("/containers/"+containerID+"/stats", query, nil)
19
+	resp, err := cli.getWithContext(ctx, "/containers/"+containerID+"/stats", query, nil)
18 20
 	if err != nil {
19 21
 		return nil, err
20 22
 	}
... ...
@@ -10,6 +10,8 @@ import (
10 10
 	"path/filepath"
11 11
 	"strings"
12 12
 
13
+	"golang.org/x/net/context"
14
+
13 15
 	"github.com/docker/engine-api/types"
14 16
 )
15 17
 
... ...
@@ -28,7 +30,7 @@ func (cli *Client) ContainerStatPath(containerID, path string) (types.ContainerP
28 28
 }
29 29
 
30 30
 // CopyToContainer copies content into the container filesystem.
31
-func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
31
+func (cli *Client) CopyToContainer(ctx context.Context, options types.CopyToContainerOptions) error {
32 32
 	query := url.Values{}
33 33
 	query.Set("path", filepath.ToSlash(options.Path)) // Normalize the paths used in the API.
34 34
 	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
... ...
@@ -38,7 +40,7 @@ func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
38 38
 
39 39
 	path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
40 40
 
41
-	response, err := cli.putRaw(path, query, options.Content, nil)
41
+	response, err := cli.putRawWithContext(ctx, path, query, options.Content, nil)
42 42
 	if err != nil {
43 43
 		return err
44 44
 	}
... ...
@@ -53,12 +55,12 @@ func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
53 53
 
54 54
 // CopyFromContainer get the content from the container and return it as a Reader
55 55
 // to manipulate it in the host. It's up to the caller to close the reader.
56
-func (cli *Client) CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
56
+func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
57 57
 	query := make(url.Values, 1)
58 58
 	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
59 59
 
60 60
 	apiPath := fmt.Sprintf("/containers/%s/archive", containerID)
61
-	response, err := cli.get(apiPath, query, nil)
61
+	response, err := cli.getWithContext(ctx, apiPath, query, nil)
62 62
 	if err != nil {
63 63
 		return nil, types.ContainerPathStat{}, err
64 64
 	}
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"net/url"
6 6
 	"time"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	"github.com/docker/engine-api/types"
9 11
 	"github.com/docker/engine-api/types/filters"
10 12
 	timetypes "github.com/docker/engine-api/types/time"
... ...
@@ -12,7 +14,7 @@ import (
12 12
 
13 13
 // Events returns a stream of events in the daemon in a ReadCloser.
14 14
 // It's up to the caller to close the stream.
15
-func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
15
+func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error) {
16 16
 	query := url.Values{}
17 17
 	ref := time.Now()
18 18
 
... ...
@@ -38,7 +40,7 @@ func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
38 38
 		query.Set("filters", filterJSON)
39 39
 	}
40 40
 
41
-	serverResponse, err := cli.get("/events", query, nil)
41
+	serverResponse, err := cli.getWithContext(ctx, "/events", query, nil)
42 42
 	if err != nil {
43 43
 		return nil, err
44 44
 	}
... ...
@@ -3,13 +3,15 @@ package client
3 3
 import (
4 4
 	"io"
5 5
 	"net/url"
6
+
7
+	"golang.org/x/net/context"
6 8
 )
7 9
 
8 10
 // ContainerExport retrieves the raw contents of a container
9 11
 // and returns them as a io.ReadCloser. It's up to the caller
10 12
 // to close the stream.
11
-func (cli *Client) ContainerExport(containerID string) (io.ReadCloser, error) {
12
-	serverResp, err := cli.get("/containers/"+containerID+"/export", url.Values{}, nil)
13
+func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) {
14
+	serverResp, err := cli.getWithContext(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)
13 15
 	if err != nil {
14 16
 		return nil, err
15 17
 	}
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"time"
12 12
 
13 13
 	"github.com/docker/engine-api/types"
14
+	"github.com/docker/go-connections/sockets"
14 15
 )
15 16
 
16 17
 // tlsClientCon holds tls information and a dialed connection.
... ...
@@ -44,7 +45,7 @@ func (cli *Client) postHijacked(path string, query url.Values, body interface{},
44 44
 	req.Header.Set("Connection", "Upgrade")
45 45
 	req.Header.Set("Upgrade", "tcp")
46 46
 
47
-	conn, err := dial(cli.proto, cli.addr, cli.tlsConfig)
47
+	conn, err := dial(cli.proto, cli.addr, cli.transport.TLSConfig())
48 48
 	if err != nil {
49 49
 		if strings.Contains(err.Error(), "connection refused") {
50 50
 			return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
... ...
@@ -156,9 +157,12 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
156 156
 }
157 157
 
158 158
 func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
159
-	if tlsConfig != nil && proto != "unix" {
159
+	if tlsConfig != nil && proto != "unix" && proto != "npipe" {
160 160
 		// Notice this isn't Go standard's tls.Dial function
161 161
 		return tlsDial(proto, addr, tlsConfig)
162 162
 	}
163
+	if proto == "npipe" {
164
+		return sockets.DialPipe(addr, 32*time.Second)
165
+	}
163 166
 	return net.Dial(proto, addr)
164 167
 }
... ...
@@ -9,6 +9,8 @@ import (
9 9
 	"strconv"
10 10
 	"strings"
11 11
 
12
+	"golang.org/x/net/context"
13
+
12 14
 	"github.com/docker/engine-api/types"
13 15
 	"github.com/docker/engine-api/types/container"
14 16
 )
... ...
@@ -18,7 +20,7 @@ var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
18 18
 // ImageBuild sends request to the daemon to build images.
19 19
 // The Body in the response implement an io.ReadCloser and it's up to the caller to
20 20
 // close it.
21
-func (cli *Client) ImageBuild(options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
21
+func (cli *Client) ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
22 22
 	query, err := imageBuildOptionsToQuery(options)
23 23
 	if err != nil {
24 24
 		return types.ImageBuildResponse{}, err
... ...
@@ -32,7 +34,7 @@ func (cli *Client) ImageBuild(options types.ImageBuildOptions) (types.ImageBuild
32 32
 	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
33 33
 	headers.Set("Content-Type", "application/tar")
34 34
 
35
-	serverResp, err := cli.postRaw("/build", query, options.Context, headers)
35
+	serverResp, err := cli.postRaw(ctx, "/build", query, options.Context, headers)
36 36
 	if err != nil {
37 37
 		return types.ImageBuildResponse{}, err
38 38
 	}
... ...
@@ -4,23 +4,25 @@ import (
4 4
 	"io"
5 5
 	"net/url"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	"github.com/docker/engine-api/types"
8 10
 )
9 11
 
10 12
 // ImageCreate creates a new image based in the parent options.
11 13
 // It returns the JSON content in the response body.
12
-func (cli *Client) ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error) {
14
+func (cli *Client) ImageCreate(ctx context.Context, options types.ImageCreateOptions) (io.ReadCloser, error) {
13 15
 	query := url.Values{}
14 16
 	query.Set("fromImage", options.Parent)
15 17
 	query.Set("tag", options.Tag)
16
-	resp, err := cli.tryImageCreate(query, options.RegistryAuth)
18
+	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
17 19
 	if err != nil {
18 20
 		return nil, err
19 21
 	}
20 22
 	return resp.body, nil
21 23
 }
22 24
 
23
-func (cli *Client) tryImageCreate(query url.Values, registryAuth string) (*serverResponse, error) {
25
+func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) {
24 26
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
25
-	return cli.post("/images/create", query, nil, headers)
27
+	return cli.postWithContext(ctx, "/images/create", query, nil, headers)
26 28
 }
... ...
@@ -4,12 +4,14 @@ import (
4 4
 	"io"
5 5
 	"net/url"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	"github.com/docker/engine-api/types"
8 10
 )
9 11
 
10 12
 // ImageImport creates a new image based in the source options.
11 13
 // It returns the JSON content in the response body.
12
-func (cli *Client) ImageImport(options types.ImageImportOptions) (io.ReadCloser, error) {
14
+func (cli *Client) ImageImport(ctx context.Context, options types.ImageImportOptions) (io.ReadCloser, error) {
13 15
 	query := url.Values{}
14 16
 	query.Set("fromSrc", options.SourceName)
15 17
 	query.Set("repo", options.RepositoryName)
... ...
@@ -19,7 +21,7 @@ func (cli *Client) ImageImport(options types.ImageImportOptions) (io.ReadCloser,
19 19
 		query.Add("changes", change)
20 20
 	}
21 21
 
22
-	resp, err := cli.postRaw("/images/create", query, options.Source, nil)
22
+	resp, err := cli.postRaw(ctx, "/images/create", query, options.Source, nil)
23 23
 	if err != nil {
24 24
 		return nil, err
25 25
 	}
... ...
@@ -4,14 +4,21 @@ import (
4 4
 	"io"
5 5
 	"net/url"
6 6
 
7
+	"golang.org/x/net/context"
8
+
7 9
 	"github.com/docker/engine-api/types"
8 10
 )
9 11
 
10 12
 // ImageLoad loads an image in the docker host from the client host.
11 13
 // It's up to the caller to close the io.ReadCloser returned by
12 14
 // this function.
13
-func (cli *Client) ImageLoad(input io.Reader) (types.ImageLoadResponse, error) {
14
-	resp, err := cli.postRaw("/images/load", url.Values{}, input, nil)
15
+func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
16
+	v := url.Values{}
17
+	v.Set("quiet", "0")
18
+	if quiet {
19
+		v.Set("quiet", "1")
20
+	}
21
+	resp, err := cli.postRaw(ctx, "/images/load", v, input, nil)
15 22
 	if err != nil {
16 23
 		return types.ImageLoadResponse{}, err
17 24
 	}
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"net/http"
6 6
 	"net/url"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	"github.com/docker/engine-api/types"
9 11
 )
10 12
 
... ...
@@ -12,20 +14,20 @@ import (
12 12
 // It executes the privileged function if the operation is unauthorized
13 13
 // and it tries one more time.
14 14
 // It's up to the caller to handle the io.ReadCloser and close it properly.
15
-func (cli *Client) ImagePull(options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
15
+func (cli *Client) ImagePull(ctx context.Context, options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
16 16
 	query := url.Values{}
17 17
 	query.Set("fromImage", options.ImageID)
18 18
 	if options.Tag != "" {
19 19
 		query.Set("tag", options.Tag)
20 20
 	}
21 21
 
22
-	resp, err := cli.tryImageCreate(query, options.RegistryAuth)
22
+	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
23 23
 	if resp.statusCode == http.StatusUnauthorized {
24 24
 		newAuthHeader, privilegeErr := privilegeFunc()
25 25
 		if privilegeErr != nil {
26 26
 			return nil, privilegeErr
27 27
 		}
28
-		resp, err = cli.tryImageCreate(query, newAuthHeader)
28
+		resp, err = cli.tryImageCreate(ctx, query, newAuthHeader)
29 29
 	}
30 30
 	if err != nil {
31 31
 		return nil, err
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"net/http"
6 6
 	"net/url"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	"github.com/docker/engine-api/types"
9 11
 )
10 12
 
... ...
@@ -12,17 +14,17 @@ import (
12 12
 // It executes the privileged function if the operation is unauthorized
13 13
 // and it tries one more time.
14 14
 // It's up to the caller to handle the io.ReadCloser and close it properly.
15
-func (cli *Client) ImagePush(options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
15
+func (cli *Client) ImagePush(ctx context.Context, options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
16 16
 	query := url.Values{}
17 17
 	query.Set("tag", options.Tag)
18 18
 
19
-	resp, err := cli.tryImagePush(options.ImageID, query, options.RegistryAuth)
19
+	resp, err := cli.tryImagePush(ctx, options.ImageID, query, options.RegistryAuth)
20 20
 	if resp.statusCode == http.StatusUnauthorized {
21 21
 		newAuthHeader, privilegeErr := privilegeFunc()
22 22
 		if privilegeErr != nil {
23 23
 			return nil, privilegeErr
24 24
 		}
25
-		resp, err = cli.tryImagePush(options.ImageID, query, newAuthHeader)
25
+		resp, err = cli.tryImagePush(ctx, options.ImageID, query, newAuthHeader)
26 26
 	}
27 27
 	if err != nil {
28 28
 		return nil, err
... ...
@@ -30,7 +32,7 @@ func (cli *Client) ImagePush(options types.ImagePushOptions, privilegeFunc Reque
30 30
 	return resp.body, nil
31 31
 }
32 32
 
33
-func (cli *Client) tryImagePush(imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
33
+func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
34 34
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
35
-	return cli.post("/images/"+imageID+"/push", query, nil, headers)
35
+	return cli.postWithContext(ctx, "/images/"+imageID+"/push", query, nil, headers)
36 36
 }
... ...
@@ -3,16 +3,18 @@ package client
3 3
 import (
4 4
 	"io"
5 5
 	"net/url"
6
+
7
+	"golang.org/x/net/context"
6 8
 )
7 9
 
8 10
 // ImageSave retrieves one or more images from the docker host as a io.ReadCloser.
9 11
 // It's up to the caller to store the images and close the stream.
10
-func (cli *Client) ImageSave(imageIDs []string) (io.ReadCloser, error) {
12
+func (cli *Client) ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error) {
11 13
 	query := url.Values{
12 14
 		"names": imageIDs,
13 15
 	}
14 16
 
15
-	resp, err := cli.get("/images/get", query, nil)
17
+	resp, err := cli.getWithContext(ctx, "/images/get", query, nil)
16 18
 	if err != nil {
17 19
 		return nil, err
18 20
 	}
... ...
@@ -3,6 +3,8 @@ package client
3 3
 import (
4 4
 	"io"
5 5
 
6
+	"golang.org/x/net/context"
7
+
6 8
 	"github.com/docker/engine-api/types"
7 9
 	"github.com/docker/engine-api/types/container"
8 10
 	"github.com/docker/engine-api/types/filters"
... ...
@@ -22,40 +24,40 @@ type APIClient interface {
22 22
 	ContainerExecInspect(execID string) (types.ContainerExecInspect, error)
23 23
 	ContainerExecResize(options types.ResizeOptions) error
24 24
 	ContainerExecStart(execID string, config types.ExecStartCheck) error
25
-	ContainerExport(containerID string) (io.ReadCloser, error)
25
+	ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error)
26 26
 	ContainerInspect(containerID string) (types.ContainerJSON, error)
27 27
 	ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error)
28 28
 	ContainerKill(containerID, signal string) error
29 29
 	ContainerList(options types.ContainerListOptions) ([]types.Container, error)
30
-	ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error)
30
+	ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error)
31 31
 	ContainerPause(containerID string) error
32 32
 	ContainerRemove(options types.ContainerRemoveOptions) error
33 33
 	ContainerRename(containerID, newContainerName string) error
34 34
 	ContainerResize(options types.ResizeOptions) error
35 35
 	ContainerRestart(containerID string, timeout int) error
36 36
 	ContainerStatPath(containerID, path string) (types.ContainerPathStat, error)
37
-	ContainerStats(containerID string, stream bool) (io.ReadCloser, error)
37
+	ContainerStats(ctx context.Context, containerID string, stream bool) (io.ReadCloser, error)
38 38
 	ContainerStart(containerID string) error
39 39
 	ContainerStop(containerID string, timeout int) error
40 40
 	ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
41 41
 	ContainerUnpause(containerID string) error
42 42
 	ContainerUpdate(containerID string, updateConfig container.UpdateConfig) error
43
-	ContainerWait(containerID string) (int, error)
44
-	CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
45
-	CopyToContainer(options types.CopyToContainerOptions) error
46
-	Events(options types.EventsOptions) (io.ReadCloser, error)
47
-	ImageBuild(options types.ImageBuildOptions) (types.ImageBuildResponse, error)
48
-	ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error)
43
+	ContainerWait(ctx context.Context, containerID string) (int, error)
44
+	CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
45
+	CopyToContainer(ctx context.Context, options types.CopyToContainerOptions) error
46
+	Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error)
47
+	ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
48
+	ImageCreate(ctx context.Context, options types.ImageCreateOptions) (io.ReadCloser, error)
49 49
 	ImageHistory(imageID string) ([]types.ImageHistory, error)
50
-	ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
50
+	ImageImport(ctx context.Context, options types.ImageImportOptions) (io.ReadCloser, error)
51 51
 	ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error)
52 52
 	ImageList(options types.ImageListOptions) ([]types.Image, error)
53
-	ImageLoad(input io.Reader) (types.ImageLoadResponse, error)
54
-	ImagePull(options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
55
-	ImagePush(options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
53
+	ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error)
54
+	ImagePull(ctx context.Context, options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
55
+	ImagePush(ctx context.Context, options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
56 56
 	ImageRemove(options types.ImageRemoveOptions) ([]types.ImageDelete, error)
57 57
 	ImageSearch(options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error)
58
-	ImageSave(imageIDs []string) (io.ReadCloser, error)
58
+	ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error)
59 59
 	ImageTag(options types.ImageTagOptions) error
60 60
 	Info() (types.Info, error)
61 61
 	NetworkConnect(networkID, containerID string, config *network.EndpointSettings) error
... ...
@@ -5,13 +5,15 @@ import (
5 5
 	"net/url"
6 6
 	"time"
7 7
 
8
+	"golang.org/x/net/context"
9
+
8 10
 	"github.com/docker/engine-api/types"
9 11
 	timetypes "github.com/docker/engine-api/types/time"
10 12
 )
11 13
 
12 14
 // ContainerLogs returns the logs generated by a container in an io.ReadCloser.
13 15
 // It's up to the caller to close the stream.
14
-func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error) {
16
+func (cli *Client) ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error) {
15 17
 	query := url.Values{}
16 18
 	if options.ShowStdout {
17 19
 		query.Set("stdout", "1")
... ...
@@ -38,7 +40,7 @@ func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadClo
38 38
 	}
39 39
 	query.Set("tail", options.Tail)
40 40
 
41
-	resp, err := cli.get("/containers/"+options.ContainerID+"/logs", query, nil)
41
+	resp, err := cli.getWithContext(ctx, "/containers/"+options.ContainerID+"/logs", query, nil)
42 42
 	if err != nil {
43 43
 		return nil, err
44 44
 	}
... ...
@@ -9,6 +9,10 @@ import (
9 9
 	"net/http"
10 10
 	"net/url"
11 11
 	"strings"
12
+
13
+	"github.com/docker/engine-api/client/transport/cancellable"
14
+
15
+	"golang.org/x/net/context"
12 16
 )
13 17
 
14 18
 // serverResponse is a wrapper for http API responses.
... ...
@@ -20,40 +24,55 @@ type serverResponse struct {
20 20
 
21 21
 // head sends an http request to the docker API using the method HEAD.
22 22
 func (cli *Client) head(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
23
-	return cli.sendRequest("HEAD", path, query, nil, headers)
23
+	return cli.sendRequest(context.Background(), "HEAD", path, query, nil, headers)
24 24
 }
25 25
 
26 26
 // get sends an http request to the docker API using the method GET.
27 27
 func (cli *Client) get(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
28
-	return cli.sendRequest("GET", path, query, nil, headers)
28
+	return cli.getWithContext(context.Background(), path, query, headers)
29
+}
30
+
31
+// getWithContext sends an http request to the docker API using the method GET with a specific go context.
32
+func (cli *Client) getWithContext(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
33
+	return cli.sendRequest(ctx, "GET", path, query, nil, headers)
29 34
 }
30 35
 
31 36
 // post sends an http request to the docker API using the method POST.
32 37
 func (cli *Client) post(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
33
-	return cli.sendRequest("POST", path, query, body, headers)
38
+	return cli.postWithContext(context.Background(), path, query, body, headers)
34 39
 }
35 40
 
36
-// postRaw sends the raw input to the docker API using the method POST.
37
-func (cli *Client) postRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
38
-	return cli.sendClientRequest("POST", path, query, body, headers)
41
+// postWithContext sends an http request to the docker API using the method POST with a specific go context.
42
+func (cli *Client) postWithContext(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
43
+	return cli.sendRequest(ctx, "POST", path, query, body, headers)
44
+}
45
+
46
+// postRaw sends the raw input to the docker API using the method POST with a specific go context.
47
+func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
48
+	return cli.sendClientRequest(ctx, "POST", path, query, body, headers)
39 49
 }
40 50
 
41 51
 // put sends an http request to the docker API using the method PUT.
42 52
 func (cli *Client) put(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
43
-	return cli.sendRequest("PUT", path, query, body, headers)
53
+	return cli.sendRequest(context.Background(), "PUT", path, query, body, headers)
44 54
 }
45 55
 
46 56
 // putRaw sends the raw input to the docker API using the method PUT.
47 57
 func (cli *Client) putRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
48
-	return cli.sendClientRequest("PUT", path, query, body, headers)
58
+	return cli.putRawWithContext(context.Background(), path, query, body, headers)
59
+}
60
+
61
+// putRawWithContext sends the raw input to the docker API using the method PUT with a specific go context.
62
+func (cli *Client) putRawWithContext(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
63
+	return cli.sendClientRequest(ctx, "PUT", path, query, body, headers)
49 64
 }
50 65
 
51 66
 // delete sends an http request to the docker API using the method DELETE.
52 67
 func (cli *Client) delete(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
53
-	return cli.sendRequest("DELETE", path, query, nil, headers)
68
+	return cli.sendRequest(context.Background(), "DELETE", path, query, nil, headers)
54 69
 }
55 70
 
56
-func (cli *Client) sendRequest(method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
71
+func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
57 72
 	params, err := encodeData(body)
58 73
 	if err != nil {
59 74
 		return nil, err
... ...
@@ -66,10 +85,10 @@ func (cli *Client) sendRequest(method, path string, query url.Values, body inter
66 66
 		headers["Content-Type"] = []string{"application/json"}
67 67
 	}
68 68
 
69
-	return cli.sendClientRequest(method, path, query, params, headers)
69
+	return cli.sendClientRequest(ctx, method, path, query, params, headers)
70 70
 }
71 71
 
72
-func (cli *Client) sendClientRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
72
+func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
73 73
 	serverResp := &serverResponse{
74 74
 		body:       nil,
75 75
 		statusCode: -1,
... ...
@@ -82,13 +101,13 @@ func (cli *Client) sendClientRequest(method, path string, query url.Values, body
82 82
 
83 83
 	req, err := cli.newRequest(method, path, query, body, headers)
84 84
 	req.URL.Host = cli.addr
85
-	req.URL.Scheme = cli.scheme
85
+	req.URL.Scheme = cli.transport.Scheme()
86 86
 
87 87
 	if expectedPayload && req.Header.Get("Content-Type") == "" {
88 88
 		req.Header.Set("Content-Type", "text/plain")
89 89
 	}
90 90
 
91
-	resp, err := cli.httpClient.Do(req)
91
+	resp, err := cancellable.Do(ctx, cli.transport, req)
92 92
 	if resp != nil {
93 93
 		serverResp.statusCode = resp.StatusCode
94 94
 	}
... ...
@@ -98,10 +117,10 @@ func (cli *Client) sendClientRequest(method, path string, query url.Values, body
98 98
 			return serverResp, ErrConnectionFailed
99 99
 		}
100 100
 
101
-		if cli.scheme == "http" && strings.Contains(err.Error(), "malformed HTTP response") {
101
+		if !cli.transport.Secure() && strings.Contains(err.Error(), "malformed HTTP response") {
102 102
 			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
103 103
 		}
104
-		if cli.scheme == "https" && strings.Contains(err.Error(), "remote error: bad certificate") {
104
+		if cli.transport.Secure() && strings.Contains(err.Error(), "remote error: bad certificate") {
105 105
 			return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
106 106
 		}
107 107
 
108 108
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+// Copyright 2015 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build go1.5
5
+
6
+package cancellable
7
+
8
+import (
9
+	"net/http"
10
+
11
+	"github.com/docker/engine-api/client/transport"
12
+)
13
+
14
+func canceler(client transport.Sender, req *http.Request) func() {
15
+	// TODO(djd): Respect any existing value of req.Cancel.
16
+	ch := make(chan struct{})
17
+	req.Cancel = ch
18
+
19
+	return func() {
20
+		close(ch)
21
+	}
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+// Copyright 2015 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build !go1.5
5
+
6
+package cancellable
7
+
8
+import (
9
+	"net/http"
10
+
11
+	"github.com/docker/engine-api/client/transport"
12
+)
13
+
14
+type requestCanceler interface {
15
+	CancelRequest(*http.Request)
16
+}
17
+
18
+func canceler(client transport.Sender, req *http.Request) func() {
19
+	rc, ok := client.(requestCanceler)
20
+	if !ok {
21
+		return func() {}
22
+	}
23
+	return func() {
24
+		rc.CancelRequest(req)
25
+	}
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,113 @@
0
+// Copyright 2015 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package cancellable provides helper function to cancel http requests.
5
+package cancellable
6
+
7
+import (
8
+	"io"
9
+	"net/http"
10
+
11
+	"github.com/docker/engine-api/client/transport"
12
+
13
+	"golang.org/x/net/context"
14
+)
15
+
16
+func nop() {}
17
+
18
+var (
19
+	testHookContextDoneBeforeHeaders = nop
20
+	testHookDoReturned               = nop
21
+	testHookDidBodyClose             = nop
22
+)
23
+
24
+// Do sends an HTTP request with the provided transport.Sender and returns an HTTP response.
25
+// If the client is nil, http.DefaultClient is used.
26
+// If the context is canceled or times out, ctx.Err() will be returned.
27
+//
28
+// FORK INFORMATION:
29
+//
30
+// This function deviates from the upstream version in golang.org/x/net/context/ctxhttp by
31
+// taking a Sender interface rather than a *http.Client directly. That allow us to use
32
+// this funcion with mocked clients and hijacked connections.
33
+func Do(ctx context.Context, client transport.Sender, req *http.Request) (*http.Response, error) {
34
+	if client == nil {
35
+		client = http.DefaultClient
36
+	}
37
+
38
+	// Request cancelation changed in Go 1.5, see canceler.go and canceler_go14.go.
39
+	cancel := canceler(client, req)
40
+
41
+	type responseAndError struct {
42
+		resp *http.Response
43
+		err  error
44
+	}
45
+	result := make(chan responseAndError, 1)
46
+
47
+	go func() {
48
+		resp, err := client.Do(req)
49
+		testHookDoReturned()
50
+		result <- responseAndError{resp, err}
51
+	}()
52
+
53
+	var resp *http.Response
54
+
55
+	select {
56
+	case <-ctx.Done():
57
+		testHookContextDoneBeforeHeaders()
58
+		cancel()
59
+		// Clean up after the goroutine calling client.Do:
60
+		go func() {
61
+			if r := <-result; r.resp != nil && r.resp.Body != nil {
62
+				testHookDidBodyClose()
63
+				r.resp.Body.Close()
64
+			}
65
+		}()
66
+		return nil, ctx.Err()
67
+	case r := <-result:
68
+		var err error
69
+		resp, err = r.resp, r.err
70
+		if err != nil {
71
+			return resp, err
72
+		}
73
+	}
74
+
75
+	c := make(chan struct{})
76
+	go func() {
77
+		select {
78
+		case <-ctx.Done():
79
+			cancel()
80
+		case <-c:
81
+			// The response's Body is closed.
82
+		}
83
+	}()
84
+	resp.Body = &notifyingReader{resp.Body, c}
85
+
86
+	return resp, nil
87
+}
88
+
89
+// notifyingReader is an io.ReadCloser that closes the notify channel after
90
+// Close is called or a Read fails on the underlying ReadCloser.
91
+type notifyingReader struct {
92
+	io.ReadCloser
93
+	notify chan<- struct{}
94
+}
95
+
96
+func (r *notifyingReader) Read(p []byte) (int, error) {
97
+	n, err := r.ReadCloser.Read(p)
98
+	if err != nil && r.notify != nil {
99
+		close(r.notify)
100
+		r.notify = nil
101
+	}
102
+	return n, err
103
+}
104
+
105
+func (r *notifyingReader) Close() error {
106
+	err := r.ReadCloser.Close()
107
+	if r.notify != nil {
108
+		close(r.notify)
109
+		r.notify = nil
110
+	}
111
+	return err
112
+}
0 113
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+package transport
1
+
2
+import (
3
+	"crypto/tls"
4
+	"net/http"
5
+)
6
+
7
+// Sender is an interface that clients must implement
8
+// to be able to send requests to a remote connection.
9
+type Sender interface {
10
+	// Do sends request to a remote endpoint.
11
+	Do(*http.Request) (*http.Response, error)
12
+}
13
+
14
+// Client is an interface that abstracts all remote connections.
15
+type Client interface {
16
+	Sender
17
+	// Secure tells whether the connection is secure or not.
18
+	Secure() bool
19
+	// Scheme returns the connection protocol the client uses.
20
+	Scheme() string
21
+	// TLSConfig returns any TLS configuration the client uses.
22
+	TLSConfig() *tls.Config
23
+}
24
+
25
+// tlsInfo returns information about the TLS configuration.
26
+type tlsInfo struct {
27
+	tlsConfig *tls.Config
28
+}
29
+
30
+// TLSConfig returns the TLS configuration.
31
+func (t *tlsInfo) TLSConfig() *tls.Config {
32
+	return t.tlsConfig
33
+}
34
+
35
+// Scheme returns protocol scheme to use.
36
+func (t *tlsInfo) Scheme() string {
37
+	if t.tlsConfig != nil {
38
+		return "https"
39
+	}
40
+	return "http"
41
+}
42
+
43
+// Secure returns true if there is a TLS configuration.
44
+func (t *tlsInfo) Secure() bool {
45
+	return t.tlsConfig != nil
46
+}
0 47
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+// +build test
1
+
2
+package transport
3
+
4
+import (
5
+	"crypto/tls"
6
+	"net/http"
7
+)
8
+
9
+type mockClient struct {
10
+	*tlsInfo
11
+	do func(*http.Request) (*http.Response, error)
12
+}
13
+
14
+// NewMockClient returns a mocked client that runs the function supplied as `client.Do` call
15
+func NewMockClient(tlsConfig *tls.Config, doer func(*http.Request) (*http.Response, error)) Client {
16
+	return mockClient{
17
+		tlsInfo: &tlsInfo{tlsConfig},
18
+		do:      doer,
19
+	}
20
+}
21
+
22
+// Do executes the supplied function for the mock.
23
+func (m mockClient) Do(req *http.Request) (*http.Response, error) {
24
+	return m.do(req)
25
+}
0 26
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+// Package transport provides function to send request to remote endpoints.
1
+package transport
2
+
3
+import (
4
+	"fmt"
5
+	"net/http"
6
+
7
+	"github.com/docker/go-connections/sockets"
8
+)
9
+
10
+// apiTransport holds information about the http transport to connect with the API.
11
+type apiTransport struct {
12
+	*http.Client
13
+	*tlsInfo
14
+	transport *http.Transport
15
+}
16
+
17
+// NewTransportWithHTTP creates a new transport based on the provided proto, address and http client.
18
+// It uses Docker's default http transport configuration if the client is nil.
19
+// It does not modify the client's transport if it's not nil.
20
+func NewTransportWithHTTP(proto, addr string, client *http.Client) (Client, error) {
21
+	var transport *http.Transport
22
+
23
+	if client != nil {
24
+		tr, ok := client.Transport.(*http.Transport)
25
+		if !ok {
26
+			return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", client.Transport)
27
+		}
28
+		transport = tr
29
+	} else {
30
+		transport = defaultTransport(proto, addr)
31
+		client = &http.Client{
32
+			Transport: transport,
33
+		}
34
+	}
35
+
36
+	return &apiTransport{
37
+		Client:    client,
38
+		tlsInfo:   &tlsInfo{transport.TLSClientConfig},
39
+		transport: transport,
40
+	}, nil
41
+}
42
+
43
+// CancelRequest stops a request execution.
44
+func (a *apiTransport) CancelRequest(req *http.Request) {
45
+	a.transport.CancelRequest(req)
46
+}
47
+
48
+// defaultTransport creates a new http.Transport with Docker's
49
+// default transport configuration.
50
+func defaultTransport(proto, addr string) *http.Transport {
51
+	tr := new(http.Transport)
52
+	sockets.ConfigureTransport(tr, proto, addr)
53
+	return tr
54
+}
55
+
56
+var _ Client = &apiTransport{}
... ...
@@ -3,13 +3,15 @@ package client
3 3
 import (
4 4
 	"encoding/json"
5 5
 
6
+	"golang.org/x/net/context"
7
+
6 8
 	"github.com/docker/engine-api/types"
7 9
 )
8 10
 
9 11
 // ContainerWait pauses execution util a container is exits.
10 12
 // It returns the API status code as response of its readiness.
11
-func (cli *Client) ContainerWait(containerID string) (int, error) {
12
-	resp, err := cli.post("/containers/"+containerID+"/wait", nil, nil, nil)
13
+func (cli *Client) ContainerWait(ctx context.Context, containerID string) (int, error) {
14
+	resp, err := cli.postWithContext(ctx, "/containers/"+containerID+"/wait", nil, nil, nil)
13 15
 	if err != nil {
14 16
 		return -1, err
15 17
 	}
... ...
@@ -72,12 +72,6 @@ func (n NetworkMode) IsUserDefined() bool {
72 72
 	return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
73 73
 }
74 74
 
75
-// IsPreDefinedNetwork indicates if a network is predefined by the daemon
76
-func IsPreDefinedNetwork(network string) bool {
77
-	n := NetworkMode(network)
78
-	return n.IsBridge() || n.IsHost() || n.IsNone()
79
-}
80
-
81 75
 //UserDefined indicates user-created network
82 76
 func (n NetworkMode) UserDefined() string {
83 77
 	if n.IsUserDefined() {
... ...
@@ -49,11 +49,6 @@ func (n NetworkMode) NetworkName() string {
49 49
 	return ""
50 50
 }
51 51
 
52
-// IsPreDefinedNetwork indicates if a network is predefined by the daemon
53
-func IsPreDefinedNetwork(network string) bool {
54
-	return false
55
-}
56
-
57 52
 // ValidateNetMode ensures that the various combinations of requested
58 53
 // network settings are valid.
59 54
 func ValidateNetMode(c *Config, hc *HostConfig) error {
... ...
@@ -387,6 +387,7 @@ type NetworkResource struct {
387 387
 	ID         string `json:"Id"`
388 388
 	Scope      string
389 389
 	Driver     string
390
+	EnableIPv6 bool
390 391
 	IPAM       network.IPAM
391 392
 	Internal   bool
392 393
 	Containers map[string]EndpointResource
... ...
@@ -407,6 +408,7 @@ type NetworkCreate struct {
407 407
 	Name           string
408 408
 	CheckDuplicate bool
409 409
 	Driver         string
410
+	EnableIPv6     bool
410 411
 	IPAM           network.IPAM
411 412
 	Internal       bool
412 413
 	Options        map[string]string
413 414
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+package sockets
1
+
2
+import (
3
+	"errors"
4
+	"net"
5
+	"sync"
6
+)
7
+
8
+var errClosed = errors.New("use of closed network connection")
9
+
10
+// InmemSocket implements net.Listener using in-memory only connections.
11
+type InmemSocket struct {
12
+	chConn  chan net.Conn
13
+	chClose chan struct{}
14
+	addr    string
15
+	mu      sync.Mutex
16
+}
17
+
18
+// dummyAddr is used to satisfy net.Addr for the in-mem socket
19
+// it is just stored as a string and returns the string for all calls
20
+type dummyAddr string
21
+
22
+// NewInmemSocket creates an in-memory only net.Listener
23
+// The addr argument can be any string, but is used to satisfy the `Addr()` part
24
+// of the net.Listener interface
25
+func NewInmemSocket(addr string, bufSize int) *InmemSocket {
26
+	return &InmemSocket{
27
+		chConn:  make(chan net.Conn, bufSize),
28
+		chClose: make(chan struct{}),
29
+		addr:    addr,
30
+	}
31
+}
32
+
33
+// Addr returns the socket's addr string to satisfy net.Listener
34
+func (s *InmemSocket) Addr() net.Addr {
35
+	return dummyAddr(s.addr)
36
+}
37
+
38
+// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.
39
+func (s *InmemSocket) Accept() (net.Conn, error) {
40
+	select {
41
+	case conn := <-s.chConn:
42
+		return conn, nil
43
+	case <-s.chClose:
44
+		return nil, errClosed
45
+	}
46
+}
47
+
48
+// Close closes the listener. It will be unavailable for use once closed.
49
+func (s *InmemSocket) Close() error {
50
+	s.mu.Lock()
51
+	defer s.mu.Unlock()
52
+	select {
53
+	case <-s.chClose:
54
+	default:
55
+		close(s.chClose)
56
+	}
57
+	return nil
58
+}
59
+
60
+// Dial is used to establish a connection with the in-mem server
61
+func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) {
62
+	srvConn, clientConn := net.Pipe()
63
+	select {
64
+	case s.chConn <- srvConn:
65
+	case <-s.chClose:
66
+		return nil, errClosed
67
+	}
68
+
69
+	return clientConn, nil
70
+}
71
+
72
+// Network returns the addr string, satisfies net.Addr
73
+func (a dummyAddr) Network() string {
74
+	return string(a)
75
+}
76
+
77
+// String returns the string form
78
+func (a dummyAddr) String() string {
79
+	return string(a)
80
+}
81
+
82
+// timeoutError is used when there is a timeout with a connection
83
+// this implements the net.Error interface
84
+type timeoutError struct{}
85
+
86
+func (e *timeoutError) Error() string   { return "i/o timeout" }
87
+func (e *timeoutError) Timeout() bool   { return true }
88
+func (e *timeoutError) Temporary() bool { return true }
0 89
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+// Package sockets provides helper functions to create and configure Unix or TCP sockets.
1
+package sockets
2
+
3
+import (
4
+	"net"
5
+	"net/http"
6
+	"time"
7
+)
8
+
9
+// Why 32? See https://github.com/docker/docker/pull/8035.
10
+const defaulTimeout = 32 * time.Second
11
+
12
+// ConfigureTransport configures the specified Transport according to the
13
+// specified proto and addr.
14
+// If the proto is unix (using a unix socket to communicate) the compression
15
+// is disabled.
16
+func ConfigureTransport(tr *http.Transport, proto, addr string) {
17
+	switch proto {
18
+	case "unix":
19
+		// No need for compression in local communications.
20
+		tr.DisableCompression = true
21
+		tr.Dial = func(_, _ string) (net.Conn, error) {
22
+			return net.DialTimeout(proto, addr, defaulTimeout)
23
+		}
24
+	case "npipe":
25
+		// No need for compression in local communications.
26
+		tr.DisableCompression = true
27
+		tr.Dial = func(_, _ string) (net.Conn, error) {
28
+			return DialPipe(addr, defaulTimeout)
29
+		}
30
+	default:
31
+		tr.Proxy = http.ProxyFromEnvironment
32
+		tr.Dial = (&net.Dialer{Timeout: defaulTimeout}).Dial
33
+	}
34
+}
0 35
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+// +build !windows
1
+
2
+package sockets
3
+
4
+import (
5
+	"net"
6
+	"syscall"
7
+	"time"
8
+)
9
+
10
+// DialPipe connects to a Windows named pipe.
11
+// This is not supported on other OSes.
12
+func DialPipe(_ string, _ time.Duration) (net.Conn, error) {
13
+	return nil, syscall.EAFNOSUPPORT
14
+}
0 15
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package sockets
1
+
2
+import (
3
+	"net"
4
+	"time"
5
+
6
+	"github.com/Microsoft/go-winio"
7
+)
8
+
9
+// DialPipe connects to a Windows named pipe.
10
+func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
11
+	return winio.DialPipe(addr, &timeout)
12
+}
... ...
@@ -4,8 +4,6 @@ package sockets
4 4
 import (
5 5
 	"crypto/tls"
6 6
 	"net"
7
-	"net/http"
8
-	"time"
9 7
 )
10 8
 
11 9
 // NewTCPSocket creates a TCP socket listener with the specified address and
... ...
@@ -22,22 +20,3 @@ func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) {
22 22
 	}
23 23
 	return l, nil
24 24
 }
25
-
26
-// ConfigureTCPTransport configures the specified Transport according to the
27
-// specified proto and addr.
28
-// If the proto is unix (using a unix socket to communicate) the compression
29
-// is disabled.
30
-func ConfigureTCPTransport(tr *http.Transport, proto, addr string) {
31
-	// Why 32? See https://github.com/docker/docker/pull/8035.
32
-	timeout := 32 * time.Second
33
-	if proto == "unix" {
34
-		// No need for compression in local communications.
35
-		tr.DisableCompression = true
36
-		tr.Dial = func(_, _ string) (net.Conn, error) {
37
-			return net.DialTimeout(proto, addr, timeout)
38
-		}
39
-	} else {
40
-		tr.Proxy = http.ProxyFromEnvironment
41
-		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
42
-	}
43
-}
... ...
@@ -41,12 +41,6 @@ var acceptedCBCCiphers = []uint16{
41 41
 	tls.TLS_RSA_WITH_AES_128_CBC_SHA,
42 42
 }
43 43
 
44
-// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
45
-var clientCipherSuites = []uint16{
46
-	tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
47
-	tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
48
-}
49
-
50 44
 // DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
51 45
 // options struct but wants to use a commonly accepted set of TLS cipher suites, with
52 46
 // known weak algorithms removed.
... ...
@@ -91,7 +85,7 @@ func certPool(caFile string) (*x509.CertPool, error) {
91 91
 func Client(options Options) (*tls.Config, error) {
92 92
 	tlsConfig := ClientDefault
93 93
 	tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
94
-	if !options.InsecureSkipVerify {
94
+	if !options.InsecureSkipVerify && options.CAFile != "" {
95 95
 		CAs, err := certPool(options.CAFile)
96 96
 		if err != nil {
97 97
 			return nil, err
... ...
@@ -99,7 +93,7 @@ func Client(options Options) (*tls.Config, error) {
99 99
 		tlsConfig.RootCAs = CAs
100 100
 	}
101 101
 
102
-	if options.CertFile != "" && options.KeyFile != "" {
102
+	if options.CertFile != "" || options.KeyFile != "" {
103 103
 		tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
104 104
 		if err != nil {
105 105
 			return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)
106 106
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+// +build go1.5
1
+
2
+// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
3
+//
4
+package tlsconfig
5
+
6
+import (
7
+	"crypto/tls"
8
+)
9
+
10
+// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
11
+var clientCipherSuites = []uint16{
12
+	tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
13
+	tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
14
+	tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
15
+	tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
16
+}
0 17
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+// +build !go1.5
1
+
2
+// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
3
+//
4
+package tlsconfig
5
+
6
+import (
7
+	"crypto/tls"
8
+)
9
+
10
+// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
11
+var clientCipherSuites = []uint16{
12
+	tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
13
+	tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
14
+}