k8s.io/apimachinery@v0.29.2/pkg/util/proxy/upgradeaware_test.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 proxy 18 19 import ( 20 "bufio" 21 "bytes" 22 "compress/gzip" 23 "context" 24 "crypto/tls" 25 "crypto/x509" 26 "errors" 27 "fmt" 28 "io" 29 "net" 30 "net/http" 31 "net/http/httptest" 32 "net/http/httputil" 33 "net/url" 34 "reflect" 35 "strconv" 36 "strings" 37 "testing" 38 "time" 39 40 "github.com/stretchr/testify/assert" 41 "github.com/stretchr/testify/require" 42 43 "golang.org/x/net/websocket" 44 45 "k8s.io/apimachinery/pkg/util/httpstream" 46 utilnet "k8s.io/apimachinery/pkg/util/net" 47 ) 48 49 const fakeStatusCode = 567 50 51 type fakeResponder struct { 52 t *testing.T 53 called bool 54 err error 55 // called chan error 56 w http.ResponseWriter 57 } 58 59 func (r *fakeResponder) Error(w http.ResponseWriter, req *http.Request, err error) { 60 if r.called { 61 r.t.Errorf("Error responder called again!\nprevious error: %v\nnew error: %v", r.err, err) 62 } 63 64 w.WriteHeader(fakeStatusCode) 65 _, writeErr := w.Write([]byte(err.Error())) 66 assert.NoError(r.t, writeErr) 67 68 r.called = true 69 r.err = err 70 } 71 72 type fakeConn struct { 73 err error // The error to return when io is performed over the connection. 74 } 75 76 func (f *fakeConn) Read([]byte) (int, error) { return 0, f.err } 77 func (f *fakeConn) Write([]byte) (int, error) { return 0, f.err } 78 func (f *fakeConn) Close() error { return nil } 79 func (fakeConn) LocalAddr() net.Addr { return nil } 80 func (fakeConn) RemoteAddr() net.Addr { return nil } 81 func (fakeConn) SetDeadline(t time.Time) error { return nil } 82 func (fakeConn) SetReadDeadline(t time.Time) error { return nil } 83 func (fakeConn) SetWriteDeadline(t time.Time) error { return nil } 84 85 type SimpleBackendHandler struct { 86 requestURL url.URL 87 requestHost string 88 requestHeader http.Header 89 requestBody []byte 90 requestMethod string 91 responseBody string 92 responseHeader map[string]string 93 t *testing.T 94 } 95 96 func (s *SimpleBackendHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 97 s.requestURL = *req.URL 98 s.requestHost = req.Host 99 s.requestHeader = req.Header 100 s.requestMethod = req.Method 101 var err error 102 s.requestBody, err = io.ReadAll(req.Body) 103 if err != nil { 104 s.t.Errorf("Unexpected error: %v", err) 105 return 106 } 107 108 if s.responseHeader != nil { 109 for k, v := range s.responseHeader { 110 w.Header().Add(k, v) 111 } 112 } 113 w.Write([]byte(s.responseBody)) 114 } 115 116 func validateParameters(t *testing.T, name string, actual url.Values, expected map[string]string) { 117 for k, v := range expected { 118 actualValue, ok := actual[k] 119 if !ok { 120 t.Errorf("%s: Expected parameter %s not received", name, k) 121 continue 122 } 123 if actualValue[0] != v { 124 t.Errorf("%s: Parameter %s values don't match. Actual: %#v, Expected: %s", 125 name, k, actualValue, v) 126 } 127 } 128 } 129 130 func validateHeaders(t *testing.T, name string, actual http.Header, expected map[string]string, notExpected []string) { 131 for k, v := range expected { 132 actualValue, ok := actual[k] 133 if !ok { 134 t.Errorf("%s: Expected header %s not received", name, k) 135 continue 136 } 137 if actualValue[0] != v { 138 t.Errorf("%s: Header %s values don't match. Actual: %s, Expected: %s", 139 name, k, actualValue, v) 140 } 141 } 142 if notExpected == nil { 143 return 144 } 145 for _, h := range notExpected { 146 if _, present := actual[h]; present { 147 t.Errorf("%s: unexpected header: %s", name, h) 148 } 149 } 150 } 151 152 func TestServeHTTP(t *testing.T) { 153 tests := []struct { 154 name string 155 method string 156 requestPath string 157 expectedPath string 158 requestBody string 159 requestParams map[string]string 160 requestHeader map[string]string 161 responseHeader map[string]string 162 expectedRespHeader map[string]string 163 notExpectedRespHeader []string 164 upgradeRequired bool 165 appendLocationPath bool 166 expectError func(err error) bool 167 useLocationHost bool 168 }{ 169 { 170 name: "root path, simple get", 171 method: "GET", 172 requestPath: "/", 173 expectedPath: "/", 174 }, 175 { 176 name: "no upgrade header sent", 177 method: "GET", 178 requestPath: "/", 179 upgradeRequired: true, 180 expectError: func(err error) bool { 181 return err != nil && strings.Contains(err.Error(), "Upgrade request required") 182 }, 183 }, 184 { 185 name: "simple path, get", 186 method: "GET", 187 requestPath: "/path/to/test", 188 expectedPath: "/path/to/test", 189 }, 190 { 191 name: "request params", 192 method: "POST", 193 requestPath: "/some/path/", 194 expectedPath: "/some/path/", 195 requestParams: map[string]string{"param1": "value/1", "param2": "value%2"}, 196 requestBody: "test request body", 197 }, 198 { 199 name: "request headers", 200 method: "PUT", 201 requestPath: "/some/path", 202 expectedPath: "/some/path", 203 requestHeader: map[string]string{"Header1": "value1", "Header2": "value2"}, 204 }, 205 { 206 name: "empty path - slash should be added", 207 method: "GET", 208 requestPath: "", 209 expectedPath: "/", 210 }, 211 { 212 name: "remove CORS headers", 213 method: "GET", 214 requestPath: "/some/path", 215 expectedPath: "/some/path", 216 responseHeader: map[string]string{ 217 "Header1": "value1", 218 "Access-Control-Allow-Origin": "some.server", 219 "Access-Control-Allow-Methods": "GET"}, 220 expectedRespHeader: map[string]string{ 221 "Header1": "value1", 222 }, 223 notExpectedRespHeader: []string{ 224 "Access-Control-Allow-Origin", 225 "Access-Control-Allow-Methods", 226 }, 227 }, 228 { 229 name: "use location host", 230 method: "GET", 231 requestPath: "/some/path", 232 expectedPath: "/some/path", 233 useLocationHost: true, 234 }, 235 { 236 name: "use location host - invalid upgrade", 237 method: "GET", 238 upgradeRequired: true, 239 requestHeader: map[string]string{ 240 httpstream.HeaderConnection: httpstream.HeaderUpgrade, 241 }, 242 expectError: func(err error) bool { 243 return err != nil && strings.Contains(err.Error(), "invalid upgrade response: status code 200") 244 }, 245 requestPath: "/some/path", 246 expectedPath: "/some/path", 247 useLocationHost: true, 248 }, 249 { 250 name: "append server path to request path", 251 method: "GET", 252 requestPath: "/base", 253 expectedPath: "/base/base", 254 appendLocationPath: true, 255 }, 256 { 257 name: "append server path to request path with ending slash", 258 method: "GET", 259 requestPath: "/base/", 260 expectedPath: "/base/base/", 261 appendLocationPath: true, 262 }, 263 { 264 name: "don't append server path to request path", 265 method: "GET", 266 requestPath: "/base", 267 expectedPath: "/base", 268 appendLocationPath: false, 269 }, 270 } 271 272 for i, test := range tests { 273 func() { 274 backendResponse := "<html><head></head><body><a href=\"/test/path\">Hello</a></body></html>" 275 backendResponseHeader := test.responseHeader 276 // Test a simple header if not specified in the test 277 if backendResponseHeader == nil && test.expectedRespHeader == nil { 278 backendResponseHeader = map[string]string{"Content-Type": "text/html"} 279 test.expectedRespHeader = map[string]string{"Content-Type": "text/html"} 280 } 281 backendHandler := &SimpleBackendHandler{ 282 responseBody: backendResponse, 283 responseHeader: backendResponseHeader, 284 } 285 backendServer := httptest.NewServer(backendHandler) 286 defer backendServer.Close() 287 288 responder := &fakeResponder{t: t} 289 backendURL, _ := url.Parse(backendServer.URL) 290 backendURL.Path = test.requestPath 291 proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, test.upgradeRequired, responder) 292 proxyHandler.UseLocationHost = test.useLocationHost 293 proxyHandler.AppendLocationPath = test.appendLocationPath 294 proxyServer := httptest.NewServer(proxyHandler) 295 defer proxyServer.Close() 296 proxyURL, _ := url.Parse(proxyServer.URL) 297 proxyURL.Path = test.requestPath 298 paramValues := url.Values{} 299 for k, v := range test.requestParams { 300 paramValues[k] = []string{v} 301 } 302 proxyURL.RawQuery = paramValues.Encode() 303 var requestBody io.Reader 304 if test.requestBody != "" { 305 requestBody = bytes.NewBufferString(test.requestBody) 306 } 307 req, err := http.NewRequest(test.method, proxyURL.String(), requestBody) 308 if test.requestHeader != nil { 309 header := http.Header{} 310 for k, v := range test.requestHeader { 311 header.Add(k, v) 312 } 313 req.Header = header 314 } 315 if err != nil { 316 t.Errorf("Error creating client request: %v", err) 317 } 318 client := &http.Client{} 319 res, err := client.Do(req) 320 if err != nil { 321 t.Errorf("Error from proxy request: %v", err) 322 } 323 324 // Host 325 if test.useLocationHost && backendHandler.requestHost != backendURL.Host { 326 t.Errorf("Unexpected request host: %s", backendHandler.requestHost) 327 } else if !test.useLocationHost && backendHandler.requestHost == backendURL.Host { 328 t.Errorf("Unexpected request host: %s", backendHandler.requestHost) 329 } 330 331 if test.expectError != nil { 332 if !responder.called { 333 t.Errorf("%d: responder was not invoked", i) 334 return 335 } 336 if !test.expectError(responder.err) { 337 t.Errorf("%d: unexpected error: %v", i, responder.err) 338 } 339 return 340 } 341 342 // Validate backend request 343 // Method 344 if backendHandler.requestMethod != test.method { 345 t.Errorf("Unexpected request method: %s. Expected: %s", 346 backendHandler.requestMethod, test.method) 347 } 348 349 // Body 350 if string(backendHandler.requestBody) != test.requestBody { 351 t.Errorf("Unexpected request body: %s. Expected: %s", 352 string(backendHandler.requestBody), test.requestBody) 353 } 354 355 // Path 356 if backendHandler.requestURL.Path != test.expectedPath { 357 t.Errorf("Unexpected request path: %s", backendHandler.requestURL.Path) 358 } 359 // Parameters 360 validateParameters(t, test.name, backendHandler.requestURL.Query(), test.requestParams) 361 362 // Headers 363 validateHeaders(t, test.name+" backend request", backendHandler.requestHeader, 364 test.requestHeader, nil) 365 366 // Validate proxy response 367 368 // Response Headers 369 validateHeaders(t, test.name+" backend headers", res.Header, test.expectedRespHeader, test.notExpectedRespHeader) 370 371 // Validate Body 372 responseBody, err := io.ReadAll(res.Body) 373 if err != nil { 374 t.Errorf("Unexpected error reading response body: %v", err) 375 } 376 if rb := string(responseBody); rb != backendResponse { 377 t.Errorf("Did not get expected response body: %s. Expected: %s", rb, backendResponse) 378 } 379 380 // Error 381 if responder.called { 382 t.Errorf("Unexpected proxy handler error: %v", responder.err) 383 } 384 }() 385 } 386 } 387 388 type RoundTripperFunc func(req *http.Request) (*http.Response, error) 389 390 func (fn RoundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { 391 return fn(req) 392 } 393 394 func TestProxyUpgrade(t *testing.T) { 395 396 localhostPool := x509.NewCertPool() 397 if !localhostPool.AppendCertsFromPEM(localhostCert) { 398 t.Errorf("error setting up localhostCert pool") 399 } 400 var d net.Dialer 401 402 testcases := map[string]struct { 403 ServerFunc func(http.Handler) *httptest.Server 404 ProxyTransport http.RoundTripper 405 UpgradeTransport UpgradeRequestRoundTripper 406 ExpectedAuth string 407 }{ 408 "http": { 409 ServerFunc: httptest.NewServer, 410 ProxyTransport: nil, 411 }, 412 "both client and server support http2, but force to http/1.1 for upgrade": { 413 ServerFunc: func(h http.Handler) *httptest.Server { 414 cert, err := tls.X509KeyPair(exampleCert, exampleKey) 415 if err != nil { 416 t.Errorf("https (invalid hostname): proxy_test: %v", err) 417 } 418 ts := httptest.NewUnstartedServer(h) 419 ts.TLS = &tls.Config{ 420 Certificates: []tls.Certificate{cert}, 421 NextProtos: []string{"http2", "http/1.1"}, 422 } 423 ts.StartTLS() 424 return ts 425 }, 426 ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: &tls.Config{ 427 NextProtos: []string{"http2", "http/1.1"}, 428 InsecureSkipVerify: true, 429 }}), 430 }, 431 "https (invalid hostname + InsecureSkipVerify)": { 432 ServerFunc: func(h http.Handler) *httptest.Server { 433 cert, err := tls.X509KeyPair(exampleCert, exampleKey) 434 if err != nil { 435 t.Errorf("https (invalid hostname): proxy_test: %v", err) 436 } 437 ts := httptest.NewUnstartedServer(h) 438 ts.TLS = &tls.Config{ 439 Certificates: []tls.Certificate{cert}, 440 } 441 ts.StartTLS() 442 return ts 443 }, 444 ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}), 445 }, 446 "https (valid hostname + RootCAs)": { 447 ServerFunc: func(h http.Handler) *httptest.Server { 448 cert, err := tls.X509KeyPair(localhostCert, localhostKey) 449 if err != nil { 450 t.Errorf("https (valid hostname): proxy_test: %v", err) 451 } 452 ts := httptest.NewUnstartedServer(h) 453 ts.TLS = &tls.Config{ 454 Certificates: []tls.Certificate{cert}, 455 } 456 ts.StartTLS() 457 return ts 458 }, 459 ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: &tls.Config{RootCAs: localhostPool}}), 460 }, 461 "https (valid hostname + RootCAs + custom dialer)": { 462 ServerFunc: func(h http.Handler) *httptest.Server { 463 cert, err := tls.X509KeyPair(localhostCert, localhostKey) 464 if err != nil { 465 t.Errorf("https (valid hostname): proxy_test: %v", err) 466 } 467 ts := httptest.NewUnstartedServer(h) 468 ts.TLS = &tls.Config{ 469 Certificates: []tls.Certificate{cert}, 470 } 471 ts.StartTLS() 472 return ts 473 }, 474 ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{DialContext: d.DialContext, TLSClientConfig: &tls.Config{RootCAs: localhostPool}}), 475 }, 476 "https (valid hostname + RootCAs + custom dialer + bearer token)": { 477 ServerFunc: func(h http.Handler) *httptest.Server { 478 cert, err := tls.X509KeyPair(localhostCert, localhostKey) 479 if err != nil { 480 t.Errorf("https (valid hostname): proxy_test: %v", err) 481 } 482 ts := httptest.NewUnstartedServer(h) 483 ts.TLS = &tls.Config{ 484 Certificates: []tls.Certificate{cert}, 485 } 486 ts.StartTLS() 487 return ts 488 }, 489 ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{DialContext: d.DialContext, TLSClientConfig: &tls.Config{RootCAs: localhostPool}}), 490 UpgradeTransport: NewUpgradeRequestRoundTripper( 491 utilnet.SetOldTransportDefaults(&http.Transport{DialContext: d.DialContext, TLSClientConfig: &tls.Config{RootCAs: localhostPool}}), 492 RoundTripperFunc(func(req *http.Request) (*http.Response, error) { 493 req = utilnet.CloneRequest(req) 494 req.Header.Set("Authorization", "Bearer 1234") 495 return MirrorRequest.RoundTrip(req) 496 }), 497 ), 498 ExpectedAuth: "Bearer 1234", 499 }, 500 } 501 502 for k, tc := range testcases { 503 tcName := k 504 backendPath := "/hello" 505 func() { // Cleanup after each test case. 506 backend := http.NewServeMux() 507 backend.Handle("/hello", websocket.Handler(func(ws *websocket.Conn) { 508 if ws.Request().Header.Get("Authorization") != tc.ExpectedAuth { 509 t.Errorf("%s: unexpected headers on request: %v", k, ws.Request().Header) 510 defer ws.Close() 511 ws.Write([]byte("you failed")) 512 return 513 } 514 defer ws.Close() 515 body := make([]byte, 5) 516 ws.Read(body) 517 ws.Write([]byte("hello " + string(body))) 518 })) 519 backend.Handle("/redirect", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 520 http.Redirect(w, r, "/hello", http.StatusFound) 521 })) 522 backendServer := tc.ServerFunc(backend) 523 defer backendServer.Close() 524 525 serverURL, _ := url.Parse(backendServer.URL) 526 serverURL.Path = backendPath 527 proxyHandler := NewUpgradeAwareHandler(serverURL, tc.ProxyTransport, false, false, &noErrorsAllowed{t: t}) 528 proxyHandler.UpgradeTransport = tc.UpgradeTransport 529 proxy := httptest.NewServer(proxyHandler) 530 defer proxy.Close() 531 532 ws, err := websocket.Dial("ws://"+proxy.Listener.Addr().String()+"/some/path", "", "http://127.0.0.1/") 533 if err != nil { 534 t.Fatalf("%s: websocket dial err: %s", tcName, err) 535 } 536 defer ws.Close() 537 538 if _, err := ws.Write([]byte("world")); err != nil { 539 t.Fatalf("%s: write err: %s", tcName, err) 540 } 541 542 response := make([]byte, 20) 543 n, err := ws.Read(response) 544 if err != nil { 545 t.Fatalf("%s: read err: %s", tcName, err) 546 } 547 if e, a := "hello world", string(response[0:n]); e != a { 548 t.Fatalf("%s: expected '%#v', got '%#v'", tcName, e, a) 549 } 550 }() 551 } 552 } 553 554 type noErrorsAllowed struct { 555 t *testing.T 556 } 557 558 func (r *noErrorsAllowed) Error(w http.ResponseWriter, req *http.Request, err error) { 559 r.t.Error(err) 560 } 561 562 func TestProxyUpgradeConnectionErrorResponse(t *testing.T) { 563 var ( 564 responder *fakeResponder 565 expectedErr = errors.New("EXPECTED") 566 ) 567 proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 568 transport := &http.Transport{ 569 Proxy: http.ProxyFromEnvironment, 570 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 571 return &fakeConn{err: expectedErr}, nil 572 }, 573 MaxIdleConns: 100, 574 IdleConnTimeout: 90 * time.Second, 575 TLSHandshakeTimeout: 10 * time.Second, 576 ExpectContinueTimeout: 1 * time.Second, 577 } 578 responder = &fakeResponder{t: t, w: w} 579 proxyHandler := NewUpgradeAwareHandler( 580 &url.URL{ 581 Host: "fake-backend", 582 }, 583 transport, 584 false, 585 true, 586 responder, 587 ) 588 proxyHandler.ServeHTTP(w, r) 589 })) 590 defer proxy.Close() 591 592 // Send request to proxy server. 593 req, err := http.NewRequest("POST", "http://"+proxy.Listener.Addr().String()+"/some/path", nil) 594 require.NoError(t, err) 595 req.Header.Set(httpstream.HeaderConnection, httpstream.HeaderUpgrade) 596 resp, err := http.DefaultClient.Do(req) 597 require.NoError(t, err) 598 defer resp.Body.Close() 599 600 // Expect error response. 601 assert.True(t, responder.called) 602 assert.Equal(t, fakeStatusCode, resp.StatusCode) 603 msg, err := io.ReadAll(resp.Body) 604 require.NoError(t, err) 605 assert.Contains(t, string(msg), expectedErr.Error()) 606 } 607 608 func TestProxyUpgradeErrorResponseTerminates(t *testing.T) { 609 for _, code := range []int{400, 500} { 610 t.Run(fmt.Sprintf("code=%v", code), func(t *testing.T) { 611 // Set up a backend server 612 backend := http.NewServeMux() 613 backend.Handle("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 614 w.WriteHeader(code) 615 w.Write([]byte(`some data`)) 616 })) 617 backend.Handle("/there", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 618 t.Error("request to /there") 619 })) 620 backendServer := httptest.NewServer(backend) 621 defer backendServer.Close() 622 backendServerURL, _ := url.Parse(backendServer.URL) 623 backendServerURL.Path = "/hello" 624 625 // Set up a proxy pointing to a specific path on the backend 626 proxyHandler := NewUpgradeAwareHandler(backendServerURL, nil, false, false, &noErrorsAllowed{t: t}) 627 proxy := httptest.NewServer(proxyHandler) 628 defer proxy.Close() 629 proxyURL, _ := url.Parse(proxy.URL) 630 631 conn, err := net.Dial("tcp", proxyURL.Host) 632 require.NoError(t, err) 633 bufferedReader := bufio.NewReader(conn) 634 635 // Send upgrade request resulting in a non-101 response from the backend 636 req, _ := http.NewRequest("GET", "/", nil) 637 req.Header.Set(httpstream.HeaderConnection, httpstream.HeaderUpgrade) 638 require.NoError(t, req.Write(conn)) 639 // Verify we get the correct response and full message body content 640 resp, err := http.ReadResponse(bufferedReader, nil) 641 require.NoError(t, err) 642 data, err := io.ReadAll(resp.Body) 643 require.NoError(t, err) 644 require.Equal(t, resp.StatusCode, code) 645 require.Equal(t, data, []byte(`some data`)) 646 resp.Body.Close() 647 648 // try to read from the connection to verify it was closed 649 b := make([]byte, 1) 650 conn.SetReadDeadline(time.Now().Add(time.Second)) 651 if _, err := conn.Read(b); err != io.EOF { 652 t.Errorf("expected EOF, got %v", err) 653 } 654 655 // Send another request to another endpoint to verify it is not received 656 req, _ = http.NewRequest("GET", "/there", nil) 657 req.Write(conn) 658 // wait to ensure the handler does not receive the request 659 time.Sleep(time.Second) 660 661 // clean up 662 conn.Close() 663 }) 664 } 665 } 666 667 func TestProxyUpgradeErrorResponse(t *testing.T) { 668 for _, code := range []int{200, 300, 302, 307} { 669 t.Run(fmt.Sprintf("code=%v", code), func(t *testing.T) { 670 // Set up a backend server 671 backend := http.NewServeMux() 672 backend.Handle("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 673 http.Redirect(w, r, "https://example.com/there", code) 674 })) 675 backendServer := httptest.NewServer(backend) 676 defer backendServer.Close() 677 backendServerURL, _ := url.Parse(backendServer.URL) 678 backendServerURL.Path = "/hello" 679 680 // Set up a proxy pointing to a specific path on the backend 681 proxyHandler := NewUpgradeAwareHandler(backendServerURL, nil, false, false, &fakeResponder{t: t}) 682 proxy := httptest.NewServer(proxyHandler) 683 defer proxy.Close() 684 proxyURL, _ := url.Parse(proxy.URL) 685 686 conn, err := net.Dial("tcp", proxyURL.Host) 687 require.NoError(t, err) 688 bufferedReader := bufio.NewReader(conn) 689 690 // Send upgrade request resulting in a non-101 response from the backend 691 req, _ := http.NewRequest("GET", "/", nil) 692 req.Header.Set(httpstream.HeaderConnection, httpstream.HeaderUpgrade) 693 require.NoError(t, req.Write(conn)) 694 // Verify we get the correct response and full message body content 695 resp, err := http.ReadResponse(bufferedReader, nil) 696 require.NoError(t, err) 697 assert.Equal(t, fakeStatusCode, resp.StatusCode) 698 resp.Body.Close() 699 700 // clean up 701 conn.Close() 702 }) 703 } 704 } 705 706 func TestRejectForwardingRedirectsOption(t *testing.T) { 707 originalBody := []byte(`some data`) 708 testCases := []struct { 709 name string 710 rejectForwardingRedirects bool 711 serverStatusCode int 712 redirect string 713 expectStatusCode int 714 expectBody []byte 715 }{ 716 { 717 name: "reject redirection enabled in proxy, backend server sending 200 response", 718 rejectForwardingRedirects: true, 719 serverStatusCode: 200, 720 expectStatusCode: 200, 721 expectBody: originalBody, 722 }, 723 { 724 name: "reject redirection enabled in proxy, backend server sending 301 response", 725 rejectForwardingRedirects: true, 726 serverStatusCode: 301, 727 redirect: "/", 728 expectStatusCode: 502, 729 expectBody: []byte(`the backend attempted to redirect this request, which is not permitted`), 730 }, 731 { 732 name: "reject redirection enabled in proxy, backend server sending 304 response with a location header", 733 rejectForwardingRedirects: true, 734 serverStatusCode: 304, 735 redirect: "/", 736 expectStatusCode: 502, 737 expectBody: []byte(`the backend attempted to redirect this request, which is not permitted`), 738 }, 739 { 740 name: "reject redirection enabled in proxy, backend server sending 304 response with no location header", 741 rejectForwardingRedirects: true, 742 serverStatusCode: 304, 743 expectStatusCode: 304, 744 expectBody: []byte{}, // client doesn't read the body for 304 responses 745 }, 746 { 747 name: "reject redirection disabled in proxy, backend server sending 200 response", 748 rejectForwardingRedirects: false, 749 serverStatusCode: 200, 750 expectStatusCode: 200, 751 expectBody: originalBody, 752 }, 753 { 754 name: "reject redirection disabled in proxy, backend server sending 301 response", 755 rejectForwardingRedirects: false, 756 serverStatusCode: 301, 757 redirect: "/", 758 expectStatusCode: 301, 759 expectBody: originalBody, 760 }, 761 } 762 for _, tc := range testCases { 763 t.Run(tc.name, func(t *testing.T) { 764 // Set up a backend server 765 backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 766 if tc.redirect != "" { 767 w.Header().Set("Location", tc.redirect) 768 } 769 w.WriteHeader(tc.serverStatusCode) 770 w.Write(originalBody) 771 })) 772 defer backendServer.Close() 773 backendServerURL, _ := url.Parse(backendServer.URL) 774 775 // Set up a proxy pointing to the backend 776 proxyHandler := NewUpgradeAwareHandler(backendServerURL, nil, false, false, &fakeResponder{t: t}) 777 proxyHandler.RejectForwardingRedirects = tc.rejectForwardingRedirects 778 proxy := httptest.NewServer(proxyHandler) 779 defer proxy.Close() 780 proxyURL, _ := url.Parse(proxy.URL) 781 782 conn, err := net.Dial("tcp", proxyURL.Host) 783 require.NoError(t, err) 784 bufferedReader := bufio.NewReader(conn) 785 786 req, _ := http.NewRequest("GET", proxyURL.String(), nil) 787 require.NoError(t, req.Write(conn)) 788 // Verify we get the correct response and message body content 789 resp, err := http.ReadResponse(bufferedReader, nil) 790 require.NoError(t, err) 791 assert.Equal(t, tc.expectStatusCode, resp.StatusCode) 792 data, err := io.ReadAll(resp.Body) 793 require.NoError(t, err) 794 assert.Equal(t, tc.expectBody, data) 795 assert.Equal(t, int64(len(tc.expectBody)), resp.ContentLength) 796 resp.Body.Close() 797 798 // clean up 799 conn.Close() 800 }) 801 } 802 } 803 804 func TestDefaultProxyTransport(t *testing.T) { 805 tests := []struct { 806 name, 807 url, 808 location, 809 expectedScheme, 810 expectedHost, 811 expectedPathPrepend string 812 }{ 813 { 814 name: "simple path", 815 url: "http://test.server:8080/a/test/location", 816 location: "http://localhost/location", 817 expectedScheme: "http", 818 expectedHost: "test.server:8080", 819 expectedPathPrepend: "/a/test", 820 }, 821 { 822 name: "empty path", 823 url: "http://test.server:8080/a/test/", 824 location: "http://localhost", 825 expectedScheme: "http", 826 expectedHost: "test.server:8080", 827 expectedPathPrepend: "/a/test", 828 }, 829 { 830 name: "location ending in slash", 831 url: "http://test.server:8080/a/test/", 832 location: "http://localhost/", 833 expectedScheme: "http", 834 expectedHost: "test.server:8080", 835 expectedPathPrepend: "/a/test", 836 }, 837 } 838 839 for _, test := range tests { 840 locURL, _ := url.Parse(test.location) 841 URL, _ := url.Parse(test.url) 842 h := NewUpgradeAwareHandler(locURL, nil, false, false, nil) 843 result := h.defaultProxyTransport(URL, nil) 844 transport := result.(*corsRemovingTransport).RoundTripper.(*Transport) 845 if transport.Scheme != test.expectedScheme { 846 t.Errorf("%s: unexpected scheme. Actual: %s, Expected: %s", test.name, transport.Scheme, test.expectedScheme) 847 } 848 if transport.Host != test.expectedHost { 849 t.Errorf("%s: unexpected host. Actual: %s, Expected: %s", test.name, transport.Host, test.expectedHost) 850 } 851 if transport.PathPrepend != test.expectedPathPrepend { 852 t.Errorf("%s: unexpected path prepend. Actual: %s, Expected: %s", test.name, transport.PathPrepend, test.expectedPathPrepend) 853 } 854 } 855 } 856 857 func TestProxyRequestContentLengthAndTransferEncoding(t *testing.T) { 858 chunk := func(data []byte) []byte { 859 out := &bytes.Buffer{} 860 chunker := httputil.NewChunkedWriter(out) 861 for _, b := range data { 862 if _, err := chunker.Write([]byte{b}); err != nil { 863 panic(err) 864 } 865 } 866 chunker.Close() 867 out.Write([]byte("\r\n")) 868 return out.Bytes() 869 } 870 871 zip := func(data []byte) []byte { 872 out := &bytes.Buffer{} 873 zipper := gzip.NewWriter(out) 874 if _, err := zipper.Write(data); err != nil { 875 panic(err) 876 } 877 zipper.Close() 878 return out.Bytes() 879 } 880 881 sampleData := []byte("abcde") 882 883 table := map[string]struct { 884 reqHeaders http.Header 885 reqBody []byte 886 887 expectedHeaders http.Header 888 expectedBody []byte 889 }{ 890 "content-length": { 891 reqHeaders: http.Header{ 892 "Content-Length": []string{"5"}, 893 }, 894 reqBody: sampleData, 895 896 expectedHeaders: http.Header{ 897 "Content-Length": []string{"5"}, 898 "Content-Encoding": nil, // none set 899 "Transfer-Encoding": nil, // none set 900 }, 901 expectedBody: sampleData, 902 }, 903 904 "content-length + gzip content-encoding": { 905 reqHeaders: http.Header{ 906 "Content-Length": []string{strconv.Itoa(len(zip(sampleData)))}, 907 "Content-Encoding": []string{"gzip"}, 908 }, 909 reqBody: zip(sampleData), 910 911 expectedHeaders: http.Header{ 912 "Content-Length": []string{strconv.Itoa(len(zip(sampleData)))}, 913 "Content-Encoding": []string{"gzip"}, 914 "Transfer-Encoding": nil, // none set 915 }, 916 expectedBody: zip(sampleData), 917 }, 918 919 "chunked transfer-encoding": { 920 reqHeaders: http.Header{ 921 "Transfer-Encoding": []string{"chunked"}, 922 }, 923 reqBody: chunk(sampleData), 924 925 expectedHeaders: http.Header{ 926 "Content-Length": nil, // none set 927 "Content-Encoding": nil, // none set 928 "Transfer-Encoding": nil, // Transfer-Encoding gets removed 929 }, 930 expectedBody: sampleData, // sample data is unchunked 931 }, 932 933 "chunked transfer-encoding + gzip content-encoding": { 934 reqHeaders: http.Header{ 935 "Content-Encoding": []string{"gzip"}, 936 "Transfer-Encoding": []string{"chunked"}, 937 }, 938 reqBody: chunk(zip(sampleData)), 939 940 expectedHeaders: http.Header{ 941 "Content-Length": nil, // none set 942 "Content-Encoding": []string{"gzip"}, 943 "Transfer-Encoding": nil, // gets removed 944 }, 945 expectedBody: zip(sampleData), // sample data is unchunked, but content-encoding is preserved 946 }, 947 948 // "Transfer-Encoding: gzip" is not supported by go 949 // See http/transfer.go#fixTransferEncoding (https://golang.org/src/net/http/transfer.go#L427) 950 // Once it is supported, this test case should succeed 951 // 952 // "gzip+chunked transfer-encoding": { 953 // reqHeaders: http.Header{ 954 // "Transfer-Encoding": []string{"chunked,gzip"}, 955 // }, 956 // reqBody: chunk(zip(sampleData)), 957 // 958 // expectedHeaders: http.Header{ 959 // "Content-Length": nil, // no content-length headers 960 // "Transfer-Encoding": nil, // Transfer-Encoding gets removed 961 // }, 962 // expectedBody: sampleData, 963 // }, 964 } 965 966 successfulResponse := "backend passed tests" 967 for k, item := range table { 968 // Start the downstream server 969 downstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 970 // Verify headers 971 for header, v := range item.expectedHeaders { 972 if !reflect.DeepEqual(v, req.Header[header]) { 973 t.Errorf("%s: Expected headers for %s to be %v, got %v", k, header, v, req.Header[header]) 974 } 975 } 976 977 // Read body 978 body, err := io.ReadAll(req.Body) 979 if err != nil { 980 t.Errorf("%s: unexpected error %v", k, err) 981 } 982 req.Body.Close() 983 984 // Verify length 985 if req.ContentLength > 0 && req.ContentLength != int64(len(body)) { 986 t.Errorf("%s: ContentLength was %d, len(data) was %d", k, req.ContentLength, len(body)) 987 } 988 989 // Verify content 990 if !bytes.Equal(item.expectedBody, body) { 991 t.Errorf("%s: Expected %q, got %q", k, string(item.expectedBody), string(body)) 992 } 993 994 // Write successful response 995 w.Write([]byte(successfulResponse)) 996 })) 997 defer downstreamServer.Close() 998 999 responder := &fakeResponder{t: t} 1000 backendURL, _ := url.Parse(downstreamServer.URL) 1001 proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, false, responder) 1002 proxyServer := httptest.NewServer(proxyHandler) 1003 defer proxyServer.Close() 1004 1005 // Dial the proxy server 1006 conn, err := net.Dial(proxyServer.Listener.Addr().Network(), proxyServer.Listener.Addr().String()) 1007 if err != nil { 1008 t.Errorf("unexpected error %v", err) 1009 continue 1010 } 1011 defer conn.Close() 1012 1013 // Add standard http 1.1 headers 1014 if item.reqHeaders == nil { 1015 item.reqHeaders = http.Header{} 1016 } 1017 item.reqHeaders.Add("Connection", "close") 1018 item.reqHeaders.Add("Host", proxyServer.Listener.Addr().String()) 1019 1020 // Write the request headers 1021 if _, err := fmt.Fprint(conn, "POST / HTTP/1.1\r\n"); err != nil { 1022 t.Fatalf("%s unexpected error %v", k, err) 1023 } 1024 for header, values := range item.reqHeaders { 1025 for _, value := range values { 1026 if _, err := fmt.Fprintf(conn, "%s: %s\r\n", header, value); err != nil { 1027 t.Fatalf("%s: unexpected error %v", k, err) 1028 } 1029 } 1030 } 1031 // Header separator 1032 if _, err := fmt.Fprint(conn, "\r\n"); err != nil { 1033 t.Fatalf("%s: unexpected error %v", k, err) 1034 } 1035 // Body 1036 if _, err := conn.Write(item.reqBody); err != nil { 1037 t.Fatalf("%s: unexpected error %v", k, err) 1038 } 1039 1040 // Read response 1041 response, err := io.ReadAll(conn) 1042 if err != nil { 1043 t.Errorf("%s: unexpected error %v", k, err) 1044 continue 1045 } 1046 if !strings.HasSuffix(string(response), successfulResponse) { 1047 t.Errorf("%s: Did not get successful response: %s", k, string(response)) 1048 continue 1049 } 1050 } 1051 } 1052 1053 func TestFlushIntervalHeaders(t *testing.T) { 1054 const expected = "hi" 1055 stopCh := make(chan struct{}) 1056 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1057 w.Header().Add("MyHeader", expected) 1058 w.WriteHeader(200) 1059 w.(http.Flusher).Flush() 1060 <-stopCh 1061 })) 1062 defer backend.Close() 1063 defer close(stopCh) 1064 1065 backendURL, err := url.Parse(backend.URL) 1066 if err != nil { 1067 t.Fatal(err) 1068 } 1069 1070 responder := &fakeResponder{t: t} 1071 proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, false, responder) 1072 1073 frontend := httptest.NewServer(proxyHandler) 1074 defer frontend.Close() 1075 1076 req, _ := http.NewRequest("GET", frontend.URL, nil) 1077 req.Close = true 1078 1079 ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second) 1080 defer cancel() 1081 req = req.WithContext(ctx) 1082 1083 res, err := frontend.Client().Do(req) 1084 if err != nil { 1085 t.Fatalf("Get: %v", err) 1086 } 1087 defer res.Body.Close() 1088 1089 if res.Header.Get("MyHeader") != expected { 1090 t.Errorf("got header %q; expected %q", res.Header.Get("MyHeader"), expected) 1091 } 1092 } 1093 1094 type fakeRT struct { 1095 err error 1096 } 1097 1098 func (frt *fakeRT) RoundTrip(*http.Request) (*http.Response, error) { 1099 return nil, frt.err 1100 } 1101 1102 // TestErrorPropagation checks if the default transport doesn't swallow the errors by providing a fakeResponder that intercepts and stores the error. 1103 func TestErrorPropagation(t *testing.T) { 1104 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1105 panic("unreachable") 1106 })) 1107 defer backend.Close() 1108 1109 backendURL, err := url.Parse(backend.URL) 1110 if err != nil { 1111 t.Fatal(err) 1112 } 1113 1114 responder := &fakeResponder{t: t} 1115 expectedErr := errors.New("nasty error") 1116 proxyHandler := NewUpgradeAwareHandler(backendURL, &fakeRT{err: expectedErr}, true, false, responder) 1117 1118 frontend := httptest.NewServer(proxyHandler) 1119 defer frontend.Close() 1120 1121 req, _ := http.NewRequest("GET", frontend.URL, nil) 1122 req.Close = true 1123 1124 ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second) 1125 defer cancel() 1126 req = req.WithContext(ctx) 1127 1128 res, err := frontend.Client().Do(req) 1129 if err != nil { 1130 t.Fatalf("Get: %v", err) 1131 } 1132 defer res.Body.Close() 1133 if res.StatusCode != fakeStatusCode { 1134 t.Fatalf("unexpected HTTP status code returned: %v, expected: %v", res.StatusCode, fakeStatusCode) 1135 } 1136 if !strings.Contains(responder.err.Error(), expectedErr.Error()) { 1137 t.Fatalf("responder got unexpected error: %v, expected the error to contain %q", responder.err.Error(), expectedErr.Error()) 1138 } 1139 } 1140 1141 func TestProxyRedirectsforRootPath(t *testing.T) { 1142 1143 tests := []struct { 1144 name string 1145 method string 1146 requestPath string 1147 expectedHeader http.Header 1148 expectedStatusCode int 1149 redirect bool 1150 }{ 1151 { 1152 name: "root path, simple get", 1153 method: "GET", 1154 requestPath: "", 1155 redirect: true, 1156 expectedStatusCode: 301, 1157 expectedHeader: http.Header{ 1158 "Location": []string{"/"}, 1159 }, 1160 }, 1161 { 1162 name: "root path, simple put", 1163 method: "PUT", 1164 requestPath: "", 1165 redirect: false, 1166 expectedStatusCode: 200, 1167 }, 1168 { 1169 name: "root path, simple head", 1170 method: "HEAD", 1171 requestPath: "", 1172 redirect: true, 1173 expectedStatusCode: 301, 1174 expectedHeader: http.Header{ 1175 "Location": []string{"/"}, 1176 }, 1177 }, 1178 { 1179 name: "root path, simple delete with params", 1180 method: "DELETE", 1181 requestPath: "", 1182 redirect: false, 1183 expectedStatusCode: 200, 1184 }, 1185 } 1186 1187 for _, test := range tests { 1188 func() { 1189 w := httptest.NewRecorder() 1190 req, err := http.NewRequest(test.method, test.requestPath, nil) 1191 if err != nil { 1192 t.Fatal(err) 1193 } 1194 1195 redirect := proxyRedirectsforRootPath(test.requestPath, w, req) 1196 if got, want := redirect, test.redirect; got != want { 1197 t.Errorf("Expected redirect state %v; got %v", want, got) 1198 } 1199 1200 res := w.Result() 1201 if got, want := res.StatusCode, test.expectedStatusCode; got != want { 1202 t.Errorf("Expected status code %d; got %d", want, got) 1203 } 1204 1205 if res.StatusCode == 301 && !reflect.DeepEqual(res.Header, test.expectedHeader) { 1206 t.Errorf("Expected location header to be %v, got %v", test.expectedHeader, res.Header) 1207 } 1208 }() 1209 } 1210 } 1211 1212 // exampleCert was generated from crypto/tls/generate_cert.go with the following command: 1213 // 1214 // go run generate_cert.go --rsa-bits 1024 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h 1215 var exampleCert = []byte(`-----BEGIN CERTIFICATE----- 1216 MIIDADCCAeigAwIBAgIQVHG3Fn9SdWayyLOZKCW1vzANBgkqhkiG9w0BAQsFADAS 1217 MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw 1218 MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 1219 MIIBCgKCAQEArTCu9fiIclNgDdWHphewM+JW55dCb5yYGlJgCBvwbOx547M9p+tn 1220 zm9QOhsdZDHDZsG9tqnWxE2Nc1HpIJyOlfYsOoonpEoG/Ep6nnK91ngj0bn/JlNy 1221 +i/bwU4r97MOukvnOIQez9/D9jAJaOX2+b8/d4lRz9BsqiwJyg+ynZ5tVVYj7aMi 1222 vXnd6HOnJmtqutOtr3beucJnkd6XbwRkLUcAYATT+ZihOWRbTuKqhCg6zGkJOoUG 1223 f8sX61JjoilxiURA//ftGVbdTCU3DrmGmardp5NNOHbumMYU8Vhmqgx1Bqxb+9he 1224 7G42uW5YWYK/GqJzgVPjjlB2dOGj9KrEWQIDAQABo1AwTjAOBgNVHQ8BAf8EBAMC 1225 AqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAWBgNVHREE 1226 DzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAig4AIi9xWs1+pLES 1227 eeGGdSDoclplFpcbXANnsYYFyLf+8pcWgVi2bOmb2gXMbHFkB07MA82wRJAUTaA+ 1228 2iNXVQMhPCoA7J6ADUbww9doJX2S9HGyArhiV/MhHtE8txzMn2EKNLdhhk3N9rmV 1229 x/qRbWAY1U2z4BpdrAR87Fe81Nlj7h45csW9K+eS+NgXipiNTIfEShKgCFM8EdxL 1230 1WXg7r9AvYV3TNDPWTjLsm1rQzzZQ7Uvcf6deWiNodZd8MOT/BFLclDPTK6cF2Hr 1231 UU4dq6G4kCwMSxWE4cM3HlZ4u1dyIt47VbkP0rtvkBCXx36y+NXYA5lzntchNFZP 1232 uvEQdw== 1233 -----END CERTIFICATE-----`) 1234 1235 var exampleKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 1236 MIIEpQIBAAKCAQEArTCu9fiIclNgDdWHphewM+JW55dCb5yYGlJgCBvwbOx547M9 1237 p+tnzm9QOhsdZDHDZsG9tqnWxE2Nc1HpIJyOlfYsOoonpEoG/Ep6nnK91ngj0bn/ 1238 JlNy+i/bwU4r97MOukvnOIQez9/D9jAJaOX2+b8/d4lRz9BsqiwJyg+ynZ5tVVYj 1239 7aMivXnd6HOnJmtqutOtr3beucJnkd6XbwRkLUcAYATT+ZihOWRbTuKqhCg6zGkJ 1240 OoUGf8sX61JjoilxiURA//ftGVbdTCU3DrmGmardp5NNOHbumMYU8Vhmqgx1Bqxb 1241 +9he7G42uW5YWYK/GqJzgVPjjlB2dOGj9KrEWQIDAQABAoIBAQClt4CiYaaF5ltx 1242 wVDjz6TNcJUBUs3CKE+uWAYFnF5Ii1nyU876Pxj8Aaz9fHZ6Kde0GkwiXY7gFOj1 1243 YHo2tzcELSKS/SEDZcYbYFTGCjq13g1AH74R+SV6WZLn+5m8kPvVrM1ZWap188H5 1244 bmuCkRDqVmIvShkbRW7EwhC35J9fiuW3majC/sjmsxtxyP6geWmu4f5/Ttqahcdb 1245 osPZIgIIPzqAkNtkLTi7+meHYI9wlrGhL7XZTwnJ1Oc/Y67zzmbthLYB5YFSLUew 1246 rXT58jtSjX4gbiQyheBSrWxW08QE4qYg6jJlAdffHhWv72hJW2MCXhuXp8gJs/Do 1247 XLRHGwSBAoGBAMdNtsbe4yae/QeHUPGxNW0ipa0yoTF6i+VYoxvqiRMzDM3+3L8k 1248 dgI1rr4330SivqDahMA/odWtM/9rVwJI2B2QhZLMHA0n9ytH007OO9TghgVB12nN 1249 xosRYBpKdHXyyvV/MUZl7Jux6zKIzRDWOkF95VVYPcAaxJqd1E5/jJ6JAoGBAN51 1250 QrebA1w/jfydeqQTz1sK01sbO4HYj4qGfo/JarVqGEkm1azeBBPPRnHz3jNKnCkM 1251 S4PpqRDased3NIcViXlAgoqPqivZ8mQa/Rb146l7WaTErASHsZ023OGrxsr/Ed6N 1252 P3GrmvxVJjebaFNaQ9sP80dLkpgeas0t2TY8iQNRAoGATOcnx8TpUVW3vNfx29DN 1253 FLdxxkrq9/SZVn3FMlhlXAsuva3B799ZybB9JNjaRdmmRNsMrkHfaFvU3JHGmRMS 1254 kRXa9LHdgRYSwZiNaLMbUyDvlce6HxFPswmZU4u3NGvi9KeHk+pwSgN1BaLTvdNr 1255 1ymE/FF4QlAR3LdZ3JBK6kECgYEA0wW4/CJ31ZIURoW8SNjh4iMqy0nR8SJVR7q9 1256 Y/hU2TKDRyEnoIwaohAFayNCrLUh3W5kVAXa8roB+OgDVAECH5sqOfZ+HorofD19 1257 x8II7ESujLZj1whBXDkm3ovsT7QWZ17lyBZZNvQvBKDPHgKKS8udowv1S4fPGENd 1258 wS07a4ECgYEAwLSbmMIVJme0jFjsp5d1wOGA2Qi2ZwGIAVlsbnJtygrU/hSBfnu8 1259 VfyJSCgg3fPe7kChWKlfcOebVKSb68LKRsz1Lz1KdbY0HOJFp/cT4lKmDAlRY9gq 1260 LB4rdf46lV0mUkvd2/oofIbTrzukjQSnyfLawb/2uJGV1IkTcZcn9CI= 1261 -----END RSA PRIVATE KEY-----`)