/*
* Copyright 2010-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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.handlers.HandlerContextKey;
import com.amazonaws.http.apache.client.impl.ConnectionManagerAwareHttpClient;
import com.amazonaws.http.apache.request.impl.ApacheHttpRequestFactory;
import com.amazonaws.http.request.HttpRequestFactory;
import com.amazonaws.http.settings.HttpClientSettings;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HttpContext;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonWebServiceResponse;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.DefaultRequest;
import com.amazonaws.Request;
import static org.junit.Assert.assertEquals;
public class AmazonHttpClientTest {
private final String SERVER_NAME = "testsvc";
private final String URI_NAME = "http://testsvc.region.amazonaws.com";
private ConnectionManagerAwareHttpClient httpClient;
private AmazonHttpClient client;
@Before
public void setUp() {
ClientConfiguration config = new ClientConfiguration();
httpClient = EasyMock.createMock(ConnectionManagerAwareHttpClient.class);
EasyMock.replay(httpClient);
client = new AmazonHttpClient(config, httpClient, null);
}
@Test
public void testRetryIOExceptionFromExecute() throws IOException {
IOException exception = new IOException("BOOM");
EasyMock.reset(httpClient);
EasyMock
.expect(httpClient.getConnectionManager())
.andReturn(null)
.anyTimes();
EasyMock
.expect(httpClient.execute(EasyMock.<HttpUriRequest>anyObject(),
EasyMock.<HttpContext>anyObject()))
.andThrow(exception)
.times(4);
EasyMock.replay(httpClient);
ExecutionContext context = new ExecutionContext();
Request<?> request = new DefaultRequest<Object>("testsvc");
request.setEndpoint(java.net.URI.create(
"http://testsvc.region.amazonaws.com"));
request.setContent(new ByteArrayInputStream(new byte[0]));
try {
client.requestExecutionBuilder().request(request).executionContext(context).execute();
Assert.fail("No exception when request repeatedly fails!");
} catch (AmazonClientException e) {
Assert.assertSame(exception, e.getCause());
}
// Verify that we called execute 4 times.
EasyMock.verify(httpClient);
}
@Test
public void testRetryIOExceptionFromHandler() throws Exception {
final IOException exception = new IOException("BOOM");
HttpResponseHandler<AmazonWebServiceResponse<Object>> handler =
EasyMock.createMock(HttpResponseHandler.class);
EasyMock
.expect(handler.needsConnectionLeftOpen())
.andReturn(false)
.anyTimes();
EasyMock
.expect(handler.handle(EasyMock.<HttpResponse>anyObject()))
.andThrow(exception)
.times(4);
EasyMock.replay(handler);
BasicHttpResponse response = createBasicHttpResponse();
EasyMock.reset(httpClient);
EasyMock
.expect(httpClient.getConnectionManager())
.andReturn(null)
.anyTimes();
EasyMock
.expect(httpClient.execute(EasyMock.<HttpUriRequest>anyObject(),
EasyMock.<HttpContext>anyObject()))
.andReturn(response)
.times(4);
EasyMock.replay(httpClient);
ExecutionContext context = new ExecutionContext();
Request<?> request = new DefaultRequest<Object>(null, "testsvc");
request.setEndpoint(java.net.URI.create(
"http://testsvc.region.amazonaws.com"));
request.setContent(new java.io.ByteArrayInputStream(new byte[0]));
try {
client.requestExecutionBuilder().request(request).executionContext(context).execute(handler);
Assert.fail("No exception when request repeatedly fails!");
} catch (AmazonClientException e) {
Assert.assertSame(exception, e.getCause());
}
// Verify that we called execute 4 times.
EasyMock.verify(httpClient);
}
@Test
public void testUseExpectContinueTrue() throws IOException {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, true);
ClientConfiguration clientConfiguration = new ClientConfiguration().withUseExpectContinue(true);
HttpRequestFactory<HttpRequestBase> httpRequestFactory = new ApacheHttpRequestFactory();
HttpRequestBase httpRequest = httpRequestFactory.create(request, HttpClientSettings.adapt(clientConfiguration));
Assert.assertNotNull(httpRequest);
Assert.assertTrue(httpRequest.getConfig().isExpectContinueEnabled());
}
@Test
public void testUseExpectContinueFalse() throws IOException {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, true);
ClientConfiguration clientConfiguration = new ClientConfiguration().withUseExpectContinue(false);
HttpRequestFactory<HttpRequestBase> httpRequestFactory = new ApacheHttpRequestFactory();
HttpRequestBase httpRequest = httpRequestFactory.create(request, HttpClientSettings.adapt(clientConfiguration));
Assert.assertNotNull(httpRequest);
Assert.assertFalse(httpRequest.getConfig().isExpectContinueEnabled());
}
@Test
public void testPutRetryNoCL() throws Exception {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, false);
testRetries(request, 100);
}
@Test
public void testPostRetryNoCL() throws Exception {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.POST, URI_NAME, false);
testRetries(request, 100);
}
@Test
public void testPutRetryCL() throws Exception {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, true);
testRetries(request, 100);
}
@Test
public void testPostRetryCL() throws Exception {
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.POST, URI_NAME, true);
testRetries(request, 100);
}
@Test
public void testUserAgentPrefixAndSuffixAreAdded() throws Exception {
String prefix = "somePrefix", suffix = "someSuffix";
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, true);
HttpResponseHandler<AmazonWebServiceResponse<Object>> handler = createStubResponseHandler();
EasyMock.replay(handler);
ClientConfiguration config =
new ClientConfiguration().withUserAgentPrefix(prefix).withUserAgentSuffix(suffix);
Capture<HttpRequestBase> capturedRequest = new Capture<HttpRequestBase>();
EasyMock.reset(httpClient);
EasyMock
.expect(httpClient.execute(
EasyMock.capture(capturedRequest), EasyMock.<HttpContext>anyObject()))
.andReturn(createBasicHttpResponse())
.once();
EasyMock.replay(httpClient);
AmazonHttpClient client = new AmazonHttpClient(config, httpClient, null);
client.requestExecutionBuilder().request(request).execute(handler);
String userAgent = capturedRequest.getValue().getFirstHeader("User-Agent").getValue();
Assert.assertTrue(userAgent.startsWith(prefix));
Assert.assertTrue(userAgent.endsWith(suffix));
}
@Test
public void testCredentialsSetInRequestContext() throws Exception {
EasyMock.reset(httpClient);
EasyMock
.expect(httpClient.execute(EasyMock.<HttpRequestBase>anyObject(), EasyMock.<HttpContext>anyObject()))
.andReturn(createBasicHttpResponse())
.once();
EasyMock.replay(httpClient);
AmazonHttpClient client = new AmazonHttpClient(new ClientConfiguration(), httpClient, null);
final BasicAWSCredentials credentials = new BasicAWSCredentials("foo", "bar");
AWSCredentialsProvider credentialsProvider = EasyMock.createMock(AWSCredentialsProvider.class);
EasyMock.expect(credentialsProvider.getCredentials())
.andReturn(credentials)
.anyTimes();
EasyMock.replay(credentialsProvider);
ExecutionContext executionContext = new ExecutionContext();
executionContext.setCredentialsProvider(credentialsProvider);
Request<?> request = mockRequest(SERVER_NAME, HttpMethodName.PUT, URI_NAME, true);
HttpResponseHandler<AmazonWebServiceResponse<Object>> handler = createStubResponseHandler();
EasyMock.replay(handler);
client.execute(request, handler, null, executionContext);
assertEquals(credentials, request.getHandlerContext(HandlerContextKey.AWS_CREDENTIALS));
}
private BasicHttpResponse createBasicHttpResponse() {
BasicHttpEntity entity = new BasicHttpEntity();
entity.setContent(new ByteArrayInputStream(new byte[0]));
BasicHttpResponse response = new BasicHttpResponse(
new ProtocolVersion("http", 1, 1),
200,
"OK");
response.setEntity(entity);
return response;
}
private HttpResponseHandler<AmazonWebServiceResponse<Object>> createStubResponseHandler() throws Exception {
HttpResponseHandler<AmazonWebServiceResponse<Object>> handler =
EasyMock.createMock(HttpResponseHandler.class);
AmazonWebServiceResponse<Object> response = new AmazonWebServiceResponse<Object>();
EasyMock
.expect(handler.needsConnectionLeftOpen())
.andReturn(false)
.anyTimes();
EasyMock
.expect(handler.handle(EasyMock.<HttpResponse>anyObject()))
.andReturn(response)
.anyTimes();
return handler;
}
private void testRetries(Request<?> request, int contentLength)
throws IOException {
ExecutionContext context = new ExecutionContext();
mockFailure(contentLength);
try {
client.requestExecutionBuilder().request(request).executionContext(context).execute();
Assert.fail("Expected AmazonClientException");
} catch (AmazonClientException e) {
}
}
private void mockFailure(final int contentLength) throws IOException {
EasyMock.reset(httpClient);
EasyMock
.expect(httpClient.getConnectionManager())
.andReturn(null)
.anyTimes();
for (int i = 0; i < 4; ++i) {
EasyMock
.expect(httpClient.execute(
EasyMock.<HttpUriRequest>anyObject(),
EasyMock.<HttpContext>anyObject()))
.andAnswer(new IAnswer<org.apache.http.HttpResponse>() {
@Override
public org.apache.http.HttpResponse answer()
throws Throwable {
HttpEntityEnclosingRequestBase request =
(HttpEntityEnclosingRequestBase)
EasyMock.getCurrentArguments()[0];
InputStream stream = request.getEntity().getContent();
int len = 0;
while (true) {
int b = stream.read(new byte[1024]);
if (b == -1) {
break;
}
len += b;
}
assertEquals(contentLength, len);
throw new IOException("BOOM");
}
});
}
EasyMock.replay(httpClient);
}
private Request<?> mockRequest(String serverName, HttpMethodName methodName, String uri, boolean hasCL) {
Request<?> request = new DefaultRequest<Object>(null, serverName);
request.setHttpMethod(methodName);
request.setContent(new ByteArrayInputStream(new byte[100]));
request.setEndpoint(URI.create(uri));
if (hasCL) request.addHeader("Content-Length", "100");
return request;
}
}