code.cloudfoundry.org/cli@v7.1.0+incompatible/cf/net/gateway_test.go (about) 1 package net_test 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "net/http" 10 "net/http/httptest" 11 "net/url" 12 "os" 13 "reflect" 14 "runtime" 15 "strings" 16 "time" 17 18 "code.cloudfoundry.org/cli/cf/api/authentication" 19 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 20 "code.cloudfoundry.org/cli/cf/errors" 21 . "code.cloudfoundry.org/cli/cf/net" 22 "code.cloudfoundry.org/cli/cf/net/netfakes" 23 "code.cloudfoundry.org/cli/cf/terminal/terminalfakes" 24 "code.cloudfoundry.org/cli/cf/trace/tracefakes" 25 testconfig "code.cloudfoundry.org/cli/cf/util/testhelpers/configuration" 26 testnet "code.cloudfoundry.org/cli/cf/util/testhelpers/net" 27 "code.cloudfoundry.org/cli/version" 28 . "github.com/onsi/ginkgo" 29 . "github.com/onsi/gomega" 30 "github.com/onsi/gomega/ghttp" 31 ) 32 33 var ( 34 initialAccessToken = testconfig.BuildTokenString(time.Now().AddDate(0, 0, 1)) 35 refreshedAccessToken = testconfig.BuildTokenString(time.Now().AddDate(0, 1, 1)) 36 refreshToken = "refresh-token" 37 ) 38 39 var _ = Describe("Gateway", func() { 40 var ( 41 ccServer *ghttp.Server 42 ccGateway Gateway 43 uaaGateway Gateway 44 config coreconfig.ReadWriter 45 authRepo authentication.Repository 46 currentTime time.Time 47 clock func() time.Time 48 49 client *netfakes.FakeHTTPClientInterface 50 ) 51 52 BeforeEach(func() { 53 currentTime = time.Unix(0, 0) 54 clock = func() time.Time { return currentTime } 55 config = testconfig.NewRepository() 56 57 ccGateway = NewCloudControllerGateway(config, clock, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 58 ccGateway.PollingThrottle = 3 * time.Millisecond 59 uaaGateway = NewUAAGateway(config, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 60 }) 61 62 Describe("async timeout", func() { 63 Context("when the config has a positive async timeout", func() { 64 It("inherits the async timeout from the config", func() { 65 config.SetAsyncTimeout(9001) 66 ccGateway = NewCloudControllerGateway(config, time.Now, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 67 Expect(ccGateway.AsyncTimeout()).To(Equal(9001 * time.Minute)) 68 }) 69 }) 70 }) 71 72 Describe("Connection errors", func() { 73 var oldNewHTTPClient func(tr *http.Transport, dumper RequestDumper) HTTPClientInterface 74 75 BeforeEach(func() { 76 client = new(netfakes.FakeHTTPClientInterface) 77 78 oldNewHTTPClient = NewHTTPClient 79 NewHTTPClient = func(tr *http.Transport, dumper RequestDumper) HTTPClientInterface { 80 return client 81 } 82 }) 83 84 AfterEach(func() { 85 NewHTTPClient = oldNewHTTPClient 86 }) 87 88 It("only retry when response body is nil and error occurred", func() { 89 client.DoReturns(&http.Response{Status: "internal error", StatusCode: 500}, errors.New("internal error")) 90 request, apiErr := ccGateway.NewRequest("GET", "https://example.com/v2/apps", "BEARER my-access-token", nil) 91 Expect(apiErr).ToNot(HaveOccurred()) 92 93 _, apiErr = ccGateway.PerformRequest(request) 94 Expect(client.DoCallCount()).To(Equal(1)) 95 Expect(apiErr).To(HaveOccurred()) 96 }) 97 98 It("Retries 3 times if we cannot contact the server", func() { 99 client.DoReturns(nil, errors.New("Connection refused")) 100 request, apiErr := ccGateway.NewRequest("GET", "https://example.com/v2/apps", "BEARER my-access-token", nil) 101 Expect(apiErr).ToNot(HaveOccurred()) 102 103 _, apiErr = ccGateway.PerformRequest(request) 104 Expect(apiErr).To(HaveOccurred()) 105 Expect(client.DoCallCount()).To(Equal(3)) 106 }) 107 }) 108 109 Describe("NewRequest", func() { 110 var ( 111 request *Request 112 apiErr error 113 ) 114 115 Context("when the body is nil", func() { 116 BeforeEach(func() { 117 request, apiErr = ccGateway.NewRequest("GET", "https://example.com/v2/apps", initialAccessToken, nil) 118 Expect(apiErr).NotTo(HaveOccurred()) 119 }) 120 121 It("does not use a ProgressReader as the SeekableBody", func() { 122 Expect(reflect.TypeOf(request.SeekableBody)).To(BeNil()) 123 }) 124 125 It("sets the Authorization header", func() { 126 Expect(request.HTTPReq.Header.Get("Authorization")).To(Equal(initialAccessToken)) 127 }) 128 129 It("sets the accept header to application/json", func() { 130 Expect(request.HTTPReq.Header.Get("accept")).To(Equal("application/json")) 131 }) 132 133 It("sets the user agent header", func() { 134 Expect(request.HTTPReq.Header.Get("User-Agent")).To(Equal("go-cli " + version.VersionString() + " / " + runtime.GOOS)) 135 }) 136 }) 137 138 Context("when the body is a file", func() { 139 BeforeEach(func() { 140 f, _ := os.Open("../../fixtures/test.file") 141 request, apiErr = ccGateway.NewRequestForFile("PUT", "https://example.com/v2/apps", initialAccessToken, f) 142 Expect(apiErr).NotTo(HaveOccurred()) 143 }) 144 145 It("Uses a ProgressReader as the SeekableBody", func() { 146 Expect(reflect.TypeOf(request.SeekableBody).String()).To(ContainSubstring("ProgressReader")) 147 }) 148 149 }) 150 151 }) 152 153 Describe("PerformRequestForJSONResponse()", func() { 154 BeforeEach(func() { 155 ccServer = ghttp.NewServer() 156 ccServer.HTTPTestServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 157 config.SetAPIEndpoint(ccServer.URL()) 158 }) 159 160 AfterEach(func() { 161 ccServer.Close() 162 }) 163 164 Context("When CC response with an api error", func() { 165 BeforeEach(func() { 166 ccServer.AppendHandlers( 167 ghttp.CombineHandlers( 168 ghttp.VerifyRequest("GET", "/v2/some-endpoint"), 169 ghttp.VerifyHeaderKV("Connection", "close"), 170 ghttp.VerifyHeader(http.Header{ 171 "accept": []string{"application/json"}, 172 }), 173 ghttp.RespondWith(http.StatusUnauthorized, `{ 174 "code": 10003, 175 "description": "You are not authorized to perform the requested action", 176 "error_code": "CF-NotAuthorized" 177 }`), 178 ), 179 ) 180 }) 181 182 It("tries to unmarshal error response into provided resource", func() { 183 type apiErrResponse struct { 184 Code int `json:"code,omitempty"` 185 Description string `json:"description,omitempty"` 186 } 187 188 errResponse := new(apiErrResponse) 189 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/some-endpoint", config.AccessToken(), nil) 190 _, apiErr := ccGateway.PerformRequestForJSONResponse(request, errResponse) 191 192 Expect(apiErr).To(HaveOccurred()) 193 Expect(errResponse.Code).To(Equal(10003)) 194 }) 195 196 It("ignores any unmarshal error and does not alter the api err response", func() { 197 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/some-endpoint", config.AccessToken(), nil) 198 _, apiErr := ccGateway.PerformRequestForJSONResponse(request, nil) 199 200 Expect(apiErr.Error()).To(Equal("Server error, status code: 401, error code: 10003, message: You are not authorized to perform the requested action")) 201 }) 202 203 }) 204 205 }) 206 207 Describe("CRUD methods", func() { 208 Describe("Delete", func() { 209 var apiServer *httptest.Server 210 211 Describe("DeleteResourceSynchronously", func() { 212 var queryParams string 213 BeforeEach(func() { 214 apiServer = httptest.NewTLSServer(http.HandlerFunc(func(_ http.ResponseWriter, request *http.Request) { 215 queryParams = request.URL.RawQuery 216 })) 217 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 218 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 219 }) 220 221 It("does not send the async=true flag", func() { 222 err := ccGateway.DeleteResourceSynchronously(apiServer.URL, "/v2/foobars/SOME_GUID") 223 Expect(err).NotTo(HaveOccurred()) 224 Expect(queryParams).ToNot(ContainSubstring("async=true")) 225 }) 226 227 It("deletes a resource", func() { 228 err := ccGateway.DeleteResource(apiServer.URL, "/v2/foobars/SOME_GUID") 229 Expect(err).ToNot(HaveOccurred()) 230 }) 231 }) 232 233 Context("when the config has an async timeout", func() { 234 BeforeEach(func() { 235 count := 0 236 apiServer = httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 237 switch request.URL.Path { 238 case "/v2/foobars/SOME_GUID": 239 writer.WriteHeader(http.StatusNoContent) 240 case "/v2/foobars/TIMEOUT": 241 currentTime = currentTime.Add(time.Minute * 31) 242 fmt.Fprintln(writer, ` 243 { 244 "metadata": { 245 "guid": "8438916f-5c00-4d44-a19b-1df65abe9d52", 246 "created_at": "2014-05-15T19:15:01+00:00", 247 "url": "/v2/jobs/8438916f-5c00-4d44-a19b-1df65abe9d52" 248 }, 249 "entity": { 250 "guid": "8438916f-5c00-4d44-a19b-1df65abe9d52", 251 "status": "queued" 252 } 253 }`) 254 writer.WriteHeader(http.StatusAccepted) 255 case "/v2/jobs/8438916f-5c00-4d44-a19b-1df65abe9d52": 256 if count == 0 { 257 count++ 258 currentTime = currentTime.Add(time.Minute * 31) 259 260 writer.WriteHeader(http.StatusOK) 261 fmt.Fprintln(writer, ` 262 { 263 "entity": { 264 "guid": "8438916f-5c00-4d44-a19b-1df65abe9d52", 265 "status": "queued" 266 } 267 }`) 268 } else { 269 panic("FAIL") 270 } 271 default: 272 panic("shouldn't have made call to this URL: " + request.URL.Path) 273 } 274 })) 275 276 config.SetAsyncTimeout(30) 277 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 278 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 279 }) 280 281 AfterEach(func() { 282 apiServer.Close() 283 }) 284 285 It("deletes a resource", func() { 286 err := ccGateway.DeleteResource(apiServer.URL, "/v2/foobars/SOME_GUID") 287 Expect(err).ToNot(HaveOccurred()) 288 }) 289 290 Context("when the request would take longer than the async timeout", func() { 291 It("returns an error", func() { 292 apiErr := ccGateway.DeleteResource(apiServer.URL, "/v2/foobars/TIMEOUT") 293 Expect(apiErr).To(HaveOccurred()) 294 Expect(apiErr).To(BeAssignableToTypeOf(errors.NewAsyncTimeoutError("http://some.url"))) 295 }) 296 }) 297 }) 298 }) 299 }) 300 301 Describe("making an async request", func() { 302 var ( 303 jobStatus string 304 apiServer *httptest.Server 305 authServer *httptest.Server 306 statusChannel chan string 307 ) 308 309 BeforeEach(func() { 310 jobStatus = "queued" 311 statusChannel = make(chan string, 10) 312 313 apiServer = httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 314 currentTime = currentTime.Add(time.Millisecond * 11) 315 316 updateStatus, ok := <-statusChannel 317 if ok { 318 jobStatus = updateStatus 319 } 320 321 switch request.URL.Path { 322 case "/v2/foo": 323 fmt.Fprintln(writer, `{ "metadata": { "url": "/v2/jobs/the-job-guid" } }`) 324 case "/v2/jobs/the-job-guid": 325 fmt.Fprintf(writer, ` 326 { 327 "entity": { 328 "status": "%s", 329 "error_details": { 330 "description": "he's dead, Jim" 331 } 332 } 333 }`, jobStatus) 334 default: 335 writer.WriteHeader(http.StatusInternalServerError) 336 fmt.Fprintf(writer, `"Unexpected request path '%s'"`, request.URL.Path) 337 } 338 })) 339 340 authServer, _ = testnet.NewTLSServer([]testnet.TestRequest{}) 341 342 config, authRepo = createAuthenticationRepository(apiServer, authServer) 343 ccGateway.SetTokenRefresher(authRepo) 344 345 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 346 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 347 authServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 348 }) 349 350 AfterEach(func() { 351 apiServer.Close() 352 authServer.Close() 353 }) 354 355 It("returns the last response if the job completes before the timeout", func() { 356 go func() { 357 statusChannel <- "queued" 358 statusChannel <- "finished" 359 }() 360 361 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/foo", config.AccessToken(), nil) 362 _, apiErr := ccGateway.PerformPollingRequestForJSONResponse(config.APIEndpoint(), request, new(struct{}), 500*time.Millisecond) 363 Expect(apiErr).NotTo(HaveOccurred()) 364 }) 365 366 It("returns an error with the right message when the job fails", func() { 367 go func() { 368 statusChannel <- "queued" 369 statusChannel <- "failed" 370 }() 371 372 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/foo", config.AccessToken(), nil) 373 _, apiErr := ccGateway.PerformPollingRequestForJSONResponse(config.APIEndpoint(), request, new(struct{}), 500*time.Millisecond) 374 Expect(apiErr.Error()).To(ContainSubstring("he's dead, Jim")) 375 }) 376 377 It("returns an error if jobs takes longer than the timeout", func() { 378 go func() { 379 statusChannel <- "queued" 380 statusChannel <- "OHNOES" 381 }() 382 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/foo", config.AccessToken(), nil) 383 _, apiErr := ccGateway.PerformPollingRequestForJSONResponse(config.APIEndpoint(), request, new(struct{}), 10*time.Millisecond) 384 Expect(apiErr).To(HaveOccurred()) 385 Expect(apiErr).To(BeAssignableToTypeOf(errors.NewAsyncTimeoutError("http://some.url"))) 386 }) 387 }) 388 389 Describe("when uploading a file", func() { 390 var ( 391 err error 392 request *Request 393 apiErr error 394 apiServer *httptest.Server 395 authServer *httptest.Server 396 fileToUpload *os.File 397 ) 398 399 BeforeEach(func() { 400 apiServer = httptest.NewTLSServer(refreshTokenAPIEndPoint( 401 `{ "code": 1000, "description": "Auth token is invalid" }`, 402 testnet.TestResponse{Status: http.StatusOK}, 403 )) 404 405 authServer = httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 406 fmt.Fprintln( 407 writer, 408 fmt.Sprintf(`{ "access_token": "%s", "token_type": "bearer", "refresh_token": "new-refresh-token"}`, refreshedAccessToken)) 409 })) 410 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 411 authServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 412 413 fileToUpload, err = ioutil.TempFile("", "test-gateway") 414 strings.NewReader("expected body").WriteTo(fileToUpload) 415 416 config, auth := createAuthenticationRepository(apiServer, authServer) 417 ccGateway.SetTokenRefresher(auth) 418 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 419 420 request, apiErr = ccGateway.NewRequestForFile("POST", config.APIEndpoint()+"/v2/foo", config.AccessToken(), fileToUpload) 421 }) 422 423 AfterEach(func() { 424 apiServer.Close() 425 authServer.Close() 426 fileToUpload.Close() 427 os.Remove(fileToUpload.Name()) 428 }) 429 430 It("sets the content length to the size of the file", func() { 431 Expect(err).NotTo(HaveOccurred()) 432 Expect(apiErr).NotTo(HaveOccurred()) 433 Expect(request.HTTPReq.ContentLength).To(Equal(int64(13))) 434 }) 435 }) 436 437 Describe("refreshing the auth token", func() { 438 var ( 439 authServer *httptest.Server 440 requestPath = "/v2/foo" 441 ) 442 443 AfterEach(func() { 444 authServer.Close() 445 }) 446 447 It("is done when the token is expired", func() { 448 authServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 449 fmt.Fprintln(w, fmt.Sprintf(`{ 450 "access_token": "%s", 451 "token_type": "bearer", 452 "refresh_token": "%s" 453 }`, refreshedAccessToken, refreshToken)) 454 })) 455 uaaGateway.SetTrustedCerts(authServer.TLS.Certificates) 456 authServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 457 458 requestPath := "/v2/foo" 459 apiServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 460 // refresh call 461 if r.Header.Get("Authorization") == "bearer "+refreshedAccessToken && r.URL.Path == requestPath { 462 return // 200 with no body 463 } else { 464 http.Error(w, "unexpected request", http.StatusTeapot) 465 } 466 })) 467 468 defer apiServer.Close() 469 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 470 471 config, auth := createAuthenticationRepository(apiServer, authServer) 472 expiredToken := testconfig.BuildTokenString(time.Time{}) 473 config.SetAccessToken("bearer " + expiredToken) 474 uaaGateway.SetTokenRefresher(auth) 475 request, apiErr := uaaGateway.NewRequest("POST", config.APIEndpoint()+requestPath, "bearer "+expiredToken, strings.NewReader("expected body")) 476 _, apiErr = uaaGateway.PerformRequest(request) 477 478 Expect(apiErr).NotTo(HaveOccurred()) 479 Expect(config.AccessToken()).To(Equal("bearer " + refreshedAccessToken)) 480 Expect(config.RefreshToken()).To(Equal(refreshToken)) 481 }) 482 483 It("is not done when the token is still valid", func() { 484 authServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 485 http.Error(w, "dont call me", http.StatusTeapot) 486 })) 487 uaaGateway.SetTrustedCerts(authServer.TLS.Certificates) 488 authServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 489 490 apiServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 491 if r.Header.Get("Authorization") == "bearer "+initialAccessToken && r.URL.Path == requestPath { 492 return // 200 with no body 493 } else { 494 http.Error(w, "unexpected request", http.StatusTeapot) 495 } 496 })) 497 defer apiServer.Close() 498 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 499 500 config, auth := createAuthenticationRepository(apiServer, authServer) 501 config.SetAccessToken("bearer " + initialAccessToken) 502 uaaGateway.SetTokenRefresher(auth) 503 request, apiErr := uaaGateway.NewRequest("POST", config.APIEndpoint()+requestPath, "bearer "+initialAccessToken, strings.NewReader("some body")) 504 _, apiErr = uaaGateway.PerformRequest(request) 505 506 Expect(apiErr).NotTo(HaveOccurred()) 507 Expect(config.AccessToken()).To(Equal("bearer " + initialAccessToken)) 508 }) 509 510 }) 511 512 Describe("SSL certificate validation errors", func() { 513 var ( 514 request *Request 515 apiServer *httptest.Server 516 ) 517 518 BeforeEach(func() { 519 apiServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 520 fmt.Fprintln(w, `{}`) 521 })) 522 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 523 request, _ = ccGateway.NewRequest("POST", apiServer.URL+"/v2/foo", "the-access-token", nil) 524 }) 525 526 AfterEach(func() { 527 apiServer.Close() 528 }) 529 530 Context("when SSL validation is enabled", func() { 531 It("returns an invalid cert error if the server's CA is unknown (e.g. cert is self-signed)", func() { 532 apiServer.TLS.Certificates = []tls.Certificate{testnet.MakeSelfSignedTLSCert()} 533 534 _, apiErr := ccGateway.PerformRequest(request) 535 certErr, ok := apiErr.(*errors.InvalidSSLCert) 536 Expect(ok).To(BeTrue()) 537 Expect(certErr.URL).To(Equal(getHost(apiServer.URL))) 538 Expect(certErr.Reason).To(Equal("unknown authority")) 539 }) 540 541 It("returns an invalid cert error if the server's cert doesn't match its host", func() { 542 apiServer.TLS.Certificates = []tls.Certificate{testnet.MakeTLSCertWithInvalidHost()} 543 544 _, apiErr := ccGateway.PerformRequest(request) 545 certErr, ok := apiErr.(*errors.InvalidSSLCert) 546 Expect(ok).To(BeTrue()) 547 Expect(certErr.URL).To(Equal(getHost(apiServer.URL))) 548 if runtime.GOOS != "windows" { 549 Expect(certErr.Reason).To(Equal("not valid for the requested host")) 550 } 551 }) 552 553 It("returns an invalid cert error if the server's cert has expired", func() { 554 apiServer.TLS.Certificates = []tls.Certificate{testnet.MakeExpiredTLSCert()} 555 556 _, apiErr := ccGateway.PerformRequest(request) 557 certErr, ok := apiErr.(*errors.InvalidSSLCert) 558 Expect(ok).To(BeTrue()) 559 Expect(certErr.URL).To(Equal(getHost(apiServer.URL))) 560 if runtime.GOOS != "windows" { 561 Expect(certErr.Reason).To(Equal("")) 562 } 563 }) 564 }) 565 566 Context("when SSL validation is disabled", func() { 567 BeforeEach(func() { 568 apiServer.TLS.Certificates = []tls.Certificate{testnet.MakeExpiredTLSCert()} 569 config.SetSSLDisabled(true) 570 }) 571 572 It("succeeds", func() { 573 _, apiErr := ccGateway.PerformRequest(request) 574 Expect(apiErr).NotTo(HaveOccurred()) 575 }) 576 }) 577 578 }) 579 580 Describe("collecting warnings", func() { 581 var ( 582 apiServer *httptest.Server 583 authServer *httptest.Server 584 ) 585 586 BeforeEach(func() { 587 apiServer = httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 588 switch request.URL.Path { 589 case "/v2/happy": 590 fmt.Fprintln(writer, `{ "metadata": { "url": "/v2/jobs/the-job-guid" } }`) 591 case "/v2/warning1": 592 writer.Header().Add("X-Cf-Warnings", url.QueryEscape("Something not too awful has happened")) 593 fmt.Fprintln(writer, `{ "metadata": { "url": "/v2/jobs/the-job-guid" } }`) 594 case "/v2/warning2": 595 writer.Header().Add("X-Cf-Warnings", url.QueryEscape("Something a little awful")) 596 writer.WriteHeader(http.StatusInternalServerError) 597 fmt.Fprintf(writer, `{ "key": "value" }`) 598 } 599 })) 600 601 authServer, _ = testnet.NewTLSServer([]testnet.TestRequest{}) 602 603 config, authRepo = createAuthenticationRepository(apiServer, authServer) 604 ccGateway.SetTokenRefresher(authRepo) 605 606 ccGateway.SetTrustedCerts(apiServer.TLS.Certificates) 607 apiServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 608 authServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 609 610 config, authRepo = createAuthenticationRepository(apiServer, authServer) 611 }) 612 613 AfterEach(func() { 614 apiServer.Close() 615 authServer.Close() 616 }) 617 618 It("saves all X-Cf-Warnings headers and exposes them", func() { 619 request, _ := ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/happy", config.AccessToken(), nil) 620 ccGateway.PerformRequest(request) 621 request, _ = ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/warning1", config.AccessToken(), nil) 622 ccGateway.PerformRequest(request) 623 request, _ = ccGateway.NewRequest("GET", config.APIEndpoint()+"/v2/warning2", config.AccessToken(), nil) 624 ccGateway.PerformRequest(request) 625 626 Expect(ccGateway.Warnings()).To(Equal( 627 []string{"Something not too awful has happened", "Something a little awful"}, 628 )) 629 }) 630 631 It("defaults warnings to an empty slice", func() { 632 Expect(ccGateway.Warnings()).ToNot(BeNil()) 633 }) 634 }) 635 }) 636 637 func getHost(urlString string) string { 638 url, err := url.Parse(urlString) 639 Expect(err).NotTo(HaveOccurred()) 640 return url.Host 641 } 642 643 func refreshTokenAPIEndPoint(unauthorizedBody string, secondReqResp testnet.TestResponse) http.HandlerFunc { 644 return func(writer http.ResponseWriter, request *http.Request) { 645 var jsonResponse string 646 647 bodyBytes, err := ioutil.ReadAll(request.Body) 648 if err != nil || string(bodyBytes) != "expected body" { 649 writer.WriteHeader(http.StatusInternalServerError) 650 return 651 } 652 653 switch request.Header.Get("Authorization") { 654 case initialAccessToken: 655 writer.WriteHeader(http.StatusUnauthorized) 656 jsonResponse = unauthorizedBody 657 case refreshedAccessToken: 658 writer.WriteHeader(secondReqResp.Status) 659 jsonResponse = secondReqResp.Body 660 default: 661 writer.WriteHeader(http.StatusInternalServerError) 662 } 663 664 fmt.Fprintln(writer, jsonResponse) 665 } 666 } 667 668 func createAuthenticationRepository(apiServer *httptest.Server, authServer *httptest.Server) (coreconfig.ReadWriter, authentication.Repository) { 669 config := testconfig.NewRepository() 670 config.SetAuthenticationEndpoint(authServer.URL) 671 config.SetAPIEndpoint(apiServer.URL) 672 673 authGateway := NewUAAGateway(config, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 674 authGateway.SetTrustedCerts(authServer.TLS.Certificates) 675 676 fakePrinter := new(tracefakes.FakePrinter) 677 dumper := NewRequestDumper(fakePrinter) 678 authenticator := authentication.NewUAARepository(authGateway, config, dumper) 679 680 return config, authenticator 681 }