@Test(timeout = TEST_TIMEOUT) public void requestTimeoutEnabled_ServerRespondsWithRetryableError_RetriesUpToLimitThenThrowsServerException() throws IOException { int maxRetries = 2; ClientConfiguration config = new ClientConfiguration().withRequestTimeout(25 * 1000) .withClientExecutionTimeout(25 * 1000).withMaxErrorRetry(maxRetries); HttpClientFactory<ConnectionManagerAwareHttpClient> httpClientFactory = new ApacheHttpClientFactory(); ConnectionManagerAwareHttpClient rawHttpClient = spy(httpClientFactory.create(HttpClientSettings.adapt(config))); httpClient = new AmazonHttpClient(config, rawHttpClient, null); try { httpClient.execute(newGetRequest(), new ErrorDuringUnmarshallingResponseHandler(), new NullErrorResponseHandler(), new ExecutionContext()); fail("Exception expected"); } catch (AmazonServiceException e) { assertEquals(e.getStatusCode(), STATUS_CODE); int expectedNumberOfRequests = 1 + maxRetries; assertNumberOfRetries(rawHttpClient, expectedNumberOfRequests); assertNumberOfTasksTriggered(httpClient.getHttpRequestTimer(), 0); assertNumberOfTasksTriggered(httpClient.getClientExecutionTimer(), 0); } }
/** * Verifies the request is actually retried for the expected times. */ private void testActualRetries(int expectedRetryAttempts) { testedClient = new AmazonHttpClient(clientConfiguration); injectMockHttpClient(testedClient, new ReturnServiceErrorHttpClient(500, "fake 500 service error")); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); try { testedClient.requestExecutionBuilder() .request(getSampleRequestWithRepeatableContent(originalRequest)) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonServiceException is expected."); } catch (AmazonServiceException ase) {} RetryTestUtils.assertExpectedRetryCount(expectedRetryAttempts, context); }
private <Y> Request<Y> prepareRequest(Request<Y> request, ExecutionContext executionContext, boolean signRequest) { request.setEndpoint(endpoint); request.setTimeOffset(timeOffset); AWSCredentials credentials = awsCredentialsProvider.getCredentials(); AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); if (originalRequest != null && originalRequest.getRequestCredentials() != null) { credentials = originalRequest.getRequestCredentials(); } if (signRequest) { // expiration date is not currently supported on service side, but presignRequest method requires // this argument so one with default value is provided. Date expirationDate = DateTime.now(DateTimeZone.UTC) .plusMinutes(DEFAULT_GET_REQUEST_EXPIRATION_MINUTES).toDate(); signer.presignRequest(request, credentials, expirationDate); } else { executionContext.setSigner(signer); executionContext.setCredentials(credentials); } return request; }
private <T> T invoke(HttpMethodName httpMethodName, String resourcePath, Object representation, HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler) throws AmazonClientException { ExecutionContext executionContext = createExecutionContext(); AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.RequestMarshallTime.name()); Request request = buildRequest(httpMethodName, resourcePath, representation); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.RequestMarshallTime.name()); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.CredentialsRequestTime.name()); AWSCredentials credentials = awsCredentialsProvider.getCredentials(); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.CredentialsRequestTime.name()); executionContext.setCredentials(credentials); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.ClientExecuteTime.name()); Response<T> response = client.execute(request, responseHandler, errorResponseHandler, executionContext); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.ClientExecuteTime.name()); awsRequestMetrics.log(); return response.getAwsResponse(); }
private GenericApiGatewayResponse execute(HttpMethodName method, String resourcePath, Map<String, String> headers, InputStream content) { final ExecutionContext executionContext = buildExecutionContext(); DefaultRequest request = new DefaultRequest(API_GATEWAY_SERVICE_NAME); request.setHttpMethod(method); request.setContent(content); request.setEndpoint(this.endpoint); request.setResourcePath(resourcePath); request.setHeaders(buildRequestHeaders(headers, apiKey)); return this.client.execute(request, responseHandler, errorResponseHandler, executionContext).getAwsResponse(); }
private ExecutionContext buildExecutionContext() { final ExecutionContext executionContext = ExecutionContext.builder().withSignerProvider( new DefaultSignerProvider(this, signer)).build(); executionContext.setCredentialsProvider(credentials); executionContext.setSigner(signer); return executionContext; }
protected ExecutionContext createExecutionContext(AmazonWebServiceRequest req, SignerProvider signerProvider) { boolean isMetricsEnabled = isRequestMetricsEnabled(req) || isProfilingEnabled(); return ExecutionContext.builder() .withRequestHandler2s(requestHandler2s) .withUseRequestMetrics(isMetricsEnabled) .withAwsClient(this) .withSignerProvider(signerProvider).build(); }
@Override public <Input, Output> Output execute( ClientExecutionParams<Input, Output> executionParams) { final Input input = executionParams.getInput(); ExecutionContext executionContext = createExecutionContext( executionParams.getRequestConfig()); AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.ClientExecuteTime); Request<Input> request = null; Response<Output> response = null; try { awsRequestMetrics.startEvent(AWSRequestMetrics.Field.RequestMarshallTime); try { request = executionParams.getMarshaller().marshall(input); request.setAWSRequestMetrics(awsRequestMetrics); } finally { awsRequestMetrics.endEvent(AWSRequestMetrics.Field.RequestMarshallTime); } response = invoke(request, executionParams.getRequestConfig(), executionContext, executionParams.getResponseHandler(), executionParams.getErrorResponseHandler()); return response.getAwsResponse(); } finally { endClientExecution(awsRequestMetrics, executionParams.getRequestConfig(), request, response); } }
private ExecutionContext createExecutionContext(RequestConfig requestConfig) { boolean isMetricsEnabled = isRequestMetricsEnabled(requestConfig); return ExecutionContext.builder() .withRequestHandler2s(requestHandler2s) .withUseRequestMetrics(isMetricsEnabled) .withSignerProvider(signerProvider) .build(); }
/** * Normal invoke with authentication. Credentials are required and may be overriden at the * request level. **/ private <Output, Input> Response<Output> invoke(Request<Input> request, RequestConfig requestConfig, ExecutionContext executionContext, HttpResponseHandler<Output> responseHandler, HttpResponseHandler<? extends SdkBaseException> errorResponseHandler) { executionContext.setCredentialsProvider(CredentialUtils.getCredentialsProvider( requestConfig, awsCredentialsProvider)); return doInvoke(request, requestConfig, executionContext, responseHandler, errorResponseHandler); }
/** * Invoke the request using the http client. Assumes credentials (or lack thereof) have been * configured in the ExecutionContext beforehand. **/ private <Output, Input> Response<Output> doInvoke(Request<Input> request, RequestConfig requestConfig, ExecutionContext executionContext, HttpResponseHandler<Output> responseHandler, HttpResponseHandler<? extends SdkBaseException> errorResponseHandler) { request.setEndpoint(endpoint); return client.requestExecutionBuilder() .request(request) .requestConfig(requestConfig) .executionContext(executionContext) .errorResponseHandler(errorResponseHandler) .execute(responseHandler); }
@Test(timeout = TEST_TIMEOUT, expected = ClientExecutionTimeoutException.class) public void clientExecutionTimeoutEnabled_SlowAfterErrorRequestHandler_ThrowsClientExecutionTimeoutException() throws Exception { httpClient = new AmazonHttpClient( new ClientConfiguration().withClientExecutionTimeout(CLIENT_EXECUTION_TIMEOUT)); List<RequestHandler2> requestHandlers = RequestHandlerTestUtils.buildRequestHandlerList( new SlowRequestHandler().withAfterErrorWaitInSeconds(SLOW_REQUEST_HANDLER_TIMEOUT)); httpClient.requestExecutionBuilder() .request(newGetRequest()) .errorResponseHandler(new NullErrorResponseHandler()) .executionContext(ExecutionContext.builder().withRequestHandler2s(requestHandlers).build()) .execute(); }
/** * Checks the RequestCount metric and compares it against the expected value. * @param expectedRetryAttempts number of expected retries * @param context request execution context */ public static void assertExpectedRetryCount(final int expectedRetryAttempts, final ExecutionContext context) { Assert.assertEquals( expectedRetryAttempts + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Perform this request. */ @Override public T perform() { final Response<T> rsp = new AmazonHttpClient(new ClientConfiguration()) .requestExecutionBuilder() .executionContext(new ExecutionContext(true)) .request(this.request) .errorResponseHandler(this.errHandler) .execute(this.respHandler); return rsp.getAwsResponse(); }
@Override public CreateSpeechResult createSpeech(CreateSpeechRequest createSpeechRequest) throws AmazonServiceException, AmazonClientException { ExecutionContext executionContext = createExecutionContext(createSpeechRequest); Request<CreateSpeechRequest> request = CreateSpeechRequestMarshallerFactory.getMarshaller( createSpeechRequest.getMethodType()).marshall(createSpeechRequest); CreateSpeechResultUnmarshaller unmarshaller = new CreateSpeechResultUnmarshaller(); StreamResponseHandler<CreateSpeechResult> responseHandler = new StreamResponseHandler<CreateSpeechResult>(unmarshaller); Response<CreateSpeechResult> response = invoke(request, responseHandler, executionContext); return response.getAwsResponse(); }
@Override public ListVoicesResult listVoices(ListVoicesRequest listVoicesRequest) throws AmazonServiceException, AmazonClientException { ExecutionContext executionContext = createExecutionContext(listVoicesRequest); Request<ListVoicesRequest> request = ListVoicesRequestMarshallerFactory.getMarshaller( listVoicesRequest.getMethodType()).marshall(listVoicesRequest); Unmarshaller<ListVoicesResult, JsonUnmarshallerContext> unmarshaller = new ListVoicesResultJsonUnmarshaller(); JsonResponseHandler<ListVoicesResult> responseHandler = new JsonResponseHandler<ListVoicesResult>(unmarshaller); Response<ListVoicesResult> response = invoke(request, responseHandler, executionContext); return response.getAwsResponse(); }
@Override public void deleteLexicon(DeleteLexiconRequest deleteLexiconRequest) throws AmazonServiceException, AmazonClientException { ExecutionContext executionContext = createExecutionContext(deleteLexiconRequest); DeleteLexiconRequestMarshaller marshaller = new DeleteLexiconPostRequestMarshaller(); Request<DeleteLexiconRequest> request = marshaller.marshall(deleteLexiconRequest); JsonResponseHandler<Void> responseHandler = new JsonResponseHandler<Void>(null); invoke(request, responseHandler, executionContext); }
@Override public GetLexiconResult getLexicon(GetLexiconRequest getLexiconRequest) throws AmazonServiceException, AmazonClientException { ExecutionContext executionContext = createExecutionContext(getLexiconRequest); GetLexiconRequestMarshaller marshaller = new GetLexiconPostRequestMarshaller(); Request<GetLexiconRequest> request = marshaller.marshall(getLexiconRequest); Unmarshaller<GetLexiconResult, JsonUnmarshallerContext> unmarshaller = new GetLexiconResultJsonUnmarshaller(); JsonResponseHandler<GetLexiconResult> responseHandler = new JsonResponseHandler<GetLexiconResult>(unmarshaller); Response<GetLexiconResult> response = invoke(request, responseHandler, executionContext); return response.getAwsResponse(); }
@Override public ListLexiconsResult listLexicons() { ListLexiconsRequest listLexiconsRequest = new ListLexiconsRequest(); ExecutionContext executionContext = createExecutionContext(listLexiconsRequest); ListLexiconsRequestMarshaller marshaller = new ListLexiconsPostRequestMarshaller(); Request<ListLexiconsRequest> request = marshaller.marshall(listLexiconsRequest); Unmarshaller<ListLexiconsResult, JsonUnmarshallerContext> unmarshaller = new ListLexiconsResultJsonUnmarshaller(); JsonResponseHandler<ListLexiconsResult> responseHandler = new JsonResponseHandler<ListLexiconsResult>(unmarshaller); Response<ListLexiconsResult> response = invoke(request, responseHandler, executionContext); return response.getAwsResponse(); }
@Override public void putLexicon(PutLexiconRequest putLexiconRequest) throws AmazonServiceException, AmazonClientException { ExecutionContext executionContext = createExecutionContext(putLexiconRequest); PutLexiconRequestMarshaller marshaller = new PutLexiconPostRequestMarshaller(); Request<PutLexiconRequest> request = marshaller.marshall(putLexiconRequest); JsonResponseHandler<Void> responseHandler = new JsonResponseHandler<Void>(null); invoke(request, responseHandler, executionContext); }
protected ExecutionContext createExecutionContext(AmazonWebServiceRequest req) { return createExecutionContext(req, signerProvider); }
protected final ExecutionContext createExecutionContext(Request<?> req) { return createExecutionContext(req.getOriginalRequest()); }
private ExecutionContext withHandlers(List<RequestHandler2> requestHandlers) { return ExecutionContext.builder().withRequestHandler2s(requestHandlers).build(); }
/** * Tests AmazonHttpClient's behavior upon simulated service exceptions when the * request payload is repeatable. */ @Test public void testServiceExceptionHandling() { int random500StatusCode = 500 + random.nextInt(100); String randomErrorCode = UUID.randomUUID().toString(); // A mock HttpClient that always returns the specified status and error code. injectMockHttpClient(testedClient, new ReturnServiceErrorHttpClient(random500StatusCode, randomErrorCode)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should keep retrying until it reaches the max retry limit and // throws the simulated ASE. AmazonServiceException expectedServiceException = null; try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonServiceException is expected."); } catch (AmazonServiceException ase) { // We should see the original service exception Assert.assertEquals(random500StatusCode, ase.getStatusCode()); Assert.assertEquals(randomErrorCode, ase.getErrorCode()); expectedServiceException = ase; } // Verifies that the correct information was passed into the RetryCondition and BackoffStrategy verifyExpectedContextData(retryCondition, originalRequest, expectedServiceException, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, originalRequest, expectedServiceException, EXPECTED_RETRY_COUNT); // We also want to check the RequestCount metric is correctly captured. Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated IOException during * executing the http request when the request payload is repeatable. */ @Test public void testIOExceptioinHandling() { // A mock HttpClient that always throws the specified IOException object IOException simulatedIOException = new IOException("fake IOException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedIOException)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should keep retrying until it reaches the max retry limit and // throws the an ACE containing the simulated IOException. AmazonClientException expectedClientException = null; try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (AmazonClientException ace) { Assert.assertTrue(simulatedIOException == ace.getCause()); expectedClientException = ace; } // Verifies that the correct information was passed into the RetryCondition and BackoffStrategy verifyExpectedContextData(retryCondition, originalRequest, expectedClientException, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, originalRequest, expectedClientException, EXPECTED_RETRY_COUNT); // We also want to check the RequestCount metric is correctly captured. Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated service exceptions when the * request payload is not repeatable. */ @Test public void testServiceExceptionHandlingWithNonRepeatableRequestContent() { int random500StatusCode = 500 + random.nextInt(100); String randomErrorCode = UUID.randomUUID().toString(); // A mock HttpClient that always returns the specified status and error code. injectMockHttpClient(testedClient, new ReturnServiceErrorHttpClient(random500StatusCode, randomErrorCode)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); // A non-repeatable request Request<?> testedNonRepeatableRequest = getSampleRequestWithNonRepeatableContent(originalRequest); // It should fail directly and throw the ASE, without consulting the // custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedNonRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonServiceException is expected."); } catch (AmazonServiceException ase) { Assert.assertEquals(random500StatusCode, ase.getStatusCode()); Assert.assertEquals(randomErrorCode, ase.getErrorCode()); } // Verifies that shouldRetry and calculateSleepTime were never called verifyExpectedContextData(retryCondition, null, null, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, null, null, EXPECTED_RETRY_COUNT); Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated IOException when the * request payload is not repeatable. */ @Test public void testIOExceptionHandlingWithNonRepeatableRequestContent() { // A mock HttpClient that always throws the specified IOException object IOException simulatedIOException = new IOException("fake IOException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedIOException)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); // A non-repeatable request Request<?> testedRepeatableRequest = getSampleRequestWithNonRepeatableContent(originalRequest); // It should fail directly and throw an ACE containing the simulated // IOException, without consulting the // custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (AmazonClientException ace) { Assert.assertTrue(simulatedIOException == ace.getCause()); } // Verifies that shouldRetry and calculateSleepTime are still called verifyExpectedContextData(retryCondition, null, null, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, null, null, EXPECTED_RETRY_COUNT); Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated RuntimeException (which * should be handled as an unexpected failure and not retried). */ @Test public void testUnexpectedFailureHandling() { // A mock HttpClient that always throws an NPE NullPointerException simulatedNPE = new NullPointerException("fake NullPointerException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedNPE)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should fail directly and throw the simulated NPE, without // consulting the custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (NullPointerException npe) { Assert.assertTrue(simulatedNPE == npe); } // Verifies that shouldRetry and calculateSleepTime were never called verifyExpectedContextData(retryCondition, null, null, 0); verifyExpectedContextData(backoffStrategy, null, null, 0); // The captured RequestCount should be 1 Assert.assertEquals( 1, context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
private <X, Y extends AmazonWebServiceRequest> Response<X> invoke( Request<Y> request, HttpResponseHandler<AmazonWebServiceResponse<X>> responseHandler, ExecutionContext executionContext) { JsonErrorResponseHandler errorResponseHandler = new JsonErrorResponseHandler(exceptionUnmarshallers); Response<X> result = client.execute( prepareRequest(request, executionContext), responseHandler, errorResponseHandler, executionContext); return result; }
private <Y> Request<Y> prepareRequest(Request<Y> request, ExecutionContext executionContext) { return prepareRequest(request, executionContext, false); }