github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+incompatible/cf/net/gateway_test.go (about)

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