pkg/util/portforward/dialer.go (86 lines of code) (raw):

package portforward // Copyright (c) Microsoft Corporation. // Licensed under the Apache License 2.0. import ( "bufio" "context" "crypto/tls" "fmt" "net" "net/http" "net/url" "time" "k8s.io/apimachinery/pkg/util/httpstream" "k8s.io/apimachinery/pkg/util/httpstream/spdy" "k8s.io/client-go/rest" "k8s.io/client-go/tools/portforward" ) // dialSpdy connects to the specified path on the API server of oc and // negotiates SPDY func dialSpdy(ctx context.Context, restconfig *rest.Config, path string) (httpstream.Connection, error) { // 1. Connect to the API server via private endpoint (and via proxy in // development mode) clusterURL, err := url.Parse(restconfig.Host) if err != nil { return nil, err } dialContext := restconfig.Dial if dialContext == nil { dialContext = (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext } rawConn, err := dialContext(ctx, "tcp", clusterURL.Host) if err != nil { return nil, err } // 2. Negotiate TLS tlsConfig, err := rest.TLSConfigFor(restconfig) if err != nil { rawConn.Close() return nil, err } if tlsConfig == nil { tlsConfig = &tls.Config{} } tlsConfig.ServerName = clusterURL.Hostname() tlsConn := tls.Client(rawConn, tlsConfig) err = tlsConn.Handshake() if err != nil { tlsConn.Close() return nil, err } // 3. Issue an HTTP POST request to the specified path req, err := http.NewRequest(http.MethodPost, path, nil) if err != nil { tlsConn.Close() return nil, err } req.Header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) req.Header.Add(httpstream.HeaderProtocolVersion, portforward.PortForwardProtocolV1Name) req.Header.Add(httpstream.HeaderUpgrade, spdy.HeaderSpdy31) if restconfig.BearerToken != "" { req.Header.Add("Authorization", "Bearer "+restconfig.BearerToken) } err = req.Write(tlsConn) if err != nil { tlsConn.Close() return nil, err } // 4. Validate the response resp, err := http.ReadResponse(bufio.NewReader(tlsConn), req) if err != nil { tlsConn.Close() return nil, err } if resp.StatusCode != http.StatusSwitchingProtocols { tlsConn.Close() return nil, fmt.Errorf("unexpected http status code %d", resp.StatusCode) } if resp.Header.Get(httpstream.HeaderConnection) != httpstream.HeaderUpgrade { tlsConn.Close() return nil, fmt.Errorf("unexpected http header %s: %s", httpstream.HeaderConnection, resp.Header.Get(httpstream.HeaderConnection)) } if resp.Header.Get(httpstream.HeaderUpgrade) != spdy.HeaderSpdy31 { tlsConn.Close() return nil, fmt.Errorf("unexpected http header %s: %s", httpstream.HeaderUpgrade, resp.Header.Get(httpstream.HeaderUpgrade)) } // 5. Negotiate SPDY spdyConn, err := spdy.NewClientConnection(tlsConn) if err != nil { tlsConn.Close() return nil, err } return spdyConn, nil }