From e40168d06cb3001a709ff4f626f7c5d5aef41c30 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Wed, 7 Aug 2013 19:20:05 +0000 Subject: [PATCH] Unit tests for MainClientExec git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1511445 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/impl/execchain/MainClientExec.java | 9 +- .../impl/execchain/TestMainClientExec.java | 750 ++++++++++++++++-- 2 files changed, 705 insertions(+), 54 deletions(-) diff --git a/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java b/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java index 3100c66f5..d7f48f046 100644 --- a/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java +++ b/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java @@ -193,12 +193,7 @@ public class MainClientExec implements ClientExecChain { final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn); try { if (execAware != null) { - if (execAware.isAborted()) { - connHolder.close(); - throw new RequestAbortedException("Request aborted"); - } else { - execAware.setCancellable(connHolder); - } + execAware.setCancellable(connHolder); } HttpResponse response = null; @@ -340,7 +335,7 @@ public class MainClientExec implements ClientExecChain { /** * Establishes the target route. */ - private void establishRoute( + void establishRoute( final AuthState proxyAuthState, final HttpClientConnection managedConn, final HttpRoute route, diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java index 599f12c21..c15edc89f 100644 --- a/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java @@ -26,104 +26,760 @@ */ package org.apache.http.impl.execchain; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import junit.framework.Assert; import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.Header; import org.apache.http.HttpClientConnection; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthOption; +import org.apache.http.auth.AuthProtocolState; +import org.apache.http.auth.AuthState; +import org.apache.http.auth.NTCredentials; +import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthenticationStrategy; +import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.UserTokenHandler; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.EntityBuilder; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestWrapper; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.concurrent.Cancellable; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.routing.RouteInfo; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.auth.NTLMScheme; +import org.apache.http.impl.conn.ConnectionShutdownException; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestExecutor; +import org.apache.http.util.EntityUtils; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; public class TestMainClientExec { - private MainClientExec mainClientExec; + @Mock private HttpRequestExecutor requestExecutor; + @Mock private HttpClientConnectionManager connManager; + @Mock private ConnectionReuseStrategy reuseStrategy; + @Mock private ConnectionKeepAliveStrategy keepAliveStrategy; + @Mock private AuthenticationStrategy targetAuthStrategy; + @Mock private AuthenticationStrategy proxyAuthStrategy; + @Mock private UserTokenHandler userTokenHandler; - private HttpClientContext context; - private HttpGet request; + @Mock private HttpExecutionAware execAware; - private HttpRoute route; + @Mock private ConnectionRequest connRequest; + @Mock private HttpClientConnection managedConn; - private HttpResponse response; - private RequestConfig config; + + private MainClientExec mainClientExec; + private HttpHost target; + private HttpHost proxy; @Before public void setup() throws Exception { - requestExecutor = Mockito.mock(HttpRequestExecutor.class); - connManager = Mockito.mock(HttpClientConnectionManager.class); - reuseStrategy = Mockito.mock(ConnectionReuseStrategy.class); - keepAliveStrategy = Mockito.mock(ConnectionKeepAliveStrategy.class); - targetAuthStrategy = Mockito.mock(AuthenticationStrategy.class); - proxyAuthStrategy = Mockito.mock(AuthenticationStrategy.class); - userTokenHandler = Mockito.mock(UserTokenHandler.class); + MockitoAnnotations.initMocks(this); mainClientExec = new MainClientExec(requestExecutor, connManager, reuseStrategy, keepAliveStrategy, targetAuthStrategy, proxyAuthStrategy, userTokenHandler); - route = new HttpRoute(new HttpHost("foo", 8080)); - context = new HttpClientContext(); - config = RequestConfig.custom().setSocketTimeout(3000).build(); - context.setRequestConfig(config); - execAware = Mockito.mock(HttpExecutionAware.class); - connRequest = Mockito.mock(ConnectionRequest.class); - managedConn = Mockito.mock(HttpClientConnection.class); - response = Mockito.mock(HttpResponse.class); - Mockito.when( - connManager.requestConnection(Mockito.any(HttpRoute.class), Mockito.any(Object.class))) - .thenReturn(connRequest); - Mockito.when(connRequest.get(Mockito.anyLong(), Mockito.any(TimeUnit.class))).thenReturn( - managedConn); - managedConn.setSocketTimeout(Mockito.eq(3000)); - Mockito.when( - requestExecutor.execute(Mockito.any(HttpRequest.class), - Mockito.any(HttpClientConnection.class), Mockito.any(HttpClientContext.class))) - .thenReturn(response); + target = new HttpHost("foo", 80); + proxy = new HttpHost("bar", 8888); + + Mockito.when(connManager.requestConnection( + Mockito.any(), Mockito.any())).thenReturn(connRequest); + Mockito.when(connRequest.get( + Mockito.anyLong(), Mockito.any())).thenReturn(managedConn); + final Map challenges = new HashMap(); + challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); + final AuthOption authOption = new AuthOption( + new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + Mockito.when(targetAuthStrategy.getChallenges( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(challenges); + Mockito.when(targetAuthStrategy.getChallenges( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(challenges); + Mockito.when(targetAuthStrategy.select( + Mockito.same(challenges), + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn( + new LinkedList(Arrays.asList(authOption))); + Mockito.when(proxyAuthStrategy.getChallenges( + Mockito.eq(proxy), + Mockito.any(), + Mockito.any())).thenReturn(challenges); + Mockito.when(proxyAuthStrategy.getChallenges( + Mockito.eq(proxy), + Mockito.any(), + Mockito.any())).thenReturn(challenges); + Mockito.when(proxyAuthStrategy.select( + Mockito.same(challenges), + Mockito.eq(proxy), + Mockito.any(), + Mockito.any())).thenReturn( + new LinkedList(Arrays.asList(authOption))); + } @Test - public void testSocketTimeoutNewConnection() throws Exception { - request = new HttpGet("http://bar/test"); - request.setConfig(config); - mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware); - Mockito.verify(managedConn).setSocketTimeout(3000); + public void testExecRequestNonPersistentConnection() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final RequestConfig config = RequestConfig.custom() + .setConnectTimeout(123) + .setSocketTimeout(234) + .setConnectionRequestTimeout(345) + .build(); + context.setRequestConfig(config); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS); + Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest); + Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.any()); + Mockito.verify(connManager).connect(managedConn, route, 123, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(managedConn).setSocketTimeout(234); + Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); + Mockito.verify(managedConn, Mockito.times(1)).close(); + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + Assert.assertNotNull(context.getTargetAuthState()); + Assert.assertNotNull(context.getProxyAuthState()); + Assert.assertSame(managedConn, context.getConnection()); + Assert.assertNull(context.getUserToken()); + Assert.assertNotNull(finalResponse); + Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass())); + } + + @Test + public void testExecRequestPersistentConnection() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.when(reuseStrategy.keepAlive( + Mockito.same(response), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(keepAliveStrategy.getKeepAliveDuration( + Mockito.same(response), + Mockito.any())).thenReturn(678L); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS); + Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); + Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS); + Mockito.verify(managedConn, Mockito.never()).close(); + + Assert.assertNotNull(finalResponse); + Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass())); + } + + @Test + public void testExecRequestPersistentStatefulConnection() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.when(reuseStrategy.keepAlive( + Mockito.same(response), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(userTokenHandler.getUserToken( + Mockito.any())).thenReturn("this and that"); + + mainClientExec.execute(route, request, context, execAware); + Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS); + Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); + Mockito.verify(connManager).releaseConnection(managedConn, "this and that", 0, TimeUnit.MILLISECONDS); + Mockito.verify(managedConn, Mockito.never()).close(); + + Assert.assertEquals("this and that", context.getUserToken()); + } + + @Test + public void testExecRequestConnectionRelease() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + // The entity is streaming + response.setEntity(EntityBuilder.create() + .setStream(new ByteArrayInputStream(new byte[]{})) + .build()); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.when(reuseStrategy.keepAlive( + Mockito.same(response), + Mockito.any())).thenReturn(Boolean.FALSE); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(connManager).requestConnection(route, null); + Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS); + Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context); + Mockito.verify(connManager, Mockito.never()).releaseConnection( + Mockito.same(managedConn), + Mockito.any(), + Mockito.anyInt(), + Mockito.any()); + Mockito.verify(managedConn, Mockito.never()).close(); + + Assert.assertNotNull(finalResponse); + Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass())); + finalResponse.close(); + + Mockito.verify(connManager, Mockito.times(1)).releaseConnection( + managedConn, null, 0, TimeUnit.MILLISECONDS); + Mockito.verify(managedConn, Mockito.times(1)).shutdown(); + } + + @Test + public void testExecRequestStaleConnectionCheck() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final RequestConfig config = RequestConfig.custom() + .setStaleConnectionCheckEnabled(true) + .build(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + context.setRequestConfig(config); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.when(reuseStrategy.keepAlive( + Mockito.same(response), + Mockito.any())).thenReturn(Boolean.TRUE); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(managedConn, Mockito.times(1)).close(); + + Assert.assertNotNull(finalResponse); + Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass())); } @Test public void testSocketTimeoutExistingConnection() throws Exception { - request = new HttpGet("http://bar/test"); - request.setConfig(config); + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).build(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + context.setRequestConfig(config); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); Mockito.when(managedConn.isOpen()).thenReturn(true); - mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + + mainClientExec.execute(route, request, context, execAware); Mockito.verify(managedConn).setSocketTimeout(3000); } @Test public void testSocketTimeoutReset() throws Exception { - request = new HttpGet("http://bar/test"); - config = RequestConfig.custom().build(); - request.setConfig(config); - context.setRequestConfig(config); - Mockito.when(managedConn.isOpen()).thenReturn(true); - mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware); - Mockito.verify(managedConn).setSocketTimeout(0); + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + + mainClientExec.execute(route, request, context, execAware); + Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt()); } -} + + @Test(expected=RequestAbortedException.class) + public void testExecAbortedPriorToConnectionLease() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE); + try { + mainClientExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(connRequest, Mockito.times(1)).cancel(); + throw ex; + } + } + + @Test(expected=RequestAbortedException.class) + public void testExecAbortedPriorToConnectionSetup() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.TRUE); + try { + mainClientExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS); + Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.any()); + Mockito.verify(connManager, Mockito.never()).connect( + Mockito.same(managedConn), + Mockito.any(), + Mockito.anyInt(), + Mockito.any()); + throw ex; + } + } + + @Test(expected=RequestAbortedException.class) + public void testExecAbortedPriorToRequestExecution() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE); + Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE); + try { + mainClientExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS); + Mockito.verify(connManager, Mockito.times(1)).connect(managedConn, route, 0, context); + Mockito.verify(requestExecutor, Mockito.never()).execute( + Mockito.same(request), + Mockito.any(), + Mockito.any()); + throw ex; + } + } + + @Test(expected=RequestAbortedException.class) + public void testExecConnectionRequestInterrupted() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.any())) + .thenThrow(new InterruptedException()); + mainClientExec.execute(route, request, context, execAware); + } + + @Test(expected=RequestAbortedException.class) + public void testExecConnectionRequestFailed() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.any())) + .thenThrow(new ExecutionException("Opppsie", null)); + mainClientExec.execute(route, request, context, execAware); + } + + @Test + public void testExecRequestRetryOnAuthChallenge() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + response1.setEntity(EntityBuilder.create() + .setStream(instream1) + .build()); + final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4})); + response2.setEntity(EntityBuilder.create() + .setStream(instream2) + .build()); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1, response2); + Mockito.when(reuseStrategy.keepAlive( + Mockito.any(), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(targetAuthStrategy.isAuthenticationRequested( + Mockito.eq(target), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context); + Mockito.verify(instream1).close(); + Mockito.verify(instream2, Mockito.never()).close(); + + Assert.assertNotNull(finalResponse); + Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode()); + } + + @Test + public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + response1.setEntity(EntityBuilder.create() + .setStream(instream1) + .build()); + final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4})); + response2.setEntity(EntityBuilder.create() + .setStream(instream2) + .build()); + + final AuthState proxyAuthState = new AuthState(); + proxyAuthState.setState(AuthProtocolState.SUCCESS); + proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass")); + + final HttpClientContext context = new HttpClientContext(); + context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1, response2); + Mockito.when(reuseStrategy.keepAlive( + Mockito.any(), + Mockito.any())).thenReturn(Boolean.FALSE); + Mockito.when(targetAuthStrategy.isAuthenticationRequested( + Mockito.eq(target), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + + final CloseableHttpResponse finalResponse = mainClientExec.execute( + route, request, context, execAware); + Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context); + Mockito.verify(instream1).close(); + Mockito.verify(managedConn).close(); + Mockito.verify(instream2, Mockito.never()).close(); + + Assert.assertNotNull(finalResponse); + Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode()); + Assert.assertNull(proxyAuthState.getAuthScheme()); + Assert.assertNull(proxyAuthState.getCredentials()); + } + + @Test(expected = NonRepeatableRequestException.class) + public void testExecEntityEnclosingRequest() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpPost post = new HttpPost("http://bar/test"); + final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3}); + post.setEntity(EntityBuilder.create() + .setStream(instream0) + .build()); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(post); + + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3}); + response1.setEntity(EntityBuilder.create() + .setStream(instream1) + .build()); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenAnswer(new Answer() { + + public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable { + final Object[] args = invocationOnMock.getArguments(); + final HttpEntityEnclosingRequest request = (HttpEntityEnclosingRequest) args[0]; + request.getEntity().writeTo(new ByteArrayOutputStream()); + return response1; + } + + }); + Mockito.when(reuseStrategy.keepAlive( + Mockito.any(), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(targetAuthStrategy.isAuthenticationRequested( + Mockito.eq(target), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + + mainClientExec.execute(route, request, context, execAware); + } + + @Test(expected=InterruptedIOException.class) + public void testExecConnectionShutDown() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(requestExecutor.execute( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenThrow(new ConnectionShutdownException()); + + mainClientExec.execute(route, request, context, execAware); + } + + @Test + public void testEstablishDirectRoute() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + + Mockito.verify(connManager).connect(managedConn, route, 0, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + } + + @Test + public void testEstablishRouteDirectProxy() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, false); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + + Mockito.verify(connManager).connect(managedConn, route, 0, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + } + + @Test + public void testEstablishRouteViaProxyTunnel() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, true); + final HttpClientContext context = new HttpClientContext(); + final RequestConfig config = RequestConfig.custom() + .setConnectTimeout(321) + .build(); + context.setRequestConfig(config); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(HttpRequest.class); + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + reqCaptor.capture(), + Mockito.any(), + Mockito.any())).thenReturn(response); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + + Mockito.verify(connManager).connect(managedConn, route, 321, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + + final HttpRequest connect = reqCaptor.getValue(); + Assert.assertNotNull(connect); + Assert.assertEquals("CONNECT", connect.getRequestLine().getMethod()); + Assert.assertEquals(HttpVersion.HTTP_1_1, connect.getRequestLine().getProtocolVersion()); + Assert.assertEquals("foo:80", connect.getRequestLine().getUri()); + } + + @Test(expected = HttpException.class) + public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, true); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 101, "Lost"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + } + + @Test(expected = HttpException.class) + public void testEstablishRouteViaProxyTunnelFailure() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, true); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 500, "Boom"); + response.setEntity(new StringEntity("Ka-boom")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + + try { + mainClientExec.establishRoute(authState, managedConn, route, request, context); + } catch (TunnelRefusedException ex) { + final HttpResponse r = ex.getResponse(); + Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity())); + + Mockito.verify(managedConn).close(); + + throw ex; + } + } + + @Test + public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, true); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + response1.setEntity(EntityBuilder.create() + .setStream(instream1) + .build()); + final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(proxyAuthStrategy.isAuthenticationRequested( + Mockito.eq(proxy), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(reuseStrategy.keepAlive( + Mockito.any(), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(requestExecutor.execute( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response1, response2); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + + Mockito.verify(connManager).connect(managedConn, route, 0, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(instream1).close(); + } + + @Test + public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception { + final AuthState authState = new AuthState(); + final HttpRoute route = new HttpRoute(target, null, proxy, true); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + response1.setEntity(EntityBuilder.create() + .setStream(instream1) + .build()); + final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + Mockito.when(proxyAuthStrategy.isAuthenticationRequested( + Mockito.eq(proxy), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(reuseStrategy.keepAlive( + Mockito.any(), + Mockito.any())).thenReturn(Boolean.FALSE); + Mockito.when(requestExecutor.execute( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response1, response2); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + + Mockito.verify(connManager).connect(managedConn, route, 0, context); + Mockito.verify(connManager).routeComplete(managedConn, route, context); + Mockito.verify(instream1, Mockito.never()).close(); + Mockito.verify(managedConn).close(); + } + + @Test(expected = HttpException.class) + public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception { + final AuthState authState = new AuthState(); + final HttpHost proxy1 = new HttpHost("this", 8888); + final HttpHost proxy2 = new HttpHost("that", 8888); + final HttpRoute route = new HttpRoute(target, null, new HttpHost[] {proxy1, proxy2}, + true, RouteInfo.TunnelType.TUNNELLED, RouteInfo.LayerType.LAYERED); + final HttpClientContext context = new HttpClientContext(); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); + + Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); + + mainClientExec.establishRoute(authState, managedConn, route, request, context); + } + +} \ No newline at end of file