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