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