github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/httpstream/spdy/roundtripper_test.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 spdy
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/base64"
    24  	"io"
    25  	"net"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"net/url"
    29  	"strconv"
    30  	"testing"
    31  
    32  	"github.com/armon/go-socks5"
    33  	"github.com/elazarl/goproxy"
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  
    37  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/httpstream"
    38  )
    39  
    40  type serverHandlerConfig struct {
    41  	shouldError      bool
    42  	statusCode       int
    43  	connectionHeader string
    44  	upgradeHeader    string
    45  }
    46  
    47  func serverHandler(t *testing.T, config serverHandlerConfig) http.HandlerFunc {
    48  	return func(w http.ResponseWriter, req *http.Request) {
    49  		if config.shouldError {
    50  			if e, a := httpstream.HeaderUpgrade, req.Header.Get(httpstream.HeaderConnection); e != a {
    51  				t.Fatalf("expected connection=upgrade header, got '%s", a)
    52  			}
    53  
    54  			w.Header().Set(httpstream.HeaderConnection, config.connectionHeader)
    55  			w.Header().Set(httpstream.HeaderUpgrade, config.upgradeHeader)
    56  			w.WriteHeader(config.statusCode)
    57  
    58  			return
    59  		}
    60  
    61  		streamCh := make(chan httpstream.Stream)
    62  
    63  		responseUpgrader := NewResponseUpgrader()
    64  		spdyConn := responseUpgrader.UpgradeResponse(w, req, func(s httpstream.Stream, replySent <-chan struct{}) error {
    65  			streamCh <- s
    66  			return nil
    67  		})
    68  		if spdyConn == nil {
    69  			t.Fatal("unexpected nil spdyConn")
    70  		}
    71  		defer spdyConn.Close()
    72  
    73  		stream := <-streamCh
    74  		io.Copy(stream, stream)
    75  	}
    76  }
    77  
    78  type serverFunc func(http.Handler) *httptest.Server
    79  
    80  func httpsServerInvalidHostname(t *testing.T) serverFunc {
    81  	return func(h http.Handler) *httptest.Server {
    82  		cert, err := tls.X509KeyPair(exampleCert, exampleKey)
    83  		if err != nil {
    84  			t.Errorf("https (invalid hostname): proxy_test: %v", err)
    85  		}
    86  		ts := httptest.NewUnstartedServer(h)
    87  		ts.TLS = &tls.Config{
    88  			Certificates: []tls.Certificate{cert},
    89  		}
    90  		ts.StartTLS()
    91  		return ts
    92  	}
    93  }
    94  
    95  func httpsServerValidHostname(t *testing.T) serverFunc {
    96  	return func(h http.Handler) *httptest.Server {
    97  		cert, err := tls.X509KeyPair(localhostCert, localhostKey)
    98  		if err != nil {
    99  			t.Errorf("https (valid hostname): proxy_test: %v", err)
   100  		}
   101  		ts := httptest.NewUnstartedServer(h)
   102  		ts.TLS = &tls.Config{
   103  			Certificates: []tls.Certificate{cert},
   104  		}
   105  		ts.StartTLS()
   106  		return ts
   107  	}
   108  }
   109  
   110  func localhostCertPool(t *testing.T) *x509.CertPool {
   111  	localhostPool := x509.NewCertPool()
   112  
   113  	if !localhostPool.AppendCertsFromPEM(localhostCert) {
   114  		t.Errorf("error setting up localhostCert pool")
   115  	}
   116  	return localhostPool
   117  }
   118  
   119  // be sure to unset environment variable https_proxy (if exported) before testing, otherwise the testing will fail unexpectedly.
   120  func TestRoundTripAndNewConnection(t *testing.T) {
   121  	localhostPool := localhostCertPool(t)
   122  
   123  	testCases := map[string]struct {
   124  		serverFunc             func(http.Handler) *httptest.Server
   125  		proxyServerFunc        func(http.Handler) *httptest.Server
   126  		proxyAuth              *url.Userinfo
   127  		clientTLS              *tls.Config
   128  		serverConnectionHeader string
   129  		serverUpgradeHeader    string
   130  		serverStatusCode       int
   131  		shouldError            bool
   132  	}{
   133  		"no headers": {
   134  			serverFunc:             httptest.NewServer,
   135  			serverConnectionHeader: "",
   136  			serverUpgradeHeader:    "",
   137  			serverStatusCode:       http.StatusSwitchingProtocols,
   138  			shouldError:            true,
   139  		},
   140  		"no upgrade header": {
   141  			serverFunc:             httptest.NewServer,
   142  			serverConnectionHeader: "Upgrade",
   143  			serverUpgradeHeader:    "",
   144  			serverStatusCode:       http.StatusSwitchingProtocols,
   145  			shouldError:            true,
   146  		},
   147  		"no connection header": {
   148  			serverFunc:             httptest.NewServer,
   149  			serverConnectionHeader: "",
   150  			serverUpgradeHeader:    "SPDY/3.1",
   151  			serverStatusCode:       http.StatusSwitchingProtocols,
   152  			shouldError:            true,
   153  		},
   154  		"no switching protocol status code": {
   155  			serverFunc:             httptest.NewServer,
   156  			serverConnectionHeader: "Upgrade",
   157  			serverUpgradeHeader:    "SPDY/3.1",
   158  			serverStatusCode:       http.StatusForbidden,
   159  			shouldError:            true,
   160  		},
   161  		"http": {
   162  			serverFunc:             httptest.NewServer,
   163  			serverConnectionHeader: "Upgrade",
   164  			serverUpgradeHeader:    "SPDY/3.1",
   165  			serverStatusCode:       http.StatusSwitchingProtocols,
   166  			shouldError:            false,
   167  		},
   168  		"https (invalid hostname + InsecureSkipVerify)": {
   169  			serverFunc:             httpsServerInvalidHostname(t),
   170  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   171  			serverConnectionHeader: "Upgrade",
   172  			serverUpgradeHeader:    "SPDY/3.1",
   173  			serverStatusCode:       http.StatusSwitchingProtocols,
   174  			shouldError:            false,
   175  		},
   176  		"https (invalid hostname + hostname verification)": {
   177  			serverFunc:             httpsServerInvalidHostname(t),
   178  			clientTLS:              &tls.Config{InsecureSkipVerify: false},
   179  			serverConnectionHeader: "Upgrade",
   180  			serverUpgradeHeader:    "SPDY/3.1",
   181  			serverStatusCode:       http.StatusSwitchingProtocols,
   182  			shouldError:            true,
   183  		},
   184  		"https (valid hostname + RootCAs)": {
   185  			serverFunc:             httpsServerValidHostname(t),
   186  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   187  			serverConnectionHeader: "Upgrade",
   188  			serverUpgradeHeader:    "SPDY/3.1",
   189  			serverStatusCode:       http.StatusSwitchingProtocols,
   190  			shouldError:            false,
   191  		},
   192  		"proxied http->http": {
   193  			serverFunc:             httptest.NewServer,
   194  			proxyServerFunc:        httptest.NewServer,
   195  			serverConnectionHeader: "Upgrade",
   196  			serverUpgradeHeader:    "SPDY/3.1",
   197  			serverStatusCode:       http.StatusSwitchingProtocols,
   198  			shouldError:            false,
   199  		},
   200  		"proxied https (invalid hostname + InsecureSkipVerify) -> http": {
   201  			serverFunc:             httptest.NewServer,
   202  			proxyServerFunc:        httpsServerInvalidHostname(t),
   203  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   204  			serverConnectionHeader: "Upgrade",
   205  			serverUpgradeHeader:    "SPDY/3.1",
   206  			serverStatusCode:       http.StatusSwitchingProtocols,
   207  			shouldError:            false,
   208  		},
   209  		"proxied https with auth (invalid hostname + InsecureSkipVerify) -> http": {
   210  			serverFunc:             httptest.NewServer,
   211  			proxyServerFunc:        httpsServerInvalidHostname(t),
   212  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   213  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   214  			serverConnectionHeader: "Upgrade",
   215  			serverUpgradeHeader:    "SPDY/3.1",
   216  			serverStatusCode:       http.StatusSwitchingProtocols,
   217  			shouldError:            false,
   218  		},
   219  		"proxied https (invalid hostname + hostname verification) -> http": {
   220  			serverFunc:             httptest.NewServer,
   221  			proxyServerFunc:        httpsServerInvalidHostname(t),
   222  			clientTLS:              &tls.Config{InsecureSkipVerify: false},
   223  			serverConnectionHeader: "Upgrade",
   224  			serverUpgradeHeader:    "SPDY/3.1",
   225  			serverStatusCode:       http.StatusSwitchingProtocols,
   226  			shouldError:            true, // fails because the client doesn't trust the proxy
   227  		},
   228  		"proxied https (valid hostname + RootCAs) -> http": {
   229  			serverFunc:             httptest.NewServer,
   230  			proxyServerFunc:        httpsServerValidHostname(t),
   231  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   232  			serverConnectionHeader: "Upgrade",
   233  			serverUpgradeHeader:    "SPDY/3.1",
   234  			serverStatusCode:       http.StatusSwitchingProtocols,
   235  			shouldError:            false,
   236  		},
   237  		"proxied https with auth (valid hostname + RootCAs) -> http": {
   238  			serverFunc:             httptest.NewServer,
   239  			proxyServerFunc:        httpsServerValidHostname(t),
   240  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   241  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   242  			serverConnectionHeader: "Upgrade",
   243  			serverUpgradeHeader:    "SPDY/3.1",
   244  			serverStatusCode:       http.StatusSwitchingProtocols,
   245  			shouldError:            false,
   246  		},
   247  		"proxied https (invalid hostname + InsecureSkipVerify) -> https (invalid hostname)": {
   248  			serverFunc:             httpsServerInvalidHostname(t),
   249  			proxyServerFunc:        httpsServerInvalidHostname(t),
   250  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   251  			serverConnectionHeader: "Upgrade",
   252  			serverUpgradeHeader:    "SPDY/3.1",
   253  			serverStatusCode:       http.StatusSwitchingProtocols,
   254  			shouldError:            false, // works because the test proxy ignores TLS errors
   255  		},
   256  		"proxied https with auth (invalid hostname + InsecureSkipVerify) -> https (invalid hostname)": {
   257  			serverFunc:             httpsServerInvalidHostname(t),
   258  			proxyServerFunc:        httpsServerInvalidHostname(t),
   259  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   260  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   261  			serverConnectionHeader: "Upgrade",
   262  			serverUpgradeHeader:    "SPDY/3.1",
   263  			serverStatusCode:       http.StatusSwitchingProtocols,
   264  			shouldError:            false, // works because the test proxy ignores TLS errors
   265  		},
   266  		"proxied https (invalid hostname + hostname verification) -> https (invalid hostname)": {
   267  			serverFunc:             httpsServerInvalidHostname(t),
   268  			proxyServerFunc:        httpsServerInvalidHostname(t),
   269  			clientTLS:              &tls.Config{InsecureSkipVerify: false},
   270  			serverConnectionHeader: "Upgrade",
   271  			serverUpgradeHeader:    "SPDY/3.1",
   272  			serverStatusCode:       http.StatusSwitchingProtocols,
   273  			shouldError:            true, // fails because the client doesn't trust the proxy
   274  		},
   275  		"proxied https (valid hostname + RootCAs) -> https (valid hostname + RootCAs)": {
   276  			serverFunc:             httpsServerValidHostname(t),
   277  			proxyServerFunc:        httpsServerValidHostname(t),
   278  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   279  			serverConnectionHeader: "Upgrade",
   280  			serverUpgradeHeader:    "SPDY/3.1",
   281  			serverStatusCode:       http.StatusSwitchingProtocols,
   282  			shouldError:            false,
   283  		},
   284  		"proxied https with auth (valid hostname + RootCAs) -> https (valid hostname + RootCAs)": {
   285  			serverFunc:             httpsServerValidHostname(t),
   286  			proxyServerFunc:        httpsServerValidHostname(t),
   287  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   288  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   289  			serverConnectionHeader: "Upgrade",
   290  			serverUpgradeHeader:    "SPDY/3.1",
   291  			serverStatusCode:       http.StatusSwitchingProtocols,
   292  			shouldError:            false,
   293  		},
   294  	}
   295  
   296  	for k, testCase := range testCases {
   297  		t.Run(k, func(t *testing.T) {
   298  			server := testCase.serverFunc(serverHandler(
   299  				t, serverHandlerConfig{
   300  					shouldError:      testCase.shouldError,
   301  					statusCode:       testCase.serverStatusCode,
   302  					connectionHeader: testCase.serverConnectionHeader,
   303  					upgradeHeader:    testCase.serverUpgradeHeader,
   304  				},
   305  			))
   306  			defer server.Close()
   307  
   308  			serverURL, err := url.Parse(server.URL)
   309  			if err != nil {
   310  				t.Fatalf("error creating request: %s", err)
   311  			}
   312  			req, err := http.NewRequest("GET", server.URL, nil)
   313  			if err != nil {
   314  				t.Fatalf("error creating request: %s", err)
   315  			}
   316  
   317  			spdyTransport := NewRoundTripper(testCase.clientTLS)
   318  
   319  			var proxierCalled bool
   320  			var proxyCalledWithHost string
   321  			var proxyCalledWithAuth bool
   322  			var proxyCalledWithAuthHeader string
   323  			if testCase.proxyServerFunc != nil {
   324  				proxyHandler := goproxy.NewProxyHttpServer()
   325  
   326  				proxyHandler.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
   327  					proxyCalledWithHost = host
   328  
   329  					proxyAuthHeaderName := "Proxy-Authorization"
   330  					_, proxyCalledWithAuth = ctx.Req.Header[proxyAuthHeaderName]
   331  					proxyCalledWithAuthHeader = ctx.Req.Header.Get(proxyAuthHeaderName)
   332  					return goproxy.OkConnect, host
   333  				})
   334  
   335  				proxy := testCase.proxyServerFunc(proxyHandler)
   336  
   337  				spdyTransport.proxier = func(proxierReq *http.Request) (*url.URL, error) {
   338  					proxierCalled = true
   339  					proxyURL, err := url.Parse(proxy.URL)
   340  					if err != nil {
   341  						return nil, err
   342  					}
   343  					proxyURL.User = testCase.proxyAuth
   344  					return proxyURL, nil
   345  				}
   346  				defer proxy.Close()
   347  			}
   348  
   349  			client := &http.Client{Transport: spdyTransport}
   350  
   351  			resp, err := client.Do(req)
   352  			var conn httpstream.Connection
   353  			if err == nil {
   354  				conn, err = spdyTransport.NewConnection(resp)
   355  			}
   356  			haveErr := err != nil
   357  			if e, a := testCase.shouldError, haveErr; e != a {
   358  				t.Fatalf("shouldError=%t, got %t: %v", e, a, err)
   359  			}
   360  			if testCase.shouldError {
   361  				return
   362  			}
   363  			defer conn.Close()
   364  
   365  			if resp.StatusCode != http.StatusSwitchingProtocols {
   366  				t.Fatalf("expected http 101 switching protocols, got %d", resp.StatusCode)
   367  			}
   368  
   369  			stream, err := conn.CreateStream(http.Header{})
   370  			if err != nil {
   371  				t.Fatalf("error creating client stream: %s", err)
   372  			}
   373  
   374  			n, err := stream.Write([]byte("hello"))
   375  			if err != nil {
   376  				t.Fatalf("error writing to stream: %s", err)
   377  			}
   378  			if n != 5 {
   379  				t.Fatalf("expected to write 5 bytes, but actually wrote %d", n)
   380  			}
   381  
   382  			b := make([]byte, 5)
   383  			n, err = stream.Read(b)
   384  			if err != nil {
   385  				t.Fatalf("error reading from stream: %s", err)
   386  			}
   387  			if n != 5 {
   388  				t.Fatalf("expected to read 5 bytes, but actually read %d", n)
   389  			}
   390  			if e, a := "hello", string(b[0:n]); e != a {
   391  				t.Fatalf("expected '%s', got '%s'", e, a)
   392  			}
   393  
   394  			if testCase.proxyServerFunc != nil {
   395  				if !proxierCalled {
   396  					t.Fatal("expected to use a proxy but proxier in SpdyRoundTripper wasn't called")
   397  				}
   398  				if proxyCalledWithHost != serverURL.Host {
   399  					t.Fatalf("expected to see a call to the proxy for backend %q, got %q", serverURL.Host, proxyCalledWithHost)
   400  				}
   401  			}
   402  
   403  			var expectedProxyAuth string
   404  			if testCase.proxyAuth != nil {
   405  				encodedCredentials := base64.StdEncoding.EncodeToString([]byte(testCase.proxyAuth.String()))
   406  				expectedProxyAuth = "Basic " + encodedCredentials
   407  			}
   408  			if len(expectedProxyAuth) == 0 && proxyCalledWithAuth {
   409  				t.Fatalf("proxy authorization unexpected, got %q", proxyCalledWithAuthHeader)
   410  			}
   411  			if proxyCalledWithAuthHeader != expectedProxyAuth {
   412  				t.Fatalf("expected to see a call to the proxy with credentials %q, got %q", testCase.proxyAuth, proxyCalledWithAuthHeader)
   413  			}
   414  
   415  		})
   416  	}
   417  }
   418  
   419  type Interceptor struct {
   420  	Authorization       socks5.AuthContext
   421  	proxyCalledWithHost *string
   422  }
   423  
   424  func (i *Interceptor) GetAuthContext() (int, map[string]string) {
   425  	return int(i.Authorization.Method), i.Authorization.Payload
   426  }
   427  
   428  func (i *Interceptor) Rewrite(ctx context.Context, req *socks5.Request) (context.Context, *socks5.AddrSpec) {
   429  	*i.proxyCalledWithHost = req.DestAddr.Address()
   430  	i.Authorization = socks5.AuthContext(*req.AuthContext)
   431  	return ctx, req.DestAddr
   432  }
   433  
   434  // be sure to unset environment variable https_proxy (if exported) before testing, otherwise the testing will fail unexpectedly.
   435  func TestRoundTripSocks5AndNewConnection(t *testing.T) {
   436  	localhostPool := localhostCertPool(t)
   437  
   438  	socks5Server := func(creds *socks5.StaticCredentials, interceptor *Interceptor) *socks5.Server {
   439  		var conf *socks5.Config
   440  		if creds != nil {
   441  			authenticator := socks5.UserPassAuthenticator{Credentials: creds}
   442  			conf = &socks5.Config{
   443  				AuthMethods: []socks5.Authenticator{authenticator},
   444  				Rewriter:    interceptor,
   445  			}
   446  		} else {
   447  			conf = &socks5.Config{Rewriter: interceptor}
   448  		}
   449  
   450  		ts, err := socks5.New(conf)
   451  		if err != nil {
   452  			t.Errorf("failed to create sock5 server: %v", err)
   453  		}
   454  		return ts
   455  	}
   456  
   457  	testCases := map[string]struct {
   458  		clientTLS              *tls.Config
   459  		proxyAuth              *url.Userinfo
   460  		serverConnectionHeader string
   461  		serverFunc             serverFunc
   462  		serverStatusCode       int
   463  		serverUpgradeHeader    string
   464  		shouldError            bool
   465  	}{
   466  		"proxied without auth -> http": {
   467  			serverFunc:             httptest.NewServer,
   468  			serverConnectionHeader: "Upgrade",
   469  			serverStatusCode:       http.StatusSwitchingProtocols,
   470  			serverUpgradeHeader:    "SPDY/3.1",
   471  			shouldError:            false,
   472  		},
   473  		"proxied with invalid auth -> http": {
   474  			serverFunc:             httptest.NewServer,
   475  			proxyAuth:              url.UserPassword("invalid", "auth"),
   476  			serverConnectionHeader: "Upgrade",
   477  			serverStatusCode:       http.StatusSwitchingProtocols,
   478  			serverUpgradeHeader:    "SPDY/3.1",
   479  			shouldError:            true,
   480  		},
   481  		"proxied with valid auth -> http": {
   482  			serverFunc:             httptest.NewServer,
   483  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   484  			serverConnectionHeader: "Upgrade",
   485  			serverStatusCode:       http.StatusSwitchingProtocols,
   486  			serverUpgradeHeader:    "SPDY/3.1",
   487  			shouldError:            false,
   488  		},
   489  		"proxied with valid auth -> https (invalid hostname + InsecureSkipVerify)": {
   490  			serverFunc:             httpsServerInvalidHostname(t),
   491  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   492  			clientTLS:              &tls.Config{InsecureSkipVerify: true},
   493  			serverConnectionHeader: "Upgrade",
   494  			serverUpgradeHeader:    "SPDY/3.1",
   495  			serverStatusCode:       http.StatusSwitchingProtocols,
   496  			shouldError:            false,
   497  		},
   498  		"proxied with valid auth -> https (invalid hostname + hostname verification)": {
   499  			serverFunc:             httpsServerInvalidHostname(t),
   500  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   501  			clientTLS:              &tls.Config{InsecureSkipVerify: false},
   502  			serverConnectionHeader: "Upgrade",
   503  			serverUpgradeHeader:    "SPDY/3.1",
   504  			serverStatusCode:       http.StatusSwitchingProtocols,
   505  			shouldError:            true,
   506  		},
   507  		"proxied with valid auth -> https (valid hostname + RootCAs)": {
   508  			serverFunc:             httpsServerValidHostname(t),
   509  			proxyAuth:              url.UserPassword("proxyuser", "proxypasswd"),
   510  			clientTLS:              &tls.Config{RootCAs: localhostPool},
   511  			serverConnectionHeader: "Upgrade",
   512  			serverUpgradeHeader:    "SPDY/3.1",
   513  			serverStatusCode:       http.StatusSwitchingProtocols,
   514  			shouldError:            false,
   515  		},
   516  	}
   517  
   518  	for name, testCase := range testCases {
   519  		t.Run(name, func(t *testing.T) {
   520  			server := testCase.serverFunc(serverHandler(
   521  				t, serverHandlerConfig{
   522  					shouldError:      testCase.shouldError,
   523  					statusCode:       testCase.serverStatusCode,
   524  					connectionHeader: testCase.serverConnectionHeader,
   525  					upgradeHeader:    testCase.serverUpgradeHeader,
   526  				},
   527  			))
   528  			defer server.Close()
   529  
   530  			req, err := http.NewRequest("GET", server.URL, nil)
   531  			if err != nil {
   532  				t.Fatalf("error creating request: %s", err)
   533  			}
   534  
   535  			spdyTransport := NewRoundTripper(testCase.clientTLS)
   536  			var proxierCalled bool
   537  			var proxyCalledWithHost string
   538  
   539  			interceptor := &Interceptor{proxyCalledWithHost: &proxyCalledWithHost}
   540  
   541  			proxyHandler := socks5Server(nil, interceptor)
   542  
   543  			if testCase.proxyAuth != nil {
   544  				proxyHandler = socks5Server(&socks5.StaticCredentials{
   545  					"proxyuser": "proxypasswd", // Socks5 server static credentials when client authentication is expected
   546  				}, interceptor)
   547  			}
   548  
   549  			closed := make(chan struct{})
   550  			isClosed := func() bool {
   551  				select {
   552  				case <-closed:
   553  					return true
   554  				default:
   555  					return false
   556  				}
   557  			}
   558  
   559  			l, err := net.Listen("tcp", "127.0.0.1:0")
   560  			if err != nil {
   561  				t.Fatalf("socks5Server: proxy_test: Listen: %v", err)
   562  			}
   563  			defer l.Close()
   564  
   565  			go func(shoulderror bool) {
   566  				conn, err := l.Accept()
   567  				if err != nil {
   568  					if isClosed() {
   569  						return
   570  					}
   571  
   572  					t.Errorf("error accepting connection: %s", err)
   573  				}
   574  
   575  				if err := proxyHandler.ServeConn(conn); err != nil && !shoulderror {
   576  					// If the connection request is closed before the channel is closed
   577  					// the test will fail with a ServeConn error. Since the test only return
   578  					// early if expects shouldError=true, the channel is closed at the end of
   579  					// the test, just before all the deferred connections Close() are executed.
   580  					if isClosed() {
   581  						return
   582  					}
   583  
   584  					t.Errorf("ServeConn error: %s", err)
   585  				}
   586  			}(testCase.shouldError)
   587  			spdyTransport.proxier = func(proxierReq *http.Request) (*url.URL, error) {
   588  				proxierCalled = true
   589  				return &url.URL{
   590  					Scheme: "socks5",
   591  					Host:   net.JoinHostPort("127.0.0.1", strconv.Itoa(l.Addr().(*net.TCPAddr).Port)),
   592  					User:   testCase.proxyAuth,
   593  				}, nil
   594  			}
   595  
   596  			client := &http.Client{Transport: spdyTransport}
   597  
   598  			resp, err := client.Do(req)
   599  			haveErr := err != nil
   600  			if e, a := testCase.shouldError, haveErr; e != a {
   601  				t.Fatalf("shouldError=%t, got %t: %v", e, a, err)
   602  			}
   603  			if testCase.shouldError {
   604  				return
   605  			}
   606  
   607  			conn, err := spdyTransport.NewConnection(resp)
   608  			haveErr = err != nil
   609  			if e, a := testCase.shouldError, haveErr; e != a {
   610  				t.Fatalf("shouldError=%t, got %t: %v", e, a, err)
   611  			}
   612  			if testCase.shouldError {
   613  				return
   614  			}
   615  
   616  			defer conn.Close()
   617  
   618  			if resp.StatusCode != http.StatusSwitchingProtocols {
   619  				t.Fatalf("expected http 101 switching protocols, got %d", resp.StatusCode)
   620  			}
   621  
   622  			stream, err := conn.CreateStream(http.Header{})
   623  			if err != nil {
   624  				t.Fatalf("error creating client stream: %s", err)
   625  			}
   626  
   627  			n, err := stream.Write([]byte("hello"))
   628  			if err != nil {
   629  				t.Fatalf("error writing to stream: %s", err)
   630  			}
   631  			if n != 5 {
   632  				t.Fatalf("expected to write 5 bytes, but actually wrote %d", n)
   633  			}
   634  
   635  			b := make([]byte, 5)
   636  			n, err = stream.Read(b)
   637  			if err != nil {
   638  				t.Fatalf("error reading from stream: %s", err)
   639  			}
   640  			if n != 5 {
   641  				t.Fatalf("expected to read 5 bytes, but actually read %d", n)
   642  			}
   643  			if e, a := "hello", string(b[0:n]); e != a {
   644  				t.Fatalf("expected '%s', got '%s'", e, a)
   645  			}
   646  
   647  			if !proxierCalled {
   648  				t.Fatal("xpected to use a proxy but proxier in SpdyRoundTripper wasn't called")
   649  			}
   650  
   651  			serverURL, err := url.Parse(server.URL)
   652  			if err != nil {
   653  				t.Fatalf("error creating request: %s", err)
   654  			}
   655  			if proxyCalledWithHost != serverURL.Host {
   656  				t.Fatalf("expected to see a call to the proxy for backend %q, got %q", serverURL.Host, proxyCalledWithHost)
   657  			}
   658  
   659  			authMethod, authUser := interceptor.GetAuthContext()
   660  
   661  			if testCase.proxyAuth != nil {
   662  				expectedSocks5AuthMethod := 2
   663  				expectedSocks5AuthUser := "proxyuser"
   664  
   665  				if expectedSocks5AuthMethod != authMethod {
   666  					t.Fatalf("socks5 Proxy authorization unexpected, got %d, expected %d", authMethod, expectedSocks5AuthMethod)
   667  				}
   668  
   669  				if expectedSocks5AuthUser != authUser["Username"] {
   670  					t.Fatalf("socks5 Proxy authorization user unexpected, got %q, expected %q", authUser["Username"], expectedSocks5AuthUser)
   671  				}
   672  			} else {
   673  				if authMethod != 0 {
   674  					t.Fatalf("proxy authentication method unexpected, got %d", authMethod)
   675  				}
   676  				if len(authUser) != 0 {
   677  					t.Fatalf("unexpected proxy user: %v", authUser)
   678  				}
   679  			}
   680  
   681  			// The channel must be closed before any of the connections are closed
   682  			close(closed)
   683  		})
   684  	}
   685  }
   686  
   687  func TestRoundTripPassesContextToDialer(t *testing.T) {
   688  	urls := []string{"http://127.0.0.1:1233/", "https://127.0.0.1:1233/"}
   689  	for _, u := range urls {
   690  		t.Run(u, func(t *testing.T) {
   691  			ctx, cancel := context.WithCancel(context.Background())
   692  			cancel()
   693  			req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
   694  			require.NoError(t, err)
   695  			spdyTransport := NewRoundTripper(&tls.Config{})
   696  			_, err = spdyTransport.Dial(req)
   697  			assert.EqualError(t, err, "dial tcp 127.0.0.1:1233: operation was canceled")
   698  		})
   699  	}
   700  }
   701  
   702  // exampleCert was generated from crypto/tls/generate_cert.go with the following command:
   703  //
   704  //	go run generate_cert.go  --rsa-bits 2048 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
   705  var exampleCert = []byte(`-----BEGIN CERTIFICATE-----
   706  MIIDADCCAeigAwIBAgIQVHG3Fn9SdWayyLOZKCW1vzANBgkqhkiG9w0BAQsFADAS
   707  MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
   708  MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
   709  MIIBCgKCAQEArTCu9fiIclNgDdWHphewM+JW55dCb5yYGlJgCBvwbOx547M9p+tn
   710  zm9QOhsdZDHDZsG9tqnWxE2Nc1HpIJyOlfYsOoonpEoG/Ep6nnK91ngj0bn/JlNy
   711  +i/bwU4r97MOukvnOIQez9/D9jAJaOX2+b8/d4lRz9BsqiwJyg+ynZ5tVVYj7aMi
   712  vXnd6HOnJmtqutOtr3beucJnkd6XbwRkLUcAYATT+ZihOWRbTuKqhCg6zGkJOoUG
   713  f8sX61JjoilxiURA//ftGVbdTCU3DrmGmardp5NNOHbumMYU8Vhmqgx1Bqxb+9he
   714  7G42uW5YWYK/GqJzgVPjjlB2dOGj9KrEWQIDAQABo1AwTjAOBgNVHQ8BAf8EBAMC
   715  AqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAWBgNVHREE
   716  DzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAig4AIi9xWs1+pLES
   717  eeGGdSDoclplFpcbXANnsYYFyLf+8pcWgVi2bOmb2gXMbHFkB07MA82wRJAUTaA+
   718  2iNXVQMhPCoA7J6ADUbww9doJX2S9HGyArhiV/MhHtE8txzMn2EKNLdhhk3N9rmV
   719  x/qRbWAY1U2z4BpdrAR87Fe81Nlj7h45csW9K+eS+NgXipiNTIfEShKgCFM8EdxL
   720  1WXg7r9AvYV3TNDPWTjLsm1rQzzZQ7Uvcf6deWiNodZd8MOT/BFLclDPTK6cF2Hr
   721  UU4dq6G4kCwMSxWE4cM3HlZ4u1dyIt47VbkP0rtvkBCXx36y+NXYA5lzntchNFZP
   722  uvEQdw==
   723  -----END CERTIFICATE-----`)
   724  
   725  var exampleKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
   726  MIIEpQIBAAKCAQEArTCu9fiIclNgDdWHphewM+JW55dCb5yYGlJgCBvwbOx547M9
   727  p+tnzm9QOhsdZDHDZsG9tqnWxE2Nc1HpIJyOlfYsOoonpEoG/Ep6nnK91ngj0bn/
   728  JlNy+i/bwU4r97MOukvnOIQez9/D9jAJaOX2+b8/d4lRz9BsqiwJyg+ynZ5tVVYj
   729  7aMivXnd6HOnJmtqutOtr3beucJnkd6XbwRkLUcAYATT+ZihOWRbTuKqhCg6zGkJ
   730  OoUGf8sX61JjoilxiURA//ftGVbdTCU3DrmGmardp5NNOHbumMYU8Vhmqgx1Bqxb
   731  +9he7G42uW5YWYK/GqJzgVPjjlB2dOGj9KrEWQIDAQABAoIBAQClt4CiYaaF5ltx
   732  wVDjz6TNcJUBUs3CKE+uWAYFnF5Ii1nyU876Pxj8Aaz9fHZ6Kde0GkwiXY7gFOj1
   733  YHo2tzcELSKS/SEDZcYbYFTGCjq13g1AH74R+SV6WZLn+5m8kPvVrM1ZWap188H5
   734  bmuCkRDqVmIvShkbRW7EwhC35J9fiuW3majC/sjmsxtxyP6geWmu4f5/Ttqahcdb
   735  osPZIgIIPzqAkNtkLTi7+meHYI9wlrGhL7XZTwnJ1Oc/Y67zzmbthLYB5YFSLUew
   736  rXT58jtSjX4gbiQyheBSrWxW08QE4qYg6jJlAdffHhWv72hJW2MCXhuXp8gJs/Do
   737  XLRHGwSBAoGBAMdNtsbe4yae/QeHUPGxNW0ipa0yoTF6i+VYoxvqiRMzDM3+3L8k
   738  dgI1rr4330SivqDahMA/odWtM/9rVwJI2B2QhZLMHA0n9ytH007OO9TghgVB12nN
   739  xosRYBpKdHXyyvV/MUZl7Jux6zKIzRDWOkF95VVYPcAaxJqd1E5/jJ6JAoGBAN51
   740  QrebA1w/jfydeqQTz1sK01sbO4HYj4qGfo/JarVqGEkm1azeBBPPRnHz3jNKnCkM
   741  S4PpqRDased3NIcViXlAgoqPqivZ8mQa/Rb146l7WaTErASHsZ023OGrxsr/Ed6N
   742  P3GrmvxVJjebaFNaQ9sP80dLkpgeas0t2TY8iQNRAoGATOcnx8TpUVW3vNfx29DN
   743  FLdxxkrq9/SZVn3FMlhlXAsuva3B799ZybB9JNjaRdmmRNsMrkHfaFvU3JHGmRMS
   744  kRXa9LHdgRYSwZiNaLMbUyDvlce6HxFPswmZU4u3NGvi9KeHk+pwSgN1BaLTvdNr
   745  1ymE/FF4QlAR3LdZ3JBK6kECgYEA0wW4/CJ31ZIURoW8SNjh4iMqy0nR8SJVR7q9
   746  Y/hU2TKDRyEnoIwaohAFayNCrLUh3W5kVAXa8roB+OgDVAECH5sqOfZ+HorofD19
   747  x8II7ESujLZj1whBXDkm3ovsT7QWZ17lyBZZNvQvBKDPHgKKS8udowv1S4fPGENd
   748  wS07a4ECgYEAwLSbmMIVJme0jFjsp5d1wOGA2Qi2ZwGIAVlsbnJtygrU/hSBfnu8
   749  VfyJSCgg3fPe7kChWKlfcOebVKSb68LKRsz1Lz1KdbY0HOJFp/cT4lKmDAlRY9gq
   750  LB4rdf46lV0mUkvd2/oofIbTrzukjQSnyfLawb/2uJGV1IkTcZcn9CI=
   751  -----END RSA PRIVATE KEY-----`)
   752  
   753  // localhostCert was generated from crypto/tls/generate_cert.go with the following command:
   754  //
   755  //	go run generate_cert.go  --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
   756  var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
   757  MIIDGTCCAgGgAwIBAgIRALL5AZcefF4kkYV1SEG6YrMwDQYJKoZIhvcNAQELBQAw
   758  EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2
   759  MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
   760  ADCCAQoCggEBALQ/FHcyVwdFHxARbbD2KBtDUT7Eni+8ioNdjtGcmtXqBv45EC1C
   761  JOqqGJTroFGJ6Q9kQIZ9FqH5IJR2fOOJD9kOTueG4Vt1JY1rj1Kbpjefu8XleZ5L
   762  SBwIWVnN/lEsEbuKmj7N2gLt5AH3zMZiBI1mg1u9Z5ZZHYbCiTpBrwsq6cTlvR9g
   763  dyo1YkM5hRESCzsrL0aUByoo0qRMD8ZsgANJwgsiO0/M6idbxDwv1BnGwGmRYvOE
   764  Hxpy3v0Jg7GJYrvnpnifJTs4nw91N5X9pXxR7FFzi/6HTYDWRljvTb0w6XciKYAz
   765  bWZ0+cJr5F7wB7ovlbm7HrQIR7z7EIIu2d8CAwEAAaNoMGYwDgYDVR0PAQH/BAQD
   766  AgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wLgYDVR0R
   767  BCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZI
   768  hvcNAQELBQADggEBAFPPWopNEJtIA2VFAQcqN6uJK+JVFOnjGRoCrM6Xgzdm0wxY
   769  XCGjsxY5dl+V7KzdGqu858rCaq5osEBqypBpYAnS9C38VyCDA1vPS1PsN8SYv48z
   770  DyBwj+7R2qar0ADBhnhWxvYO9M72lN/wuCqFKYMeFSnJdQLv3AsrrHe9lYqOa36s
   771  8wxSwVTFTYXBzljPEnSaaJMPqFD8JXaZK1ryJPkO5OsCNQNGtatNiWAf3DcmwHAT
   772  MGYMzP0u4nw47aRz9shB8w+taPKHx2BVwE1m/yp3nHVioOjXqA1fwRQVGclCJSH1
   773  D2iq3hWVHRENgjTjANBPICLo9AZ4JfN6PH19mnU=
   774  -----END CERTIFICATE-----`)
   775  
   776  // localhostKey is the private key for localhostCert.
   777  var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
   778  MIIEogIBAAKCAQEAtD8UdzJXB0UfEBFtsPYoG0NRPsSeL7yKg12O0Zya1eoG/jkQ
   779  LUIk6qoYlOugUYnpD2RAhn0WofkglHZ844kP2Q5O54bhW3UljWuPUpumN5+7xeV5
   780  nktIHAhZWc3+USwRu4qaPs3aAu3kAffMxmIEjWaDW71nllkdhsKJOkGvCyrpxOW9
   781  H2B3KjViQzmFERILOysvRpQHKijSpEwPxmyAA0nCCyI7T8zqJ1vEPC/UGcbAaZFi
   782  84QfGnLe/QmDsYliu+emeJ8lOzifD3U3lf2lfFHsUXOL/odNgNZGWO9NvTDpdyIp
   783  gDNtZnT5wmvkXvAHui+VubsetAhHvPsQgi7Z3wIDAQABAoIBAGmw93IxjYCQ0ncc
   784  kSKMJNZfsdtJdaxuNRZ0nNNirhQzR2h403iGaZlEpmdkhzxozsWcto1l+gh+SdFk
   785  bTUK4MUZM8FlgO2dEqkLYh5BcMT7ICMZvSfJ4v21E5eqR68XVUqQKoQbNvQyxFk3
   786  EddeEGdNrkb0GDK8DKlBlzAW5ep4gjG85wSTjR+J+muUv3R0BgLBFSuQnIDM/IMB
   787  LWqsja/QbtB7yppe7jL5u8UCFdZG8BBKT9fcvFIu5PRLO3MO0uOI7LTc8+W1Xm23
   788  uv+j3SY0+v+6POjK0UlJFFi/wkSPTFIfrQO1qFBkTDQHhQ6q/7GnILYYOiGbIRg2
   789  NNuP52ECgYEAzXEoy50wSYh8xfFaBuxbm3ruuG2W49jgop7ZfoFrPWwOQKAZS441
   790  VIwV4+e5IcA6KkuYbtGSdTYqK1SMkgnUyD/VevwAqH5TJoEIGu0pDuKGwVuwqioZ
   791  frCIAV5GllKyUJ55VZNbRr2vY2fCsWbaCSCHETn6C16DNuTCe5C0JBECgYEA4JqY
   792  5GpNbMG8fOt4H7hU0Fbm2yd6SHJcQ3/9iimef7xG6ajxsYrIhg1ft+3IPHMjVI0+
   793  9brwHDnWg4bOOx/VO4VJBt6Dm/F33bndnZRkuIjfSNpLM51P+EnRdaFVHOJHwKqx
   794  uF69kihifCAG7YATgCveeXImzBUSyZUz9UrETu8CgYARNBimdFNG1RcdvEg9rC0/
   795  p9u1tfecvNySwZqU7WF9kz7eSonTueTdX521qAHowaAdSpdJMGODTTXaywm6cPhQ
   796  jIfj9JZZhbqQzt1O4+08Qdvm9TamCUB5S28YLjza+bHU7nBaqixKkDfPqzCyilpX
   797  yVGGL8SwjwmN3zop/sQXAQKBgC0JMsESQ6YcDsRpnrOVjYQc+LtW5iEitTdfsaID
   798  iGGKihmOI7B66IxgoCHMTws39wycKdSyADVYr5e97xpR3rrJlgQHmBIrz+Iow7Q2
   799  LiAGaec8xjl6QK/DdXmFuQBKqyKJ14rljFODP4QuE9WJid94bGqjpf3j99ltznZP
   800  4J8HAoGAJb4eb4lu4UGwifDzqfAPzLGCoi0fE1/hSx34lfuLcc1G+LEu9YDKoOVJ
   801  9suOh0b5K/bfEy9KrVMBBriduvdaERSD8S3pkIQaitIz0B029AbE4FLFf9lKQpP2
   802  KR8NJEkK99Vh/tew6jAMll70xFrE7aF8VLXJVE7w4sQzuvHxl9Q=
   803  -----END RSA PRIVATE KEY-----
   804  `)