/* * Copyright 2010-2015 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. * You may obtain a copy of the License at: * * http://aws.amazon.com/apache2.0 * * 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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonServiceException.ErrorType; import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.ClientConfiguration; import com.amazonaws.DefaultRequest; import com.amazonaws.Request; import com.amazonaws.RequestClientOptions; import com.amazonaws.Response; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AnonymousAWSCredentials; import com.amazonaws.auth.Signer; import com.amazonaws.handlers.CredentialsRequestHandler; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.internal.CRC32MismatchException; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.util.AWSRequestMetrics; import org.easymock.Capture; import org.easymock.EasyMock; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; public class AmazonHttpClientTest { private HttpClient httpClient; private AmazonHttpClient client; @Before public void setUp() { ClientConfiguration config = new ClientConfiguration(); httpClient = EasyMock.createMock(HttpClient.class); EasyMock.reset(httpClient); client = new AmazonHttpClient(config, httpClient); } @Test public void testTemporaryRedirect() throws IOException, URISyntaxException { HttpResponse redirectResponse = HttpResponse.builder().statusCode(307) .header("Location", "https://www.redirect.com").build(); HttpResponse successfulResponse = HttpResponse.builder().statusCode(200).content(null) .build(); final Request<?> request = new DefaultRequest<String>(new AmazonWebServiceRequest() { }, "TestService"); request.setHttpMethod(HttpMethodName.GET); request.setEndpoint(new URI("https://www.test.com")); request.setResourcePath("/test/table"); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { AmazonWebServiceResponse<String> awsResponse = new AmazonWebServiceResponse<String>(); awsResponse.setResult("Result"); return awsResponse; } @Override public boolean needsConnectionLeftOpen() { return false; } }; ExecutionContext ec = EasyMock.createMock(ExecutionContext.class); EasyMock.reset(httpClient, ec); final List<Boolean> signerCalled = new ArrayList<Boolean>(); EasyMock.expect(ec.getAwsRequestMetrics()).andReturn(new AWSRequestMetrics()).anyTimes(); EasyMock.expect(ec.getContextUserAgent()).andReturn("TestUserAgent").anyTimes(); EasyMock.expect(ec.getCredentials()).andReturn(new AnonymousAWSCredentials()); EasyMock.expect(ec.getSignerByURI(EasyMock.anyObject(URI.class))).andReturn(new Signer() { @Override public void sign(Request<?> requestToSign, AWSCredentials credentials) { assertSame(request, requestToSign); assertTrue(credentials instanceof AnonymousAWSCredentials); signerCalled.add(true); } }); EasyMock .expect(httpClient.execute(EasyMock.<HttpRequest> anyObject())) .andReturn(redirectResponse); Capture<HttpRequest> capture = new Capture<HttpRequest>(); EasyMock.expect(httpClient.execute(EasyMock.capture(capture))) .andReturn(successfulResponse); EasyMock.replay(httpClient, ec); Response<String> response = client.executeHelper(request, responseHandler, null, ec); assertEquals(response.getAwsResponse(), "Result"); assertEquals(signerCalled.size(), 2); assertTrue(signerCalled.get(0)); assertTrue(signerCalled.get(1)); assertEquals(capture.getValue().getUri().toString(), "https://www.redirect.com/"); EasyMock.verify(httpClient, ec); } @Test public void testRetryIOExceptionFromExecute() throws IOException { IOException exception = new IOException("BOOM"); EasyMock .expect(httpClient.execute(EasyMock.<HttpRequest> 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.addHeader(HttpHeader.CONTENT_LENGTH, "0"); request.setContent(new ByteArrayInputStream(new byte[0])); try { client.execute(request, null, null, context); 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); HttpResponse response = HttpResponse.builder() .content(new ByteArrayInputStream(new byte[0])) .statusCode(200) .statusText("OK") .build(); EasyMock .expect(httpClient.execute(EasyMock.<HttpRequest> anyObject())) .andReturn(response) .times(4); EasyMock.replay(handler, httpClient); ExecutionContext context = new ExecutionContext(); Request<?> request = new DefaultRequest<Object>("testsvc"); request.setEndpoint(java.net.URI.create( "http://testsvc.region.amazonaws.com")); request.addHeader(HttpHeader.CONTENT_LENGTH, "0"); request.setContent(new java.io.ByteArrayInputStream(new byte[0])); try { client.execute(request, handler, null, context); 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 testHandleResponse() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(200).build(); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); AmazonWebServiceResponse<String> awsResponse = new AmazonWebServiceResponse<String>(); awsResponse.setResult("Result"); return awsResponse; } @Override public boolean needsConnectionLeftOpen() { return false; } }; assertEquals("Result", client.handleResponse(request, responseHandler, httpResponse, new ExecutionContext())); } @Test(expected = RuntimeException.class) public void testHandleResponseWithNullResult() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(200).build(); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); return null; } @Override public boolean needsConnectionLeftOpen() { return false; } }; client.handleResponse(request, responseHandler, httpResponse, new ExecutionContext()); } @Test(expected = CRC32MismatchException.class) public void testHandleResponseThrowsCRC32MisMatch() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(200).build(); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new CRC32MismatchException("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; client.handleResponse(request, responseHandler, httpResponse, new ExecutionContext()); } @Test(expected = IOException.class) public void testHandleResponseThrowsIOException() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(200).build(); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new IOException("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; client.handleResponse(request, responseHandler, httpResponse, new ExecutionContext()); } @Test(expected = Exception.class) public void testHandleResponseThrowsGenericException() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(200).build(); HttpResponseHandler<AmazonWebServiceResponse<String>> responseHandler = new HttpResponseHandler<AmazonWebServiceResponse<String>>() { @Override public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new Exception("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; client.handleResponse(request, responseHandler, httpResponse, new ExecutionContext()); } @Test public void testHandleErrorResponse() throws IOException { final Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(400).build(); HttpResponseHandler<AmazonServiceException> errorResponseHandler = new HttpResponseHandler<AmazonServiceException>() { @Override public AmazonServiceException handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); AmazonServiceException ase = new AmazonServiceException("Test"); ase.setErrorCode("TestError"); ase.setErrorType(ErrorType.Service); ase.setServiceName(request.getServiceName()); ase.setStatusCode(response.getStatusCode()); return ase; } @Override public boolean needsConnectionLeftOpen() { return false; } }; AmazonServiceException e = client.handleErrorResponse(request, errorResponseHandler, httpResponse); assertEquals(e.getStatusCode(), 400); assertEquals(e.getErrorCode(), "TestError"); assertEquals(e.getErrorType(), ErrorType.Service); assertEquals(e.getServiceName(), "ServiceName"); } @Test public void testHandleErrorResponseHandlerFailsWith413() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(413).build(); HttpResponseHandler<AmazonServiceException> errorResponseHandler = new HttpResponseHandler<AmazonServiceException>() { @Override public AmazonServiceException handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new Exception("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; AmazonServiceException e = client.handleErrorResponse(request, errorResponseHandler, httpResponse); assertEquals(e.getStatusCode(), 413); assertEquals(e.getErrorCode(), "Request entity too large"); assertEquals(e.getErrorType(), ErrorType.Client); assertEquals(e.getServiceName(), "ServiceName"); } @Test public void testHandleErrorResponseHandlerFailsWith503() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder() .statusText("Service unavailable") .statusCode(503).build(); HttpResponseHandler<AmazonServiceException> errorResponseHandler = new HttpResponseHandler<AmazonServiceException>() { @Override public AmazonServiceException handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new Exception("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; AmazonServiceException e = client.handleErrorResponse(request, errorResponseHandler, httpResponse); assertEquals(e.getStatusCode(), 503); assertEquals(e.getErrorCode(), "Service unavailable"); assertEquals(e.getErrorType(), ErrorType.Service); assertEquals(e.getServiceName(), "ServiceName"); } @Test(expected = AmazonClientException.class) public void testHandleErrorResponseHandlerFailsWithUnknownException() throws IOException { Request<?> request = new DefaultRequest<String>("ServiceName"); final HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(400).build(); HttpResponseHandler<AmazonServiceException> errorResponseHandler = new HttpResponseHandler<AmazonServiceException>() { @Override public AmazonServiceException handle(HttpResponse response) throws Exception { assertSame(response, httpResponse); throw new Exception("test"); } @Override public boolean needsConnectionLeftOpen() { return false; } }; AmazonServiceException e = client.handleErrorResponse(request, errorResponseHandler, httpResponse); } @Test public void testClockskewOffset() { HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .header("Date", "Sat, 06 Nov 2004 08:49:37 GMT") .statusCode(400).build(); AmazonServiceException ase = new AmazonServiceException("ClockSkew"); // assert date is > 10 years int offset = client.parseClockSkewOffset(httpResponse, ase); assertTrue(offset > 315400000); } @Test public void testClockskewOffsetWithDateInBodyOfException() { HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .statusCode(400).build(); AmazonServiceException ase = new AmazonServiceException("(20041106T084937Z - 15)"); // assert date is > 10 years int offset = client.parseClockSkewOffset(httpResponse, ase); assertTrue(offset > 315400000); } @Test public void testClockskewOffsetWithBogusDateAsssumesOffsetIsZero() { HttpResponse httpResponse = new HttpResponse.Builder().statusText("TestResponse") .header("Date", "Sat, 064 Jann 20044 08:49:37 GMT") .statusCode(400).build(); AmazonServiceException ase = new AmazonServiceException("ClockSkew"); // assert date is > 10 years int offset = client.parseClockSkewOffset(httpResponse, ase); assertEquals(offset, 0); } @Test public void testRequestHander2s() { final Request<?> testRequest = new DefaultRequest<String>("test"); final List<Object> calls = new ArrayList<Object>(); List<RequestHandler2> handlers = new ArrayList<RequestHandler2>(); RequestHandler2 defaultHandler = new RequestHandler2() { @Override public void beforeRequest(Request<?> request) { assertSame(testRequest, request); calls.add(this); } @Override public void afterResponse(Request<?> request, Response<?> response) { } @Override public void afterError(Request<?> request, Response<?> response, Exception e) { } }; RequestHandler2 credentialHandler = new CredentialsRequestHandler() { @Override public void beforeRequest(Request<?> request) { assertSame(testRequest, request); calls.add(this); } @Override public void afterResponse(Request<?> request, Response<?> response) { } @Override public void afterError(Request<?> request, Response<?> response, Exception e) { } }; handlers.add(defaultHandler); handlers.add(credentialHandler); ExecutionContext ec = new ExecutionContext(handlers, false, null); client.requestHandler2s(testRequest, ec); assertEquals(calls.size(), 2); assertTrue(calls.contains(defaultHandler)); assertTrue(calls.contains(credentialHandler)); } @Test public void testAfterError() { final Request<?> testRequest = new DefaultRequest<String>("test"); final Response<?> testResponse = new Response<String>("test", new HttpResponse.Builder().build()); final List<Object> calls = new ArrayList<Object>(); List<RequestHandler2> handlers = new ArrayList<RequestHandler2>(); final AmazonClientException ace = new AmazonClientException("Test exception"); RequestHandler2 handler = new RequestHandler2() { @Override public void beforeRequest(Request<?> request) { } @Override public void afterResponse(Request<?> request, Response<?> response) { } @Override public void afterError(Request<?> request, Response<?> response, Exception e) { assertSame(testRequest, request); assertSame(testResponse, response); assertSame(e, ace); calls.add(this); } }; handlers.add(handler); client.afterError(testRequest, testResponse, handlers, ace); assertEquals(calls.size(), 1); assertTrue(calls.contains(handler)); } @Test public void testAfterResponse() { final Request<?> testRequest = new DefaultRequest<String>("test"); final Response<?> testResponse = new Response<String>("test", new HttpResponse.Builder().build()); final List<Object> calls = new ArrayList<Object>(); List<RequestHandler2> handlers = new ArrayList<RequestHandler2>(); RequestHandler2 handler = new RequestHandler2() { @Override public void beforeRequest(Request<?> request) { } @Override public void afterResponse(Request<?> request, Response<?> response) { assertSame(testRequest, request); assertSame(testResponse, response); calls.add(this); } @Override public void afterError(Request<?> request, Response<?> response, Exception e) { } }; handlers.add(handler); client.afterResponse(testRequest, handlers, testResponse, null); assertEquals(calls.size(), 1); assertTrue(calls.contains(handler)); } @Test public void testDeprecatedConstructors() { ClientConfiguration conf = new ClientConfiguration(); RequestMetricCollector rmc = RequestMetricCollector.NONE; AmazonHttpClient defaultToUrlClient = new AmazonHttpClient(conf, rmc); assertTrue(defaultToUrlClient.httpClient instanceof UrlHttpClient); assertSame(defaultToUrlClient.config, conf); UrlHttpClient urlClient = new UrlHttpClient(conf); AmazonHttpClient deprecatedSpecifiedConstrucotr = new AmazonHttpClient(conf, urlClient, rmc); assertSame(deprecatedSpecifiedConstrucotr.config, conf); assertSame(deprecatedSpecifiedConstrucotr.httpClient, urlClient); } @Test public void testResetRequestAfterErrorWithNullContent() { final Request<?> testRequest = new DefaultRequest<String>("test"); testRequest.setContent(null); Exception cause = new Exception(); // Should be no-op client.resetRequestAfterError(testRequest, cause); } @Test(expected = AmazonClientException.class) public void testResetRequestAfterErrorWithNonRepeatableContent() { final Request<?> testRequest = new DefaultRequest<String>("test"); testRequest.setContent(new InputStream() { @Override public boolean markSupported() { return false; } @Override public int read() throws IOException { return 0; } }); Exception cause = new Exception(); // Should be no-op client.resetRequestAfterError(testRequest, cause); } @Test(expected = AmazonClientException.class) public void testResetRequestAfterErrorWithNonResetableContent() { final Request<?> testRequest = new DefaultRequest<String>("test"); testRequest.setContent(new InputStream() { @Override public void reset() { throw new RuntimeException("CannotReset"); } @Override public int read() throws IOException { return 0; } }); Exception cause = new Exception(); // Should be no-op client.resetRequestAfterError(testRequest, cause); } @Test public void testCreateUserAgent() { String existingUA = "Existing"; String additionalUA = "Additional"; String ua = AmazonHttpClient.createUserAgentString(existingUA, additionalUA); assertEquals(ua, (existingUA + " " + additionalUA)); } @Test public void testCreateUserAgentWithDuplicateAddition() { String existingUA = "Existing"; String additionalUA = "Existing"; String ua = AmazonHttpClient.createUserAgentString(existingUA, additionalUA); assertEquals(ua, (existingUA)); } @Test public void testSetUserAgentDefault() { ClientConfiguration config = new ClientConfiguration(); client = new AmazonHttpClient(config); final Request<?> request = new DefaultRequest<String>("ServiceName"); client.setUserAgent(request); String userAgent = request.getHeaders().get("User-Agent"); assertEquals("same user agent", ClientConfiguration.DEFAULT_USER_AGENT, userAgent); } @Test public void testSetUserAgentCustom() { String versionInfoUserAgent = ClientConfiguration.DEFAULT_USER_AGENT; String customUserAgent = "custom_user_agent"; String requestUserAgent = "request_user_agent"; String targetUserAgent = versionInfoUserAgent + " " + requestUserAgent + " " + customUserAgent; AmazonWebServiceRequest originalRequest = new AmazonWebServiceRequest() { }; RequestClientOptions opts = originalRequest.getRequestClientOptions(); opts.appendUserAgent("request_user_agent"); ClientConfiguration config = new ClientConfiguration(); config.setUserAgent("custom_user_agent"); client = new AmazonHttpClient(config); final Request<?> request = new DefaultRequest<String>(originalRequest, "ServiceName"); client.setUserAgent(request); String userAgent = request.getHeaders().get("User-Agent"); assertEquals("same user agent", targetUserAgent, userAgent); } }