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