github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/proxy/dial.go (about)

     1  /*
     2  Copyright 2015 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 proxy
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"net"
    24  	"net/http"
    25  	"net/url"
    26  
    27  	utilnet "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/net"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/third_party/forked/golang/netutil"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  // dialURL will dial the specified URL using the underlying dialer held by the passed
    33  // RoundTripper. The primary use of this method is to support proxying upgradable connections.
    34  // For this reason this method will prefer to negotiate http/1.1 if the URL scheme is https.
    35  // If you wish to ensure ALPN negotiates http2 then set NextProto=[]string{"http2"} in the
    36  // TLSConfig of the http.Transport
    37  func dialURL(ctx context.Context, url *url.URL, transport http.RoundTripper) (net.Conn, error) {
    38  	dialAddr := netutil.CanonicalAddr(url)
    39  
    40  	dialer, err := utilnet.DialerFor(transport)
    41  	if err != nil {
    42  		klog.V(5).Infof("Unable to unwrap transport %T to get dialer: %v", transport, err)
    43  	}
    44  
    45  	switch url.Scheme {
    46  	case "http":
    47  		if dialer != nil {
    48  			return dialer(ctx, "tcp", dialAddr)
    49  		}
    50  		var d net.Dialer
    51  		return d.DialContext(ctx, "tcp", dialAddr)
    52  	case "https":
    53  		// Get the tls config from the transport if we recognize it
    54  		tlsConfig, err := utilnet.TLSClientConfig(transport)
    55  		if err != nil {
    56  			klog.V(5).Infof("Unable to unwrap transport %T to get at TLS config: %v", transport, err)
    57  		}
    58  
    59  		if dialer != nil {
    60  			// We have a dialer; use it to open the connection, then
    61  			// create a tls client using the connection.
    62  			netConn, err := dialer(ctx, "tcp", dialAddr)
    63  			if err != nil {
    64  				return nil, err
    65  			}
    66  			if tlsConfig == nil {
    67  				// tls.Client requires non-nil config
    68  				klog.Warning("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
    69  				// tls.Handshake() requires ServerName or InsecureSkipVerify
    70  				tlsConfig = &tls.Config{
    71  					InsecureSkipVerify: true,
    72  				}
    73  			} else if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
    74  				// tls.HandshakeContext() requires ServerName or InsecureSkipVerify
    75  				// infer the ServerName from the hostname we're connecting to.
    76  				inferredHost := dialAddr
    77  				if host, _, err := net.SplitHostPort(dialAddr); err == nil {
    78  					inferredHost = host
    79  				}
    80  				// Make a copy to avoid polluting the provided config
    81  				tlsConfigCopy := tlsConfig.Clone()
    82  				tlsConfigCopy.ServerName = inferredHost
    83  				tlsConfig = tlsConfigCopy
    84  			}
    85  
    86  			// Since this method is primarily used within a "Connection: Upgrade" call we assume the caller is
    87  			// going to write HTTP/1.1 request to the wire. http2 should not be allowed in the TLSConfig.NextProtos,
    88  			// so we explicitly set that here. We only do this check if the TLSConfig support http/1.1.
    89  			if supportsHTTP11(tlsConfig.NextProtos) {
    90  				tlsConfig = tlsConfig.Clone()
    91  				tlsConfig.NextProtos = []string{"http/1.1"}
    92  			}
    93  
    94  			tlsConn := tls.Client(netConn, tlsConfig)
    95  			if err := tlsConn.HandshakeContext(ctx); err != nil {
    96  				netConn.Close()
    97  				return nil, err
    98  			}
    99  			return tlsConn, nil
   100  		} else {
   101  			// Dial.
   102  			tlsDialer := tls.Dialer{
   103  				Config: tlsConfig,
   104  			}
   105  			return tlsDialer.DialContext(ctx, "tcp", dialAddr)
   106  		}
   107  	default:
   108  		return nil, fmt.Errorf("unknown scheme: %s", url.Scheme)
   109  	}
   110  }
   111  
   112  func supportsHTTP11(nextProtos []string) bool {
   113  	if len(nextProtos) == 0 {
   114  		return true
   115  	}
   116  	for _, proto := range nextProtos {
   117  		if proto == "http/1.1" {
   118  			return true
   119  		}
   120  	}
   121  	return false
   122  }