1// Copyright 2013 go-dockerclient authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package docker provides a client for the Docker remote API.
6//
7// See https://goo.gl/o2v3rk for more details on the remote API.
8package docker
9
10import (
11	"bufio"
12	"bytes"
13	"context"
14	"crypto/tls"
15	"crypto/x509"
16	"encoding/json"
17	"errors"
18	"fmt"
19	"io"
20	"io/ioutil"
21	"net"
22	"net/http"
23	"net/http/httputil"
24	"net/url"
25	"os"
26	"path/filepath"
27	"reflect"
28	"runtime"
29	"strconv"
30	"strings"
31	"sync/atomic"
32	"time"
33
34	"github.com/ory/dockertest/docker/opts"
35	"github.com/ory/dockertest/docker/pkg/homedir"
36	"github.com/ory/dockertest/docker/pkg/jsonmessage"
37	"github.com/ory/dockertest/docker/pkg/stdcopy"
38)
39
40const (
41	userAgent = "go-dockerclient"
42
43	unixProtocol      = "unix"
44	namedPipeProtocol = "npipe"
45)
46
47var (
48	// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
49	ErrInvalidEndpoint = errors.New("invalid endpoint")
50
51	// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
52	ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
53
54	// ErrInactivityTimeout is returned when a streamable call has been inactive for some time.
55	ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
56
57	apiVersion112, _ = NewAPIVersion("1.12")
58	apiVersion119, _ = NewAPIVersion("1.19")
59	apiVersion124, _ = NewAPIVersion("1.24")
60	apiVersion125, _ = NewAPIVersion("1.25")
61)
62
63// APIVersion is an internal representation of a version of the Remote API.
64type APIVersion []int
65
66// NewAPIVersion returns an instance of APIVersion for the given string.
67//
68// The given string must be in the form <major>.<minor>.<patch>, where <major>,
69// <minor> and <patch> are integer numbers.
70func NewAPIVersion(input string) (APIVersion, error) {
71	if !strings.Contains(input, ".") {
72		return nil, fmt.Errorf("Unable to parse version %q", input)
73	}
74	raw := strings.Split(input, "-")
75	arr := strings.Split(raw[0], ".")
76	ret := make(APIVersion, len(arr))
77	var err error
78	for i, val := range arr {
79		ret[i], err = strconv.Atoi(val)
80		if err != nil {
81			return nil, fmt.Errorf("Unable to parse version %q: %q is not an integer", input, val)
82		}
83	}
84	return ret, nil
85}
86
87func (version APIVersion) String() string {
88	var str string
89	for i, val := range version {
90		str += strconv.Itoa(val)
91		if i < len(version)-1 {
92			str += "."
93		}
94	}
95	return str
96}
97
98// LessThan is a function for comparing APIVersion structs
99func (version APIVersion) LessThan(other APIVersion) bool {
100	return version.compare(other) < 0
101}
102
103// LessThanOrEqualTo is a function for comparing APIVersion structs
104func (version APIVersion) LessThanOrEqualTo(other APIVersion) bool {
105	return version.compare(other) <= 0
106}
107
108// GreaterThan is a function for comparing APIVersion structs
109func (version APIVersion) GreaterThan(other APIVersion) bool {
110	return version.compare(other) > 0
111}
112
113// GreaterThanOrEqualTo is a function for comparing APIVersion structs
114func (version APIVersion) GreaterThanOrEqualTo(other APIVersion) bool {
115	return version.compare(other) >= 0
116}
117
118func (version APIVersion) compare(other APIVersion) int {
119	for i, v := range version {
120		if i <= len(other)-1 {
121			otherVersion := other[i]
122
123			if v < otherVersion {
124				return -1
125			} else if v > otherVersion {
126				return 1
127			}
128		}
129	}
130	if len(version) > len(other) {
131		return 1
132	}
133	if len(version) < len(other) {
134		return -1
135	}
136	return 0
137}
138
139// Client is the basic type of this package. It provides methods for
140// interaction with the API.
141type Client struct {
142	SkipServerVersionCheck bool
143	HTTPClient             *http.Client
144	TLSConfig              *tls.Config
145	Dialer                 Dialer
146
147	endpoint            string
148	endpointURL         *url.URL
149	eventMonitor        *eventMonitoringState
150	requestedAPIVersion APIVersion
151	serverAPIVersion    APIVersion
152	expectedAPIVersion  APIVersion
153}
154
155// Dialer is an interface that allows network connections to be dialed
156// (net.Dialer fulfills this interface) and named pipes (a shim using
157// winio.DialPipe)
158type Dialer interface {
159	Dial(network, address string) (net.Conn, error)
160}
161
162// NewClient returns a Client instance ready for communication with the given
163// server endpoint. It will use the latest remote API version available in the
164// server.
165func NewClient(endpoint string) (*Client, error) {
166	client, err := NewVersionedClient(endpoint, "")
167	if err != nil {
168		return nil, err
169	}
170	client.SkipServerVersionCheck = true
171	return client, nil
172}
173
174// NewTLSClient returns a Client instance ready for TLS communications with the givens
175// server endpoint, key and certificates . It will use the latest remote API version
176// available in the server.
177func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
178	client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "")
179	if err != nil {
180		return nil, err
181	}
182	client.SkipServerVersionCheck = true
183	return client, nil
184}
185
186// NewTLSClientFromBytes returns a Client instance ready for TLS communications with the givens
187// server endpoint, key and certificates (passed inline to the function as opposed to being
188// read from a local file). It will use the latest remote API version available in the server.
189func NewTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte) (*Client, error) {
190	client, err := NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, "")
191	if err != nil {
192		return nil, err
193	}
194	client.SkipServerVersionCheck = true
195	return client, nil
196}
197
198// NewVersionedClient returns a Client instance ready for communication with
199// the given server endpoint, using a specific remote API version.
200func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
201	u, err := parseEndpoint(endpoint, false)
202	if err != nil {
203		return nil, err
204	}
205	var requestedAPIVersion APIVersion
206	if strings.Contains(apiVersionString, ".") {
207		requestedAPIVersion, err = NewAPIVersion(apiVersionString)
208		if err != nil {
209			return nil, err
210		}
211	}
212	c := &Client{
213		HTTPClient:          defaultClient(),
214		Dialer:              &net.Dialer{},
215		endpoint:            endpoint,
216		endpointURL:         u,
217		eventMonitor:        new(eventMonitoringState),
218		requestedAPIVersion: requestedAPIVersion,
219	}
220	c.initializeNativeClient(defaultTransport)
221	return c, nil
222}
223
224// WithTransport replaces underlying HTTP client of Docker Client by accepting
225// a function that returns pointer to a transport object.
226func (c *Client) WithTransport(trFunc func() *http.Transport) {
227	c.initializeNativeClient(trFunc)
228}
229
230// NewVersionnedTLSClient is like NewVersionedClient, but with ann extra n.
231//
232// Deprecated: Use NewVersionedTLSClient instead.
233func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
234	return NewVersionedTLSClient(endpoint, cert, key, ca, apiVersionString)
235}
236
237// NewVersionedTLSClient returns a Client instance ready for TLS communications with the givens
238// server endpoint, key and certificates, using a specific remote API version.
239func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
240	var certPEMBlock []byte
241	var keyPEMBlock []byte
242	var caPEMCert []byte
243	if _, err := os.Stat(cert); !os.IsNotExist(err) {
244		certPEMBlock, err = ioutil.ReadFile(cert)
245		if err != nil {
246			return nil, err
247		}
248	}
249	if _, err := os.Stat(key); !os.IsNotExist(err) {
250		keyPEMBlock, err = ioutil.ReadFile(key)
251		if err != nil {
252			return nil, err
253		}
254	}
255	if _, err := os.Stat(ca); !os.IsNotExist(err) {
256		caPEMCert, err = ioutil.ReadFile(ca)
257		if err != nil {
258			return nil, err
259		}
260	}
261	return NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, apiVersionString)
262}
263
264// NewClientFromEnv returns a Client instance ready for communication created from
265// Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH.
266//
267// See https://github.com/docker/docker/blob/1f963af697e8df3a78217f6fdbf67b8123a7db94/docker/docker.go#L68.
268// See https://github.com/docker/compose/blob/81707ef1ad94403789166d2fe042c8a718a4c748/compose/cli/docker_client.py#L7.
269func NewClientFromEnv() (*Client, error) {
270	client, err := NewVersionedClientFromEnv("")
271	if err != nil {
272		return nil, err
273	}
274	client.SkipServerVersionCheck = true
275	return client, nil
276}
277
278// NewVersionedClientFromEnv returns a Client instance ready for TLS communications created from
279// Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH,
280// and using a specific remote API version.
281//
282// See https://github.com/docker/docker/blob/1f963af697e8df3a78217f6fdbf67b8123a7db94/docker/docker.go#L68.
283// See https://github.com/docker/compose/blob/81707ef1ad94403789166d2fe042c8a718a4c748/compose/cli/docker_client.py#L7.
284func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
285	dockerEnv, err := getDockerEnv()
286	if err != nil {
287		return nil, err
288	}
289	dockerHost := dockerEnv.dockerHost
290	if dockerEnv.dockerTLSVerify {
291		parts := strings.SplitN(dockerEnv.dockerHost, "://", 2)
292		if len(parts) != 2 {
293			return nil, fmt.Errorf("could not split %s into two parts by ://", dockerHost)
294		}
295		cert := filepath.Join(dockerEnv.dockerCertPath, "cert.pem")
296		key := filepath.Join(dockerEnv.dockerCertPath, "key.pem")
297		ca := filepath.Join(dockerEnv.dockerCertPath, "ca.pem")
298		return NewVersionedTLSClient(dockerEnv.dockerHost, cert, key, ca, apiVersionString)
299	}
300	return NewVersionedClient(dockerEnv.dockerHost, apiVersionString)
301}
302
303// NewVersionedTLSClientFromBytes returns a Client instance ready for TLS communications with the givens
304// server endpoint, key and certificates (passed inline to the function as opposed to being
305// read from a local file), using a specific remote API version.
306func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte, apiVersionString string) (*Client, error) {
307	u, err := parseEndpoint(endpoint, true)
308	if err != nil {
309		return nil, err
310	}
311	var requestedAPIVersion APIVersion
312	if strings.Contains(apiVersionString, ".") {
313		requestedAPIVersion, err = NewAPIVersion(apiVersionString)
314		if err != nil {
315			return nil, err
316		}
317	}
318	tlsConfig := &tls.Config{}
319	if certPEMBlock != nil && keyPEMBlock != nil {
320		tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
321		if err != nil {
322			return nil, err
323		}
324		tlsConfig.Certificates = []tls.Certificate{tlsCert}
325	}
326	if caPEMCert == nil {
327		tlsConfig.InsecureSkipVerify = true
328	} else {
329		caPool := x509.NewCertPool()
330		if !caPool.AppendCertsFromPEM(caPEMCert) {
331			return nil, errors.New("Could not add RootCA pem")
332		}
333		tlsConfig.RootCAs = caPool
334	}
335	tr := defaultTransport()
336	tr.TLSClientConfig = tlsConfig
337	if err != nil {
338		return nil, err
339	}
340	c := &Client{
341		HTTPClient:          &http.Client{Transport: tr},
342		TLSConfig:           tlsConfig,
343		Dialer:              &net.Dialer{},
344		endpoint:            endpoint,
345		endpointURL:         u,
346		eventMonitor:        new(eventMonitoringState),
347		requestedAPIVersion: requestedAPIVersion,
348	}
349	c.initializeNativeClient(defaultTransport)
350	return c, nil
351}
352
353// SetTimeout takes a timeout and applies it to the HTTPClient. It should not
354// be called concurrently with any other Client methods.
355func (c *Client) SetTimeout(t time.Duration) {
356	if c.HTTPClient != nil {
357		c.HTTPClient.Timeout = t
358	}
359}
360
361func (c *Client) checkAPIVersion() error {
362	serverAPIVersionString, err := c.getServerAPIVersionString()
363	if err != nil {
364		return err
365	}
366	c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
367	if err != nil {
368		return err
369	}
370	if c.requestedAPIVersion == nil {
371		c.expectedAPIVersion = c.serverAPIVersion
372	} else {
373		c.expectedAPIVersion = c.requestedAPIVersion
374	}
375	return nil
376}
377
378// Endpoint returns the current endpoint. It's useful for getting the endpoint
379// when using functions that get this data from the environment (like
380// NewClientFromEnv.
381func (c *Client) Endpoint() string {
382	return c.endpoint
383}
384
385// Ping pings the docker server
386//
387// See https://goo.gl/wYfgY1 for more details.
388func (c *Client) Ping() error {
389	return c.PingWithContext(nil)
390}
391
392// PingWithContext pings the docker server
393// The context object can be used to cancel the ping request.
394//
395// See https://goo.gl/wYfgY1 for more details.
396func (c *Client) PingWithContext(ctx context.Context) error {
397	path := "/_ping"
398	resp, err := c.do("GET", path, doOptions{context: ctx})
399	if err != nil {
400		return err
401	}
402	if resp.StatusCode != http.StatusOK {
403		return newError(resp)
404	}
405	resp.Body.Close()
406	return nil
407}
408
409func (c *Client) getServerAPIVersionString() (version string, err error) {
410	resp, err := c.do("GET", "/version", doOptions{})
411	if err != nil {
412		return "", err
413	}
414	defer resp.Body.Close()
415	if resp.StatusCode != http.StatusOK {
416		return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", resp.StatusCode)
417	}
418	var versionResponse map[string]interface{}
419	if err := json.NewDecoder(resp.Body).Decode(&versionResponse); err != nil {
420		return "", err
421	}
422	if version, ok := (versionResponse["ApiVersion"]).(string); ok {
423		return version, nil
424	}
425	return "", nil
426}
427
428type doOptions struct {
429	data      interface{}
430	forceJSON bool
431	headers   map[string]string
432	context   context.Context
433}
434
435func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, error) {
436	var params io.Reader
437	if doOptions.data != nil || doOptions.forceJSON {
438		buf, err := json.Marshal(doOptions.data)
439		if err != nil {
440			return nil, err
441		}
442		params = bytes.NewBuffer(buf)
443	}
444	if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
445		err := c.checkAPIVersion()
446		if err != nil {
447			return nil, err
448		}
449	}
450	protocol := c.endpointURL.Scheme
451	var u string
452	switch protocol {
453	case unixProtocol, namedPipeProtocol:
454		u = c.getFakeNativeURL(path)
455	default:
456		u = c.getURL(path)
457	}
458
459	req, err := http.NewRequest(method, u, params)
460	if err != nil {
461		return nil, err
462	}
463	req.Header.Set("User-Agent", userAgent)
464	if doOptions.data != nil {
465		req.Header.Set("Content-Type", "application/json")
466	} else if method == "POST" {
467		req.Header.Set("Content-Type", "plain/text")
468	}
469
470	for k, v := range doOptions.headers {
471		req.Header.Set(k, v)
472	}
473
474	ctx := doOptions.context
475	if ctx == nil {
476		ctx = context.Background()
477	}
478
479	resp, err := c.HTTPClient.Do(req.WithContext(ctx))
480	if err != nil {
481		if strings.Contains(err.Error(), "connection refused") {
482			return nil, ErrConnectionRefused
483		}
484
485		return nil, chooseError(ctx, err)
486	}
487	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
488		return nil, newError(resp)
489	}
490	return resp, nil
491}
492
493type streamOptions struct {
494	setRawTerminal bool
495	rawJSONStream  bool
496	useJSONDecoder bool
497	headers        map[string]string
498	in             io.Reader
499	stdout         io.Writer
500	stderr         io.Writer
501	reqSent        chan struct{}
502	// timeout is the initial connection timeout
503	timeout time.Duration
504	// Timeout with no data is received, it's reset every time new data
505	// arrives
506	inactivityTimeout time.Duration
507	context           context.Context
508}
509
510// if error in context, return that instead of generic http error
511func chooseError(ctx context.Context, err error) error {
512	select {
513	case <-ctx.Done():
514		return ctx.Err()
515	default:
516		return err
517	}
518}
519
520func (c *Client) stream(method, path string, streamOptions streamOptions) error {
521	if (method == "POST" || method == "PUT") && streamOptions.in == nil {
522		streamOptions.in = bytes.NewReader(nil)
523	}
524	if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
525		err := c.checkAPIVersion()
526		if err != nil {
527			return err
528		}
529	}
530	req, err := http.NewRequest(method, c.getURL(path), streamOptions.in)
531	if err != nil {
532		return err
533	}
534	req.Header.Set("User-Agent", userAgent)
535	if method == "POST" {
536		req.Header.Set("Content-Type", "plain/text")
537	}
538	for key, val := range streamOptions.headers {
539		req.Header.Set(key, val)
540	}
541	var resp *http.Response
542	protocol := c.endpointURL.Scheme
543	address := c.endpointURL.Path
544	if streamOptions.stdout == nil {
545		streamOptions.stdout = ioutil.Discard
546	}
547	if streamOptions.stderr == nil {
548		streamOptions.stderr = ioutil.Discard
549	}
550
551	// make a sub-context so that our active cancellation does not affect parent
552	ctx := streamOptions.context
553	if ctx == nil {
554		ctx = context.Background()
555	}
556	subCtx, cancelRequest := context.WithCancel(ctx)
557	defer cancelRequest()
558
559	if protocol == unixProtocol || protocol == namedPipeProtocol {
560		var dial net.Conn
561		dial, err = c.Dialer.Dial(protocol, address)
562		if err != nil {
563			return err
564		}
565		go func() {
566			<-subCtx.Done()
567			dial.Close()
568		}()
569		breader := bufio.NewReader(dial)
570		err = req.Write(dial)
571		if err != nil {
572			return chooseError(subCtx, err)
573		}
574
575		// ReadResponse may hang if server does not replay
576		if streamOptions.timeout > 0 {
577			dial.SetDeadline(time.Now().Add(streamOptions.timeout))
578		}
579
580		if streamOptions.reqSent != nil {
581			close(streamOptions.reqSent)
582		}
583		if resp, err = http.ReadResponse(breader, req); err != nil {
584			// Cancel timeout for future I/O operations
585			if streamOptions.timeout > 0 {
586				dial.SetDeadline(time.Time{})
587			}
588			if strings.Contains(err.Error(), "connection refused") {
589				return ErrConnectionRefused
590			}
591
592			return chooseError(subCtx, err)
593		}
594	} else {
595		if resp, err = c.HTTPClient.Do(req.WithContext(subCtx)); err != nil {
596			if strings.Contains(err.Error(), "connection refused") {
597				return ErrConnectionRefused
598			}
599			return chooseError(subCtx, err)
600		}
601		if streamOptions.reqSent != nil {
602			close(streamOptions.reqSent)
603		}
604	}
605	defer resp.Body.Close()
606	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
607		return newError(resp)
608	}
609	var canceled uint32
610	if streamOptions.inactivityTimeout > 0 {
611		var ch chan<- struct{}
612		resp.Body, ch = handleInactivityTimeout(resp.Body, streamOptions.inactivityTimeout, cancelRequest, &canceled)
613		defer close(ch)
614	}
615	err = handleStreamResponse(resp, &streamOptions)
616	if err != nil {
617		if atomic.LoadUint32(&canceled) != 0 {
618			return ErrInactivityTimeout
619		}
620		return chooseError(subCtx, err)
621	}
622	return nil
623}
624
625func handleStreamResponse(resp *http.Response, streamOptions *streamOptions) error {
626	var err error
627	if !streamOptions.useJSONDecoder && resp.Header.Get("Content-Type") != "application/json" {
628		if streamOptions.setRawTerminal {
629			_, err = io.Copy(streamOptions.stdout, resp.Body)
630		} else {
631			_, err = stdcopy.StdCopy(streamOptions.stdout, streamOptions.stderr, resp.Body)
632		}
633		return err
634	}
635	// if we want to get raw json stream, just copy it back to output
636	// without decoding it
637	if streamOptions.rawJSONStream {
638		_, err = io.Copy(streamOptions.stdout, resp.Body)
639		return err
640	}
641	if st, ok := streamOptions.stdout.(interface {
642		io.Writer
643		FD() uintptr
644		IsTerminal() bool
645	}); ok {
646		err = jsonmessage.DisplayJSONMessagesToStream(resp.Body, st, nil)
647	} else {
648		err = jsonmessage.DisplayJSONMessagesStream(resp.Body, streamOptions.stdout, 0, false, nil)
649	}
650	return err
651}
652
653type proxyReader struct {
654	io.ReadCloser
655	calls uint64
656}
657
658func (p *proxyReader) callCount() uint64 {
659	return atomic.LoadUint64(&p.calls)
660}
661
662func (p *proxyReader) Read(data []byte) (int, error) {
663	atomic.AddUint64(&p.calls, 1)
664	return p.ReadCloser.Read(data)
665}
666
667func handleInactivityTimeout(reader io.ReadCloser, timeout time.Duration, cancelRequest func(), canceled *uint32) (io.ReadCloser, chan<- struct{}) {
668	done := make(chan struct{})
669	proxyReader := &proxyReader{ReadCloser: reader}
670	go func() {
671		var lastCallCount uint64
672		for {
673			select {
674			case <-time.After(timeout):
675			case <-done:
676				return
677			}
678			curCallCount := proxyReader.callCount()
679			if curCallCount == lastCallCount {
680				atomic.AddUint32(canceled, 1)
681				cancelRequest()
682				return
683			}
684			lastCallCount = curCallCount
685		}
686	}()
687	return proxyReader, done
688}
689
690type hijackOptions struct {
691	success        chan struct{}
692	setRawTerminal bool
693	in             io.Reader
694	stdout         io.Writer
695	stderr         io.Writer
696	data           interface{}
697}
698
699// CloseWaiter is an interface with methods for closing the underlying resource
700// and then waiting for it to finish processing.
701type CloseWaiter interface {
702	io.Closer
703	Wait() error
704}
705
706type waiterFunc func() error
707
708func (w waiterFunc) Wait() error { return w() }
709
710type closerFunc func() error
711
712func (c closerFunc) Close() error { return c() }
713
714func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (CloseWaiter, error) {
715	if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
716		err := c.checkAPIVersion()
717		if err != nil {
718			return nil, err
719		}
720	}
721	var params io.Reader
722	if hijackOptions.data != nil {
723		buf, err := json.Marshal(hijackOptions.data)
724		if err != nil {
725			return nil, err
726		}
727		params = bytes.NewBuffer(buf)
728	}
729	req, err := http.NewRequest(method, c.getURL(path), params)
730	if err != nil {
731		return nil, err
732	}
733	req.Header.Set("Content-Type", "application/json")
734	req.Header.Set("Connection", "Upgrade")
735	req.Header.Set("Upgrade", "tcp")
736	protocol := c.endpointURL.Scheme
737	address := c.endpointURL.Path
738	if protocol != unixProtocol && protocol != namedPipeProtocol {
739		protocol = "tcp"
740		address = c.endpointURL.Host
741	}
742	var dial net.Conn
743	if c.TLSConfig != nil && protocol != unixProtocol && protocol != namedPipeProtocol {
744		netDialer, ok := c.Dialer.(*net.Dialer)
745		if !ok {
746			return nil, ErrTLSNotSupported
747		}
748		dial, err = tlsDialWithDialer(netDialer, protocol, address, c.TLSConfig)
749		if err != nil {
750			return nil, err
751		}
752	} else {
753		dial, err = c.Dialer.Dial(protocol, address)
754		if err != nil {
755			return nil, err
756		}
757	}
758
759	errs := make(chan error, 1)
760	quit := make(chan struct{})
761	go func() {
762		clientconn := httputil.NewClientConn(dial, nil)
763		defer clientconn.Close()
764		clientconn.Do(req)
765		if hijackOptions.success != nil {
766			hijackOptions.success <- struct{}{}
767			<-hijackOptions.success
768		}
769		rwc, br := clientconn.Hijack()
770		defer rwc.Close()
771
772		errChanOut := make(chan error, 1)
773		errChanIn := make(chan error, 2)
774		if hijackOptions.stdout == nil && hijackOptions.stderr == nil {
775			close(errChanOut)
776		} else {
777			// Only copy if hijackOptions.stdout and/or hijackOptions.stderr is actually set.
778			// Otherwise, if the only stream you care about is stdin, your attach session
779			// will "hang" until the container terminates, even though you're not reading
780			// stdout/stderr
781			if hijackOptions.stdout == nil {
782				hijackOptions.stdout = ioutil.Discard
783			}
784			if hijackOptions.stderr == nil {
785				hijackOptions.stderr = ioutil.Discard
786			}
787
788			go func() {
789				defer func() {
790					if hijackOptions.in != nil {
791						if closer, ok := hijackOptions.in.(io.Closer); ok {
792							closer.Close()
793						}
794						errChanIn <- nil
795					}
796				}()
797
798				var err error
799				if hijackOptions.setRawTerminal {
800					_, err = io.Copy(hijackOptions.stdout, br)
801				} else {
802					_, err = stdcopy.StdCopy(hijackOptions.stdout, hijackOptions.stderr, br)
803				}
804				errChanOut <- err
805			}()
806		}
807
808		go func() {
809			var err error
810			if hijackOptions.in != nil {
811				_, err = io.Copy(rwc, hijackOptions.in)
812			}
813			errChanIn <- err
814			rwc.(interface {
815				CloseWrite() error
816			}).CloseWrite()
817		}()
818
819		var errIn error
820		select {
821		case errIn = <-errChanIn:
822		case <-quit:
823		}
824
825		var errOut error
826		select {
827		case errOut = <-errChanOut:
828		case <-quit:
829		}
830
831		if errIn != nil {
832			errs <- errIn
833		} else {
834			errs <- errOut
835		}
836	}()
837
838	return struct {
839		closerFunc
840		waiterFunc
841	}{
842		closerFunc(func() error { close(quit); return nil }),
843		waiterFunc(func() error { return <-errs }),
844	}, nil
845}
846
847func (c *Client) getURL(path string) string {
848	urlStr := strings.TrimRight(c.endpointURL.String(), "/")
849	if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
850		urlStr = ""
851	}
852	if c.requestedAPIVersion != nil {
853		return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedAPIVersion, path)
854	}
855	return fmt.Sprintf("%s%s", urlStr, path)
856}
857
858// getFakeNativeURL returns the URL needed to make an HTTP request over a UNIX
859// domain socket to the given path.
860func (c *Client) getFakeNativeURL(path string) string {
861	u := *c.endpointURL // Copy.
862
863	// Override URL so that net/http will not complain.
864	u.Scheme = "http"
865	u.Host = "unix.sock" // Doesn't matter what this is - it's not used.
866	u.Path = ""
867	urlStr := strings.TrimRight(u.String(), "/")
868	if c.requestedAPIVersion != nil {
869		return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedAPIVersion, path)
870	}
871	return fmt.Sprintf("%s%s", urlStr, path)
872}
873
874type jsonMessage struct {
875	Status   string `json:"status,omitempty"`
876	Progress string `json:"progress,omitempty"`
877	Error    string `json:"error,omitempty"`
878	Stream   string `json:"stream,omitempty"`
879}
880
881func queryString(opts interface{}) string {
882	if opts == nil {
883		return ""
884	}
885	value := reflect.ValueOf(opts)
886	if value.Kind() == reflect.Ptr {
887		value = value.Elem()
888	}
889	if value.Kind() != reflect.Struct {
890		return ""
891	}
892	items := url.Values(map[string][]string{})
893	for i := 0; i < value.NumField(); i++ {
894		field := value.Type().Field(i)
895		if field.PkgPath != "" {
896			continue
897		}
898		key := field.Tag.Get("qs")
899		if key == "" {
900			key = strings.ToLower(field.Name)
901		} else if key == "-" {
902			continue
903		}
904		addQueryStringValue(items, key, value.Field(i))
905	}
906	return items.Encode()
907}
908
909func addQueryStringValue(items url.Values, key string, v reflect.Value) {
910	switch v.Kind() {
911	case reflect.Bool:
912		if v.Bool() {
913			items.Add(key, "1")
914		}
915	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
916		if v.Int() > 0 {
917			items.Add(key, strconv.FormatInt(v.Int(), 10))
918		}
919	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
920		if v.Uint() > 0 {
921			items.Add(key, strconv.FormatUint(v.Uint(), 10))
922		}
923	case reflect.Float32, reflect.Float64:
924		if v.Float() > 0 {
925			items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
926		}
927	case reflect.String:
928		if v.String() != "" {
929			items.Add(key, v.String())
930		}
931	case reflect.Ptr:
932		if !v.IsNil() {
933			if b, err := json.Marshal(v.Interface()); err == nil {
934				items.Add(key, string(b))
935			}
936		}
937	case reflect.Map:
938		if len(v.MapKeys()) > 0 {
939			if b, err := json.Marshal(v.Interface()); err == nil {
940				items.Add(key, string(b))
941			}
942		}
943	case reflect.Array, reflect.Slice:
944		vLen := v.Len()
945		if vLen > 0 {
946			for i := 0; i < vLen; i++ {
947				addQueryStringValue(items, key, v.Index(i))
948			}
949		}
950	}
951}
952
953// Error represents failures in the API. It represents a failure from the API.
954type Error struct {
955	Status  int
956	Message string
957}
958
959func newError(resp *http.Response) *Error {
960	type ErrMsg struct {
961		Message string `json:"message"`
962	}
963	defer resp.Body.Close()
964	data, err := ioutil.ReadAll(resp.Body)
965	if err != nil {
966		return &Error{Status: resp.StatusCode, Message: fmt.Sprintf("cannot read body, err: %v", err)}
967	}
968	var emsg ErrMsg
969	err = json.Unmarshal(data, &emsg)
970	if err != nil {
971		return &Error{Status: resp.StatusCode, Message: string(data)}
972	}
973	return &Error{Status: resp.StatusCode, Message: emsg.Message}
974}
975
976func (e *Error) Error() string {
977	return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
978}
979
980func parseEndpoint(endpoint string, tls bool) (*url.URL, error) {
981	if endpoint != "" && !strings.Contains(endpoint, "://") {
982		endpoint = "tcp://" + endpoint
983	}
984	u, err := url.Parse(endpoint)
985	if err != nil {
986		return nil, ErrInvalidEndpoint
987	}
988	if tls && u.Scheme != "unix" {
989		u.Scheme = "https"
990	}
991	switch u.Scheme {
992	case unixProtocol, namedPipeProtocol:
993		return u, nil
994	case "http", "https", "tcp":
995		_, port, err := net.SplitHostPort(u.Host)
996		if err != nil {
997			if e, ok := err.(*net.AddrError); ok {
998				if e.Err == "missing port in address" {
999					return u, nil
1000				}
1001			}
1002			return nil, ErrInvalidEndpoint
1003		}
1004		number, err := strconv.ParseInt(port, 10, 64)
1005		if err == nil && number > 0 && number < 65536 {
1006			if u.Scheme == "tcp" {
1007				if tls {
1008					u.Scheme = "https"
1009				} else {
1010					u.Scheme = "http"
1011				}
1012			}
1013			return u, nil
1014		}
1015		return nil, ErrInvalidEndpoint
1016	default:
1017		return nil, ErrInvalidEndpoint
1018	}
1019}
1020
1021type dockerEnv struct {
1022	dockerHost      string
1023	dockerTLSVerify bool
1024	dockerCertPath  string
1025}
1026
1027func getDockerEnv() (*dockerEnv, error) {
1028	dockerHost := os.Getenv("DOCKER_HOST")
1029	var err error
1030	if dockerHost == "" {
1031		dockerHost = opts.DefaultHost
1032	}
1033	dockerTLSVerify := os.Getenv("DOCKER_TLS_VERIFY") != ""
1034	var dockerCertPath string
1035	if dockerTLSVerify {
1036		dockerCertPath = os.Getenv("DOCKER_CERT_PATH")
1037		if dockerCertPath == "" {
1038			home := homedir.Get()
1039			if home == "" {
1040				return nil, errors.New("environment variable HOME must be set if DOCKER_CERT_PATH is not set")
1041			}
1042			dockerCertPath = filepath.Join(home, ".docker")
1043			dockerCertPath, err = filepath.Abs(dockerCertPath)
1044			if err != nil {
1045				return nil, err
1046			}
1047		}
1048	}
1049	return &dockerEnv{
1050		dockerHost:      dockerHost,
1051		dockerTLSVerify: dockerTLSVerify,
1052		dockerCertPath:  dockerCertPath,
1053	}, nil
1054}
1055
1056// defaultTransport returns a new http.Transport with similar default values to
1057// http.DefaultTransport, but with idle connections and keepalives disabled.
1058func defaultTransport() *http.Transport {
1059	transport := defaultPooledTransport()
1060	transport.DisableKeepAlives = true
1061	transport.MaxIdleConnsPerHost = -1
1062	return transport
1063}
1064
1065// defaultPooledTransport returns a new http.Transport with similar default
1066// values to http.DefaultTransport. Do not use this for transient transports as
1067// it can leak file descriptors over time. Only use this for transports that
1068// will be re-used for the same host(s).
1069func defaultPooledTransport() *http.Transport {
1070	transport := &http.Transport{
1071		Proxy: http.ProxyFromEnvironment,
1072		DialContext: (&net.Dialer{
1073			Timeout:   30 * time.Second,
1074			KeepAlive: 30 * time.Second,
1075		}).DialContext,
1076		MaxIdleConns:          100,
1077		IdleConnTimeout:       90 * time.Second,
1078		TLSHandshakeTimeout:   10 * time.Second,
1079		ExpectContinueTimeout: 1 * time.Second,
1080		MaxIdleConnsPerHost:   runtime.GOMAXPROCS(0) + 1,
1081	}
1082	return transport
1083}
1084
1085// defaultClient returns a new http.Client with similar default values to
1086// http.Client, but with a non-shared Transport, idle connections disabled, and
1087// keepalives disabled.
1088func defaultClient() *http.Client {
1089	return &http.Client{
1090		Transport: defaultTransport(),
1091	}
1092}
1093