mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	godep restore pushd $GOPATH/src/github.com/appc/spec git co master popd go get go4.org/errorutil rm -rf Godeps godep save ./... git add vendor git add -f $(git ls-files --other vendor/) git co -- Godeps/LICENSES Godeps/.license_file_state Godeps/OWNERS
		
			
				
	
	
		
			175 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package client
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"net/http/httputil"
 | |
| 	"net/url"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/docker/engine-api/types"
 | |
| 	"github.com/docker/go-connections/sockets"
 | |
| 	"golang.org/x/net/context"
 | |
| )
 | |
| 
 | |
| // tlsClientCon holds tls information and a dialed connection.
 | |
| type tlsClientCon struct {
 | |
| 	*tls.Conn
 | |
| 	rawConn net.Conn
 | |
| }
 | |
| 
 | |
| func (c *tlsClientCon) CloseWrite() error {
 | |
| 	// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
 | |
| 	// on its underlying connection.
 | |
| 	if conn, ok := c.rawConn.(types.CloseWriter); ok {
 | |
| 		return conn.CloseWrite()
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // postHijacked sends a POST request and hijacks the connection.
 | |
| 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
 | |
| 	}
 | |
| 
 | |
| 	// When we set up a TCP connection for hijack, there could be long periods
 | |
| 	// of inactivity (a long running command with no output) that in certain
 | |
| 	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
 | |
| 	// state. Setting TCP KeepAlive on the socket connection will prohibit
 | |
| 	// ECONNTIMEOUT unless the socket connection truly is broken
 | |
| 	if tcpConn, ok := conn.(*net.TCPConn); ok {
 | |
| 		tcpConn.SetKeepAlive(true)
 | |
| 		tcpConn.SetKeepAlivePeriod(30 * time.Second)
 | |
| 	}
 | |
| 
 | |
| 	clientconn := httputil.NewClientConn(conn, nil)
 | |
| 	defer clientconn.Close()
 | |
| 
 | |
| 	// Server hijacks the connection, error 'connection closed' expected
 | |
| 	_, 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)
 | |
| }
 | |
| 
 | |
| // 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.
 | |
| func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
 | |
| 	// We want the Timeout and Deadline values from dialer to cover the
 | |
| 	// whole process: TCP connection and TLS handshake. This means that we
 | |
| 	// also need to start our own timers now.
 | |
| 	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
 | |
| 	}
 | |
| 	// When we set up a TCP connection for hijack, there could be long periods
 | |
| 	// of inactivity (a long running command with no output) that in certain
 | |
| 	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
 | |
| 	// state. Setting TCP KeepAlive on the socket connection will prohibit
 | |
| 	// ECONNTIMEOUT unless the socket connection truly is broken
 | |
| 	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 no ServerName is set, infer the ServerName
 | |
| 	// from the hostname we're connecting to.
 | |
| 	if config.ServerName == "" {
 | |
| 		// Make a copy to avoid polluting argument or default.
 | |
| 		c := *config
 | |
| 		c.ServerName = hostname
 | |
| 		config = &c
 | |
| 	}
 | |
| 
 | |
| 	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
 | |
| 	}
 | |
| 
 | |
| 	// This is Docker difference with standard's crypto/tls package: returned a
 | |
| 	// wrapper which holds both the TLS and raw connections.
 | |
| 	return &tlsClientCon{conn, rawConn}, nil
 | |
| }
 | |
| 
 | |
| func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
 | |
| 	if tlsConfig != nil && proto != "unix" && proto != "npipe" {
 | |
| 		// Notice this isn't Go standard's tls.Dial function
 | |
| 		return tlsDial(proto, addr, tlsConfig)
 | |
| 	}
 | |
| 	if proto == "npipe" {
 | |
| 		return sockets.DialPipe(addr, 32*time.Second)
 | |
| 	}
 | |
| 	return net.Dial(proto, addr)
 | |
| }
 |