k8s.io/client-go@v0.22.2/transport/spdy/spdy.go (about)

     1  /*
     2  Copyright 2017 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 spdy
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"net/url"
    23  	"time"
    24  
    25  	"k8s.io/apimachinery/pkg/util/httpstream"
    26  	"k8s.io/apimachinery/pkg/util/httpstream/spdy"
    27  	restclient "k8s.io/client-go/rest"
    28  )
    29  
    30  // Upgrader validates a response from the server after a SPDY upgrade.
    31  type Upgrader interface {
    32  	// NewConnection validates the response and creates a new Connection.
    33  	NewConnection(resp *http.Response) (httpstream.Connection, error)
    34  }
    35  
    36  // RoundTripperFor returns a round tripper and upgrader to use with SPDY.
    37  func RoundTripperFor(config *restclient.Config) (http.RoundTripper, Upgrader, error) {
    38  	tlsConfig, err := restclient.TLSConfigFor(config)
    39  	if err != nil {
    40  		return nil, nil, err
    41  	}
    42  	proxy := http.ProxyFromEnvironment
    43  	if config.Proxy != nil {
    44  		proxy = config.Proxy
    45  	}
    46  	upgradeRoundTripper := spdy.NewRoundTripperWithConfig(spdy.RoundTripperConfig{
    47  		TLS:                      tlsConfig,
    48  		FollowRedirects:          true,
    49  		RequireSameHostRedirects: false,
    50  		Proxier:                  proxy,
    51  		PingPeriod:               time.Second * 5,
    52  	})
    53  	wrapper, err := restclient.HTTPWrappersForConfig(config, upgradeRoundTripper)
    54  	if err != nil {
    55  		return nil, nil, err
    56  	}
    57  	return wrapper, upgradeRoundTripper, nil
    58  }
    59  
    60  // dialer implements the httpstream.Dialer interface.
    61  type dialer struct {
    62  	client   *http.Client
    63  	upgrader Upgrader
    64  	method   string
    65  	url      *url.URL
    66  }
    67  
    68  var _ httpstream.Dialer = &dialer{}
    69  
    70  // NewDialer will create a dialer that connects to the provided URL and upgrades the connection to SPDY.
    71  func NewDialer(upgrader Upgrader, client *http.Client, method string, url *url.URL) httpstream.Dialer {
    72  	return &dialer{
    73  		client:   client,
    74  		upgrader: upgrader,
    75  		method:   method,
    76  		url:      url,
    77  	}
    78  }
    79  
    80  func (d *dialer) Dial(protocols ...string) (httpstream.Connection, string, error) {
    81  	req, err := http.NewRequest(d.method, d.url.String(), nil)
    82  	if err != nil {
    83  		return nil, "", fmt.Errorf("error creating request: %v", err)
    84  	}
    85  	return Negotiate(d.upgrader, d.client, req, protocols...)
    86  }
    87  
    88  // Negotiate opens a connection to a remote server and attempts to negotiate
    89  // a SPDY connection. Upon success, it returns the connection and the protocol selected by
    90  // the server. The client transport must use the upgradeRoundTripper - see RoundTripperFor.
    91  func Negotiate(upgrader Upgrader, client *http.Client, req *http.Request, protocols ...string) (httpstream.Connection, string, error) {
    92  	for i := range protocols {
    93  		req.Header.Add(httpstream.HeaderProtocolVersion, protocols[i])
    94  	}
    95  	resp, err := client.Do(req)
    96  	if err != nil {
    97  		return nil, "", fmt.Errorf("error sending request: %v", err)
    98  	}
    99  	defer resp.Body.Close()
   100  	conn, err := upgrader.NewConnection(resp)
   101  	if err != nil {
   102  		return nil, "", err
   103  	}
   104  	return conn, resp.Header.Get(httpstream.HeaderProtocolVersion), nil
   105  }