/* * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights * Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is * distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either * express or implied. See the License for the specific language * governing * permissions and limitations under the License. */ package com.amazonaws.http.timers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.IOException; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.amazonaws.http.apache.client.impl.ApacheHttpClientFactory; import com.amazonaws.http.apache.client.impl.ConnectionManagerAwareHttpClient; import com.amazonaws.http.response.ErrorDuringUnmarshallingResponseHandler; import com.amazonaws.http.settings.HttpClientSettings; import org.apache.http.HttpEntity; import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; import org.apache.http.protocol.HttpContext; import com.amazonaws.ClientConfiguration; import com.amazonaws.Request; import com.amazonaws.http.AmazonHttpClient; import com.amazonaws.http.ExecutionContext; import com.amazonaws.http.client.HttpClientFactory; import com.amazonaws.http.HttpMethodName; import com.amazonaws.http.request.EmptyHttpRequest; import com.amazonaws.http.response.HttpResponseProxy; import com.amazonaws.http.response.NullErrorResponseHandler; import com.amazonaws.http.response.NullResponseHandler; import com.amazonaws.http.timers.client.ClientExecutionTimer; import com.amazonaws.http.timers.request.HttpRequestTimer; /** * Useful asserts and utilities for verifying behavior or the client execution timeout and request * timeout features */ public class ClientExecutionAndRequestTimerTestUtils { /** * Can take a little bit for ScheduledThreadPoolExecutor to update it's internal state */ private static final int WAIT_BEFORE_ASSERT_ON_EXECUTOR = 500; /** * Assert that the executor backing {@link ClientExecutionTimer} was never created or used * * @param requestTimer */ public static void assertRequestTimerExecutorNotCreated(HttpRequestTimer requestTimer) { assertNull(requestTimer.getExecutor()); } /** * Assert that the executor backing {@link ClientExecutionTimer} was never created or used * * @param clientExecutionTimer */ public static void assertClientExecutionTimerExecutorNotCreated(ClientExecutionTimer clientExecutionTimer) { assertNull(clientExecutionTimer.getExecutor()); } /** * Assert response was buffered into memory to enforce the timeout on both connection * established and reading of content * * @param responseProxy * Must by a spied {@link HttpResponseProxy} */ public static void assertResponseIsBuffered(HttpResponseProxy responseProxy) { verify(responseProxy).setEntity(any(BufferedHttpEntity.class)); } /** * Assert response was NOT buffered into memory as should be the case when client execution and * request timeouts are disabled or an operation is streaming and it's content must be left open * * @param responseProxy * Must by a spied {@link HttpResponseProxy} */ public static void assertResponseWasNotBuffered(HttpResponseProxy responseProxy) { verify(responseProxy, never()).setEntity(any(BufferedHttpEntity.class)); } /** * Waits until a little after the thread pools keep alive time and then asserts that all thre * * @param timerExecutor * Executor used by timer implementation * @throws InterruptedException */ public static void assertCoreThreadsShutDownAfterBeingIdle(ScheduledThreadPoolExecutor timerExecutor) { try { Thread.sleep(timerExecutor.getKeepAliveTime(TimeUnit.MILLISECONDS) + 1000); } catch (InterruptedException ignored) { } assertEquals(0, timerExecutor.getPoolSize()); } /** * If the request completes successfully then the timer task should be canceled and should be * removed from the thread pool to prevent build up of canceled tasks * * @param timerExecutor * Executor used by timer implementation */ public static void assertCanceledTasksRemoved(ScheduledThreadPoolExecutor timerExecutor) { waitBeforeAssertOnExecutor(); assertEquals(0, timerExecutor.getQueue().size()); } /** * Asserts the timer never went off (I.E. no timeout was exceeded and no timer task was * executed) * * @param timerExecutor * Executor used by timer implementation */ public static void assertTimerNeverTriggered(ScheduledThreadPoolExecutor timerExecutor) { assertNumberOfTasksTriggered(timerExecutor, 0); } public static void assertNumberOfTasksTriggered(ClientExecutionTimer clientExecutionTimer, int expectedNumberOfTasks) { assertNumberOfTasksTriggered(clientExecutionTimer.getExecutor(), expectedNumberOfTasks); } public static void assertNumberOfTasksTriggered(HttpRequestTimer requestTimer, int expectedNumberOfTasks) { assertNumberOfTasksTriggered(requestTimer.getExecutor(), expectedNumberOfTasks); } private static void assertNumberOfTasksTriggered(ScheduledThreadPoolExecutor timerExecutor, int expectedNumberOfTasks) { waitBeforeAssertOnExecutor(); assertEquals(expectedNumberOfTasks, timerExecutor.getCompletedTaskCount()); } /** * Creates Apache {@link HttpClient} spy * * @param config * {@link ClientConfiguration} for {@link HttpClientFactory} * @return Real implementation of {@link HttpClient} with ability to verify method calls or * partially mock */ public static ConnectionManagerAwareHttpClient createRawHttpClientSpy(ClientConfiguration config) { HttpClientFactory<ConnectionManagerAwareHttpClient> httpClientFactory = new ApacheHttpClientFactory(); return spy(httpClientFactory.create(HttpClientSettings.adapt(config, false))); } /** * Creates Apache {@link HttpResponseProxy} spy * * @return Real implementation of {@link HttpResponseProxy} with ability to verify method calls * or partially mock */ public static HttpResponseProxy createHttpResponseProxySpy() throws IOException { StringEntity entity = new StringEntity("mock response body"); HttpResponseProxy responseProxy = spy(createHttpResponseProxy(entity)); doReturn(entity).when(responseProxy).getEntity(); return responseProxy; } /** * Creates Apache {@link HttpResponseProxy} with a null entity */ public static HttpResponseProxy createHttpHeadResponseProxy() throws IOException { return createHttpResponseProxy(null); } private static HttpResponseProxy createHttpResponseProxy(HttpEntity entity) { ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); BasicStatusLine statusLine = new BasicStatusLine(protocolVersion, 200, "mock response"); BasicHttpResponse response = new BasicHttpResponse(statusLine); response.setEntity(entity); return new HttpResponseProxy(response); } public static Request<?> createMockGetRequest() { String localhostEndpoint = "http://localhost:0"; return new EmptyHttpRequest(localhostEndpoint, HttpMethodName.GET); } public static Request<?> createMockHeadRequest() { String localhostEndpoint = "http://localhost:0"; return new EmptyHttpRequest(localhostEndpoint, HttpMethodName.HEAD); } /** * Execute the request with a dummy response handler and error response handler */ public static void execute(AmazonHttpClient httpClient, Request<?> request) { httpClient.requestExecutionBuilder() .request(request) .errorResponseHandler(new NullErrorResponseHandler()) .execute(new ErrorDuringUnmarshallingResponseHandler()); } public static void assertNumberOfRetries(HttpClient spyClient, int expectedNumberOfRequests) { try { verify(spyClient, times(expectedNumberOfRequests)).execute(any(HttpRequestBase.class), any(HttpContext.class)); } catch (Exception e) { throw new RuntimeException(e); } } private static void waitBeforeAssertOnExecutor() { try { Thread.sleep(WAIT_BEFORE_ASSERT_ON_EXECUTOR); } catch (InterruptedException ignored) { } } public static void interruptCurrentThreadAfterDelay(final long delay) { final Thread currentThread = Thread.currentThread(); new Thread() { public void run() { try { Thread.sleep(delay); currentThread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } }