func chooseError(ctx context.Context, err error) error {
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		return err
	}
}
if error in context, return that instead of generic http error
package docker
import (
	"bufio"
	"bytes"
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/homedir"
	"github.com/docker/docker/pkg/stdcopy"
	"github.com/fsouza/go-dockerclient/internal/jsonmessage"
)
const (
	userAgent = "go-dockerclient"
	unixProtocol      = "unix"
	namedPipeProtocol = "npipe"
)
var (
	ErrInvalidEndpoint = errors.New("invalid endpoint")
	ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
	ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
	apiVersion112, _ = NewAPIVersion("1.12")
	apiVersion119, _ = NewAPIVersion("1.19")
	apiVersion124, _ = NewAPIVersion("1.24")
	apiVersion125, _ = NewAPIVersion("1.25")
	apiVersion135, _ = NewAPIVersion("1.35")
)
type APIVersion []int
func NewAPIVersion(input string) (APIVersion, error) {
	if !strings.Contains(input, ".") {
		return nil, fmt.Errorf("Unable to parse version %q", input)
	}
	raw := strings.Split(input, "-")
	arr := strings.Split(raw[0], ".")
	ret := make(APIVersion, len(arr))
	var err error
	for i, val := range arr {
		ret[i], err = strconv.Atoi(val)
		if err != nil {
			return nil, fmt.Errorf("Unable to parse version %q: %q is not an integer", input, val)
		}
	}
	return ret, nil
}
func (version APIVersion) String() string {
	var str string
	for i, val := range version {
		str += strconv.Itoa(val)
		if i < len(version)-1 {
			str += "."
		}
	}
	return str
}
func (version APIVersion) LessThan(other APIVersion) bool {
	return version.compare(other) < 0
}
func (version APIVersion) LessThanOrEqualTo(other APIVersion) bool {
	return version.compare(other) <= 0
}
func (version APIVersion) GreaterThan(other APIVersion) bool {
	return version.compare(other) > 0
}
func (version APIVersion) GreaterThanOrEqualTo(other APIVersion) bool {
	return version.compare(other) >= 0
}
func (version APIVersion) compare(other APIVersion) int {
	for i, v := range version {
		if i <= len(other)-1 {
			otherVersion := other[i]
			if v < otherVersion {
				return -1
			} else if v > otherVersion {
				return 1
			}
		}
	}
	if len(version) > len(other) {
		return 1
	}
	if len(version) < len(other) {
		return -1
	}
	return 0
}
type Client struct {
	SkipServerVersionCheck bool
	HTTPClient             *http.Client
	TLSConfig              *tls.Config
	Dialer                 Dialer
	endpoint            string
	endpointURL         *url.URL
	eventMonitor        *eventMonitoringState
	requestedAPIVersion APIVersion
	serverAPIVersion    APIVersion
	expectedAPIVersion  APIVersion
}
type Dialer interface {
	Dial(network, address string) (net.Conn, error)
}
func NewClient(endpoint string) (*Client, error) {
	client, err := NewVersionedClient(endpoint, "")
	if err != nil {
		return nil, err
	}
	client.SkipServerVersionCheck = true
	return client, nil
}
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
	client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "")
	if err != nil {
		return nil, err
	}
	client.SkipServerVersionCheck = true
	return client, nil
}
func NewTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte) (*Client, error) {
	client, err := NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, "")
	if err != nil {
		return nil, err
	}
	client.SkipServerVersionCheck = true
	return client, nil
}
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
	u, err := parseEndpoint(endpoint, false)
	if err != nil {
		return nil, err
	}
	var requestedAPIVersion APIVersion
	if strings.Contains(apiVersionString, ".") {
		requestedAPIVersion, err = NewAPIVersion(apiVersionString)
		if err != nil {
			return nil, err
		}
	}
	c := &Client{
		HTTPClient:          defaultClient(),
		Dialer:              &net.Dialer{},
		endpoint:            endpoint,
		endpointURL:         u,
		eventMonitor:        new(eventMonitoringState),
		requestedAPIVersion: requestedAPIVersion,
	}
	c.initializeNativeClient(defaultTransport)
	return c, nil
}
func (c *Client) WithTransport(trFunc func() *http.Transport) {
	c.initializeNativeClient(trFunc)
}
func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
	return NewVersionedTLSClient(endpoint, cert, key, ca, apiVersionString)
}
func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
	var certPEMBlock []byte
	var keyPEMBlock []byte
	var caPEMCert []byte
	if _, err := os.Stat(cert); !os.IsNotExist(err) {
		certPEMBlock, err = ioutil.ReadFile(cert)
		if err != nil {
			return nil, err
		}
	}
	if _, err := os.Stat(key); !os.IsNotExist(err) {
		keyPEMBlock, err = ioutil.ReadFile(key)
		if err != nil {
			return nil, err
		}
	}
	if _, err := os.Stat(ca); !os.IsNotExist(err) {
		caPEMCert, err = ioutil.ReadFile(ca)
		if err != nil {
			return nil, err
		}
	}
	return NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, apiVersionString)
}
func NewClientFromEnv() (*Client, error) {
	client, err := NewVersionedClientFromEnv("")
	if err != nil {
		return nil, err
	}
	client.SkipServerVersionCheck = true
	return client, nil
}
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
	dockerEnv, err := getDockerEnv()
	if err != nil {
		return nil, err
	}
	dockerHost := dockerEnv.dockerHost
	if dockerEnv.dockerTLSVerify {
		parts := strings.SplitN(dockerEnv.dockerHost, "://", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("could not split %s into two parts by ://", dockerHost)
		}
		cert := filepath.Join(dockerEnv.dockerCertPath, "cert.pem")
		key := filepath.Join(dockerEnv.dockerCertPath, "key.pem")
		ca := filepath.Join(dockerEnv.dockerCertPath, "ca.pem")
		return NewVersionedTLSClient(dockerEnv.dockerHost, cert, key, ca, apiVersionString)
	}
	return NewVersionedClient(dockerEnv.dockerHost, apiVersionString)
}
func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte, apiVersionString string) (*Client, error) {
	u, err := parseEndpoint(endpoint, true)
	if err != nil {
		return nil, err
	}
	var requestedAPIVersion APIVersion
	if strings.Contains(apiVersionString, ".") {
		requestedAPIVersion, err = NewAPIVersion(apiVersionString)
		if err != nil {
			return nil, err
		}
	}
	tlsConfig := &tls.Config{}
	if certPEMBlock != nil && keyPEMBlock != nil {
		tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{tlsCert}
	}
	if caPEMCert == nil {
		tlsConfig.InsecureSkipVerify = true
	} else {
		caPool := x509.NewCertPool()
		if !caPool.AppendCertsFromPEM(caPEMCert) {
			return nil, errors.New("Could not add RootCA pem")
		}
		tlsConfig.RootCAs = caPool
	}
	tr := defaultTransport()
	tr.TLSClientConfig = tlsConfig
	if err != nil {
		return nil, err
	}
	c := &Client{
		HTTPClient:          &http.Client{Transport: tr},
		TLSConfig:           tlsConfig,
		Dialer:              &net.Dialer{},
		endpoint:            endpoint,
		endpointURL:         u,
		eventMonitor:        new(eventMonitoringState),
		requestedAPIVersion: requestedAPIVersion,
	}
	c.initializeNativeClient(defaultTransport)
	return c, nil
}
func (c *Client) SetTimeout(t time.Duration) {
	if c.HTTPClient != nil {
		c.HTTPClient.Timeout = t
	}
}
func (c *Client) checkAPIVersion() error {
	serverAPIVersionString, err := c.getServerAPIVersionString()
	if err != nil {
		return err
	}
	c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
	if err != nil {
		return err
	}
	if c.requestedAPIVersion == nil {
		c.expectedAPIVersion = c.serverAPIVersion
	} else {
		c.expectedAPIVersion = c.requestedAPIVersion
	}
	return nil
}
func (c *Client) Endpoint() string {
	return c.endpoint
}
func (c *Client) Ping() error {
	return c.PingWithContext(nil)
}
func (c *Client) PingWithContext(ctx context.Context) error {
	path := "/_ping"
	resp, err := c.do("GET", path, doOptions{context: ctx})
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return newError(resp)
	}
	resp.Body.Close()
	return nil
}
func (c *Client) getServerAPIVersionString() (version string, err error) {
	resp, err := c.do("GET", "/version", doOptions{})
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", resp.StatusCode)
	}
	var versionResponse map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&versionResponse); err != nil {
		return "", err
	}
	if version, ok := (versionResponse["ApiVersion"]).(string); ok {
		return version, nil
	}
	return "", nil
}
type doOptions struct {
	data      interface{}
	forceJSON bool
	headers   map[string]string
	context   context.Context
}
func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, error) {
	var params io.Reader
	if doOptions.data != nil || doOptions.forceJSON {
		buf, err := json.Marshal(doOptions.data)
		if err != nil {
			return nil, err
		}
		params = bytes.NewBuffer(buf)
	}
	if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
		err := c.checkAPIVersion()
		if err != nil {
			return nil, err
		}
	}
	protocol := c.endpointURL.Scheme
	var u string
	switch protocol {
	case unixProtocol, namedPipeProtocol:
		u = c.getFakeNativeURL(path)
	default:
		u = c.getURL(path)
	}
	req, err := http.NewRequest(method, u, params)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", userAgent)
	if doOptions.data != nil {
		req.Header.Set("Content-Type", "application/json")
	} else if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	for k, v := range doOptions.headers {
		req.Header.Set(k, v)
	}
	ctx := doOptions.context
	if ctx == nil {
		ctx = context.Background()
	}
	resp, err := c.HTTPClient.Do(req.WithContext(ctx))
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return nil, ErrConnectionRefused
		}
		return nil, chooseError(ctx, err)
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, newError(resp)
	}
	return resp, nil
}
type streamOptions struct {
	setRawTerminal bool
	rawJSONStream  bool
	useJSONDecoder bool
	headers        map[string]string
	in             io.Reader
	stdout         io.Writer
	stderr         io.Writer
	reqSent        chan struct{}
	timeout time.Duration
	inactivityTimeout time.Duration
	context           context.Context
}