private OkHttpClient createRetryingClient(int maxNumRetries, Duration backoffSlotSize) { return createRetryingClient(maxNumRetries, backoffSlotSize, url); }
@Test public void meshProxyClientChangesTargetAndInjectHostHeader() throws Exception { server.enqueue(new MockResponse().setBody("foo")); String serviceUrl = "http://foo.com/"; ClientConfiguration proxiedConfig = ClientConfiguration.builder() .from(createTestConfig(serviceUrl)) .meshProxy(HostAndPort.fromParts("localhost", server.getPort())) .maxNumRetries(0) .build(); OkHttpClient client = OkHttpClients.create(proxiedConfig, AGENT, hostEventsSink, OkHttpClientsTest.class); assertThat(client.newCall(new Request.Builder().url(serviceUrl).build()).execute().body().string()) .isEqualTo("foo"); assertThat(server.takeRequest().getHeader(HttpHeaders.HOST)).isEqualTo("foo.com"); }
@Test public void handlesRetryOther_redirectsToOtherUrl() throws Exception { OkHttpClient client = OkHttpClients.withStableUris( ClientConfiguration.builder().from(createTestConfig(url, url2)).build(), AGENT, hostEventsSink, OkHttpClientsTest.class); server.enqueue(new MockResponse().setResponseCode(308).addHeader(HttpHeaders.LOCATION, url2)); server2.enqueue(new MockResponse().setResponseCode(200).setBody("foo")); Call call = client.newCall(new Request.Builder().url(url + "/foo?bar").build()); assertThat(call.execute().body().string()).isEqualTo("foo"); assertThat(server.takeRequest().getPath()).isEqualTo("/foo?bar"); assertThat(server2.takeRequest().getPath()).isEqualTo("/foo?bar"); }
private OkHttpClient createRetryingClient(int maxNumRetries, String... urls) { return createRetryingClient(maxNumRetries, Duration.ofMillis(10), urls); }
@Test(timeout = 10_000) public void randomizesUrls() throws IOException { boolean server2WasHit = false; server.shutdown(); server2.enqueue(new MockResponse().setResponseCode(200).setBody("foo")); while (!server2WasHit) { OkHttpClient client = OkHttpClients.create( ClientConfiguration.builder() .from(createTestConfig(url, url2)) .maxNumRetries(0) .build(), AGENT, hostEventsSink, OkHttpClientsTest.class); Call call = client.newCall(new Request.Builder().url(url).build()); String response = null; try { response = call.execute().body().string(); } catch (Exception e) { System.out.println("Failed to talk to shutdown server (this is expected with p=1/2)"); assertThat(e.getCause().toString()).contains(Integer.toString(server.getPort())); } if (response != null) { assertThat(response).isEqualTo("foo"); server2WasHit = true; } } }
private OkHttpClient createRetryingClient(int maxNumRetries) { return createRetryingClient(maxNumRetries, Duration.ofMillis(500)); }
@Test public void timeouts_set_to_all_zero_should_be_treated_as_infinity() throws Exception { server.enqueue(new MockResponse() .setBodyDelay(Duration.ofSeconds(11).toMillis(), TimeUnit.MILLISECONDS) .setBody("Hello, world!")); OkHttpClient client = OkHttpClients.withStableUris( ClientConfiguration.builder() .from(createTestConfig(url)) .connectTimeout(Duration.ZERO) .readTimeout(Duration.ZERO) .writeTimeout(Duration.ZERO) // want to allow unlimited time for uploads .maxNumRetries(0) .backoffSlotSize(Duration.ofMillis(10)) .build(), AGENT, hostEventsSink, OkHttpClientsTest.class); Request request = new Request.Builder() .url(url) .build(); Response synchronousCall = client.newCall(request).execute(); assertThat(synchronousCall.body().string()).isEqualTo("Hello, world!"); }
@Test public void doesNotHangIfManyCallsResultInExceptions() throws Exception { int maxRetries = OkHttpClients.NUM_SCHEDULING_THREADS * 2; for (int i = 0; i <= maxRetries; i++) { server.enqueue(new MockResponse().setResponseCode(503)); } Call call = createRetryingClient(maxRetries, Duration.ofMillis(2) /* backoff slot size */) .newCall(new Request.Builder().url(url).build()); assertThatThrownBy(call::execute).isInstanceOf(IOException.class); }
private OkHttpClient createRetryingClient(int maxNumRetries, Duration backoffSlotSize, String... urls) { return OkHttpClients.withStableUris( ClientConfiguration.builder() .from(createTestConfig(urls)) .maxNumRetries(maxNumRetries) .backoffSlotSize(backoffSlotSize) .build(), AGENT, hostEventsSink, OkHttpClientsTest.class); } }
@Test(timeout = 10_000) public void handlesInterruptedThreads() throws Exception { server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); OkHttpClient client = createRetryingClient(0); Thread thread = new Thread(() -> { try { client.newCall(new Request.Builder().url(url).build()).execute(); } catch (IOException e) { // nothing } }); thread.start(); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); thread.interrupt(); thread.join(); }
@Test public void unsuccessfulCallDoesNotInvokeSuccessHandler() throws Exception { server.shutdown(); Call call = createRetryingClient(0).newCall(new Request.Builder().url(url).build()); Semaphore failureHandlerExecuted = new Semaphore(0); Semaphore successHandlerExecuted = new Semaphore(0); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException ioException) { failureHandlerExecuted.release(); } @Override public void onResponse(Call call, Response response) throws IOException { successHandlerExecuted.release(); // should never happen } }); assertThat(successHandlerExecuted.tryAcquire(100, TimeUnit.MILLISECONDS)) .as("onSuccess was executed").isFalse(); assertThat(failureHandlerExecuted.tryAcquire(100, TimeUnit.MILLISECONDS)).isTrue(); }
@Test public void handlesRetryOther_doesNotRedirectInfinitelyOften() throws Exception { // Note that RemotingOkHttpClient.MAX_NUM_RELOCATIONS = 20 for (int i = 0; i < 21; ++i) { server.enqueue(new MockResponse().setResponseCode(308).addHeader(HttpHeaders.LOCATION, url)); } Call call = createRetryingClient(1).newCall(new Request.Builder().url(url).build()); assertThatThrownBy(call::execute) .isInstanceOf(IOException.class) .hasMessage("Exceeded the maximum number of allowed redirects for initial URL: %s/", url); assertThat(server.getRequestCount()).isEqualTo(21); }
@Test public void verifyIoExceptionMetricsAreRegistered() { Call call = createRetryingClient(0, "http://bogus").newCall(new Request.Builder().url("http://bogus").build()); assertThatExceptionOfType(IOException.class) .isThrownBy(call::execute); List<HostMetrics> hostMetrics = hostEventsSink.getMetrics().stream() .filter(metrics -> metrics.hostname().equals("bogus")) .filter(metrics -> metrics.serviceName().equals("OkHttpClientsTest")) .collect(Collectors.toList()); HostMetrics actualMetrics = Iterables.getOnlyElement(hostMetrics); assertThat(actualMetrics.getIoExceptions().getCount()).isEqualTo(1); }
@Test public void handlesQos_503FailsOverToAnotherUrl() throws Exception { server.enqueue(new MockResponse().setResponseCode(503)); server2.enqueue(new MockResponse().setBody("foo")); server2.enqueue(new MockResponse().setBody("bar")); OkHttpClient client = createRetryingClient(1, url, url2); Call call = client.newCall(new Request.Builder().url(url).build()); assertThat(call.execute().body().string()).isEqualTo("foo"); Call call2 = client.newCall(new Request.Builder().url(url).build()); assertThat(call2.execute().body().string()).isEqualTo("bar"); assertThat(server.getRequestCount()).isEqualTo(1); assertThat(server2.getRequestCount()).isEqualTo(2); }
@Test public void successfulCallDoesNotInvokeFailureHandler() throws Exception { server.enqueue(new MockResponse().setBody("pong")); Call call = createRetryingClient(0).newCall(new Request.Builder().url(url).build()); Semaphore failureHandlerExecuted = new Semaphore(0); Semaphore successHandlerExecuted = new Semaphore(0); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException ioException) { failureHandlerExecuted.release(); // should never happen } @Override public void onResponse(Call call, Response response) throws IOException { successHandlerExecuted.release(); } }); assertThat(successHandlerExecuted.tryAcquire(500, TimeUnit.MILLISECONDS)).isTrue(); assertThat(failureHandlerExecuted.tryAcquire(500, TimeUnit.MILLISECONDS)) .as("onFailure was executed").isFalse(); }
@Test public void handlesIoExceptions_obeysMaxNumRetries() throws Exception { server.shutdown(); server2.shutdown(); server3.enqueue(new MockResponse().setResponseCode(200).setBody("foo")); OkHttpClient client = createRetryingClient(1, url, url2, url3); Call call = client.newCall(new Request.Builder().url(url + "/foo?bar").build()); assertThatThrownBy(call::execute) .isInstanceOf(IOException.class) .hasMessage("Failed to complete the request due to an IOException"); assertThat(server3.getRequestCount()).isEqualTo(0); }
@Test public void verifyResponseMetricsAreRegistered() throws IOException { server.enqueue(new MockResponse().setBody("pong")); createRetryingClient(1).newCall(new Request.Builder().url(url).build()).execute(); List<HostMetrics> hostMetrics = hostEventsSink.getMetrics().stream() .filter(metrics -> metrics.hostname().equals("localhost")) .filter(metrics -> metrics.serviceName().equals("OkHttpClientsTest")) .filter(metrics -> metrics.port() == server.getPort()) .collect(Collectors.toList()); HostMetrics actualMetrics = Iterables.getOnlyElement(hostMetrics); assertThat(actualMetrics.get2xx().getCount()).isGreaterThanOrEqualTo(1); }
@Test public void doesNotShareBackoffStateBetweenDifferentCalls() throws Exception { OkHttpClient client = createRetryingClient(1); server.enqueue(new MockResponse().setResponseCode(503)); server.enqueue(new MockResponse().setBody("pong")); Call call = client.newCall(new Request.Builder().url(url).build()); assertThat(call.execute().body().string()).isEqualTo("pong"); // The following call would fail if OkHttpClients.create() constructed clients that share backoff state. server.enqueue(new MockResponse().setResponseCode(503)); server.enqueue(new MockResponse().setBody("pong")); call = client.newCall(new Request.Builder().url(url).build()); assertThat(call.execute().body().string()).isEqualTo("pong"); assertThat(server.getRequestCount()).isEqualTo(4 /* two from each call */); }
@Test public void handlesIoExceptions_retriesOtherServers() throws Exception { server.shutdown(); server2.shutdown(); server3.enqueue(new MockResponse().setResponseCode(200).setBody("foo")); OkHttpClient client = createRetryingClient(2, url, url2, url3); Call call = client.newCall(new Request.Builder().url(url + "/foo?bar").build()); assertThat(call.execute().body().string()).isEqualTo("foo"); assertThat(server3.takeRequest().getPath()).isEqualTo("/foo?bar"); }
@Test public void handlesSuccessfulResponseCodesWithSuccessHandler() throws Exception { // Not testing HTTP 100 status code, because it is special, see: // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100 for (int code : new int[] {101, 200, 204}) { server.enqueue(new MockResponse().setResponseCode(code)); Call call = createRetryingClient(0).newCall(new Request.Builder().url(url).build()); CountDownLatch wasSuccessful = new CountDownLatch(1); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException exception) {} @Override public void onResponse(Call call, Response response) throws IOException { if (response.code() == code) { wasSuccessful.countDown(); } } }); assertThat(wasSuccessful.await(500, TimeUnit.MILLISECONDS)).as("Expected code: " + code).isTrue(); } }