func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
	timeout := dialer.Timeout
	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}
	var errChannel chan error
	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- errors.New("")
		})
	}
	proxyDialer, err := sockets.DialerFromEnvironment(dialer)
	if err != nil {
		return nil, err
	}
	rawConn, err := proxyDialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	if tcpConn, ok := rawConn.(*net.TCPConn); ok {
		tcpConn.SetKeepAlive(true)
		tcpConn.SetKeepAlivePeriod(30 * time.Second)
	}
	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]
	if config.ServerName == "" {
		config = transport.TLSConfigClone(config)
		config.ServerName = hostname
	}
	conn := tls.Client(rawConn, config)
	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()
		err = <-errChannel
	}
	if err != nil {
		rawConn.Close()
		return nil, err
	}
	return &tlsClientCon{conn, rawConn}, nil
}
We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in
order to return our custom tlsClientCon struct which holds both the tls.Conn
object _and_ its underlying raw connection. The rationale for this is that
we need to be able to close the write end of the connection when attaching,
which tls.Conn does not provide.
package client
import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
	"github.com/docker/engine-api/client/transport"
	"github.com/docker/engine-api/types"
	"github.com/docker/go-connections/sockets"
	"golang.org/x/net/context"
)
type tlsClientCon struct {
	*tls.Conn
	rawConn net.Conn
}
func (c *tlsClientCon) CloseWrite() error {
	if conn, ok := c.rawConn.(types.CloseWriter); ok {
		return conn.CloseWrite()
	}
	return nil
}
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
	bodyEncoded, err := encodeData(body)
	if err != nil {
		return types.HijackedResponse{}, err
	}
	req, err := cli.newRequest("POST", path, query, bodyEncoded, headers)
	if err != nil {
		return types.HijackedResponse{}, err
	}
	req.Host = cli.addr
	req.Header.Set("Connection", "Upgrade")
	req.Header.Set("Upgrade", "tcp")
	conn, err := dial(cli.proto, cli.addr, cli.transport.TLSConfig())
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
		}
		return types.HijackedResponse{}, err
	}
	if tcpConn, ok := conn.(*net.TCPConn); ok {
		tcpConn.SetKeepAlive(true)
		tcpConn.SetKeepAlivePeriod(30 * time.Second)
	}
	clientconn := httputil.NewClientConn(conn, nil)
	defer clientconn.Close()
	_, err = clientconn.Do(req)
	rwc, br := clientconn.Hijack()
	return types.HijackedResponse{Conn: rwc, Reader: br}, err
}
func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
	return tlsDialWithDialer(new(net.Dialer), network, addr, config)
}