...

Source file src/k8s.io/client-go/tools/portforward/tunneling_dialer.go

Documentation: k8s.io/client-go/tools/portforward

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package portforward
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"net/url"
    23  	"strings"
    24  	"time"
    25  
    26  	"k8s.io/apimachinery/pkg/util/httpstream"
    27  	"k8s.io/apimachinery/pkg/util/httpstream/spdy"
    28  	constants "k8s.io/apimachinery/pkg/util/portforward"
    29  	restclient "k8s.io/client-go/rest"
    30  	"k8s.io/client-go/transport/websocket"
    31  	"k8s.io/klog/v2"
    32  )
    33  
    34  const PingPeriod = 10 * time.Second
    35  
    36  // tunnelingDialer implements "httpstream.Dial" interface
    37  type tunnelingDialer struct {
    38  	url       *url.URL
    39  	transport http.RoundTripper
    40  	holder    websocket.ConnectionHolder
    41  }
    42  
    43  // NewTunnelingDialer creates and returns the tunnelingDialer structure which implemements the "httpstream.Dialer"
    44  // interface. The dialer can upgrade a websocket request, creating a websocket connection. This function
    45  // returns an error if one occurs.
    46  func NewSPDYOverWebsocketDialer(url *url.URL, config *restclient.Config) (httpstream.Dialer, error) {
    47  	transport, holder, err := websocket.RoundTripperFor(config)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	return &tunnelingDialer{
    52  		url:       url,
    53  		transport: transport,
    54  		holder:    holder,
    55  	}, nil
    56  }
    57  
    58  // Dial upgrades to a tunneling streaming connection, returning a SPDY connection
    59  // containing a WebSockets connection (which implements "net.Conn"). Also
    60  // returns the protocol negotiated, or an error.
    61  func (d *tunnelingDialer) Dial(protocols ...string) (httpstream.Connection, string, error) {
    62  	// There is no passed context, so skip the context when creating request for now.
    63  	// Websockets requires "GET" method: RFC 6455 Sec. 4.1 (page 17).
    64  	req, err := http.NewRequest("GET", d.url.String(), nil)
    65  	if err != nil {
    66  		return nil, "", err
    67  	}
    68  	// Add the spdy tunneling prefix to the requested protocols. The tunneling
    69  	// handler will know how to negotiate these protocols.
    70  	tunnelingProtocols := []string{}
    71  	for _, protocol := range protocols {
    72  		tunnelingProtocol := constants.WebsocketsSPDYTunnelingPrefix + protocol
    73  		tunnelingProtocols = append(tunnelingProtocols, tunnelingProtocol)
    74  	}
    75  	klog.V(4).Infoln("Before WebSocket Upgrade Connection...")
    76  	conn, err := websocket.Negotiate(d.transport, d.holder, req, tunnelingProtocols...)
    77  	if err != nil {
    78  		return nil, "", err
    79  	}
    80  	if conn == nil {
    81  		return nil, "", fmt.Errorf("negotiated websocket connection is nil")
    82  	}
    83  	protocol := conn.Subprotocol()
    84  	protocol = strings.TrimPrefix(protocol, constants.WebsocketsSPDYTunnelingPrefix)
    85  	klog.V(4).Infof("negotiated protocol: %s", protocol)
    86  
    87  	// Wrap the websocket connection which implements "net.Conn".
    88  	tConn := NewTunnelingConnection("client", conn)
    89  	// Create SPDY connection injecting the previously created tunneling connection.
    90  	spdyConn, err := spdy.NewClientConnectionWithPings(tConn, PingPeriod)
    91  
    92  	return spdyConn, protocol, err
    93  }
    94  

View as plain text