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