From 553e7dd3206aafa4aa8f009ddff53fdd243cfb45 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Fri, 9 Aug 2013 11:38:05 +0000 Subject: [PATCH] Added test cases for ProtocolExec and RedirectExec git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1512264 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/http/conn/routing/HttpRoute.java | 12 + .../http/impl/execchain/ProtocolExec.java | 16 +- .../impl/execchain/TestMainClientExec.java | 9 +- .../http/impl/execchain/TestProtocolExec.java | 210 +++++++++- .../http/impl/execchain/TestRedirectExec.java | 370 ++++++++++++++++++ 5 files changed, 598 insertions(+), 19 deletions(-) create mode 100644 httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java diff --git a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java index c9d8f4348..0b6a68d76 100644 --- a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java +++ b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java @@ -175,6 +175,18 @@ public final class HttpRoute implements RouteInfo, Cloneable { secure ? LayerType.LAYERED : LayerType.PLAIN); } + /** + * Creates a new plain route through a proxy. + * + * @param target the host to which to route + * @param proxy the proxy to use + * + * @since 4.3 + */ + public HttpRoute(final HttpHost target, final HttpHost proxy) { + this(target, null, proxy, false); + } + public final HttpHost getTargetHost() { return this.targetHost; } diff --git a/httpclient/src/main/java/org/apache/http/impl/execchain/ProtocolExec.java b/httpclient/src/main/java/org/apache/http/impl/execchain/ProtocolExec.java index 3315cef1a..f2d240835 100644 --- a/httpclient/src/main/java/org/apache/http/impl/execchain/ProtocolExec.java +++ b/httpclient/src/main/java/org/apache/http/impl/execchain/ProtocolExec.java @@ -49,6 +49,7 @@ import org.apache.http.client.params.ClientPNames; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.URIUtils; import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpProcessor; import org.apache.http.util.Args; @@ -72,7 +73,7 @@ public class ProtocolExec implements ClientExecChain { this.httpProcessor = httpProcessor; } - private void rewriteRequestURI( + void rewriteRequestURI( final HttpRequestWrapper request, final HttpRoute route) throws ProtocolException { try { @@ -101,8 +102,11 @@ public class ProtocolExec implements ClientExecChain { } } - public CloseableHttpResponse execute(final HttpRoute route, final HttpRequestWrapper request, - final HttpClientContext context, final HttpExecutionAware execAware) throws IOException, + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { Args.notNull(route, "HTTP route"); Args.notNull(request, "HTTP request"); @@ -159,7 +163,11 @@ public class ProtocolExec implements ClientExecChain { if (uri != null) { final String userinfo = uri.getUserInfo(); if (userinfo != null) { - final CredentialsProvider credsProvider = context.getCredentialsProvider(); + CredentialsProvider credsProvider = context.getCredentialsProvider(); + if (credsProvider == null) { + credsProvider = new BasicCredentialsProvider(); + context.setCredentialsProvider(credsProvider); + } credsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials(userinfo)); 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 34addb0aa..0bdb56172 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 @@ -636,10 +636,9 @@ public class TestMainClientExec { 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(), Mockito.any())).thenReturn(response); @@ -647,7 +646,11 @@ public class TestMainClientExec { Mockito.verify(connManager).connect(managedConn, route, 321, context); Mockito.verify(connManager).routeComplete(managedConn, route, context); - + final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(HttpRequest.class); + Mockito.verify(requestExecutor).execute( + reqCaptor.capture(), + Mockito.same(managedConn), + Mockito.same(context)); final HttpRequest connect = reqCaptor.getValue(); Assert.assertNotNull(connect); Assert.assertEquals("CONNECT", connect.getRequestLine().getMethod()); diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestProtocolExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestProtocolExec.java index 583472990..75427cd74 100644 --- a/httpclient/src/test/java/org/apache/http/impl/execchain/TestProtocolExec.java +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestProtocolExec.java @@ -26,41 +26,131 @@ */ package org.apache.http.impl.execchain; +import org.apache.http.HttpException; import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.CredentialsProvider; +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.HttpRequestWrapper; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.message.BasicHttpRequest; +import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.net.URI; public class TestProtocolExec { + @Mock private ClientExecChain requestExecutor; + @Mock private HttpProcessor httpProcessor; - private ProtocolExec protocolExec; - private HttpClientContext context; - private HttpRequestWrapper request; + @Mock private HttpExecutionAware execAware; - private HttpRoute route; + + private ProtocolExec protocolExec; + private HttpHost target; + private HttpHost proxy; @Before public void setup() throws Exception { - requestExecutor = Mockito.mock(ClientExecChain.class); - httpProcessor = Mockito.mock(HttpProcessor.class); + MockitoAnnotations.initMocks(this); protocolExec = new ProtocolExec(requestExecutor, httpProcessor); - route = new HttpRoute(new HttpHost("foo", 8080)); - context = new HttpClientContext(); - execAware = Mockito.mock(HttpExecutionAware.class); + target = new HttpHost("foo", 80); + proxy = new HttpHost("bar", 8888); + } + + @Test + public void testFundamentals() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("/test")); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + + protocolExec.execute(route, request, context, execAware); + + Mockito.verify(httpProcessor).process(request, context); + Mockito.verify(requestExecutor).execute(route, request, context, execAware); + Mockito.verify(httpProcessor).process(response, context); + + Assert.assertEquals(new HttpHost("foo", 80), context.getTargetHost()); + Assert.assertEquals(target, context.getTargetHost()); + Assert.assertEquals(route, context.getHttpRoute()); + Assert.assertSame(request, context.getRequest()); + Assert.assertSame(response, context.getResponse()); + } + + @Test + public void testRewriteAbsoluteRequestURI() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("http://foo/test")); + protocolExec.rewriteRequestURI(request, route); + Assert.assertEquals(new URI("/test"), request.getURI()); + } + + @Test + public void testRewriteEmptyRequestURI() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("")); + protocolExec.rewriteRequestURI(request, route); + Assert.assertEquals(new URI("/"), request.getURI()); + } + + @Test + public void testRewriteAbsoluteRequestURIViaPRoxy() throws Exception { + final HttpRoute route = new HttpRoute(target, proxy); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("http://foo/test")); + protocolExec.rewriteRequestURI(request, route); + Assert.assertEquals(new URI("http://foo/test"), request.getURI()); + } + + @Test + public void testRewriteRelativeRequestURIViaPRoxy() throws Exception { + final HttpRoute route = new HttpRoute(target, proxy); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("/test")); + protocolExec.rewriteRequestURI(request, route); + Assert.assertEquals(new URI("http://foo:80/test"), request.getURI()); + } + + @Test + public void testHostHeaderUriRequest() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("http://bar/test")); + final HttpClientContext context = HttpClientContext.create(); + protocolExec.execute(route, request, context, execAware); + // ProtocolExect should have extracted the host from request URI + Assert.assertEquals(new HttpHost("bar", -1, "http"), context.getTargetHost()); } @Test public void testHostHeaderWhenNonUriRequest() throws Exception { - request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "http://bar/test")); + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new BasicHttpRequest("GET", "http://bar/test")); + final HttpClientContext context = HttpClientContext.create(); protocolExec.execute(route, request, context, execAware); // ProtocolExect should have extracted the host from request URI Assert.assertEquals(new HttpHost("bar", -1, "http"), context.getTargetHost()); @@ -68,11 +158,107 @@ public class TestProtocolExec { @Test public void testHostHeaderWhenNonUriRequestAndInvalidUri() throws Exception { - request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "http://bar/test|")); + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new BasicHttpRequest("GET", "http://bar/test|")); + final HttpClientContext context = HttpClientContext.create(); protocolExec.execute(route, request, context, execAware); // ProtocolExect should have fall back to physical host as request URI // is not parseable - Assert.assertEquals(new HttpHost("foo", 8080, "http"), context.getTargetHost()); + Assert.assertEquals(new HttpHost("foo", 80, "http"), context.getTargetHost()); + } + + @Test + public void testHostHeaderImplicitHost() throws Exception { + final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080)); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("/test")); + final HttpClientContext context = HttpClientContext.create(); + protocolExec.execute(route, request, context, execAware); + Assert.assertEquals(new HttpHost("somehost", 8080), context.getTargetHost()); + } + + @Test + public void testUserInfoInRequestURI() throws Exception { + final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080)); + final HttpRequestWrapper request = HttpRequestWrapper.wrap( + new HttpGet("http://somefella:secret@bar/test")); + final HttpClientContext context = HttpClientContext.create(); + protocolExec.execute(route, request, context, execAware); + Assert.assertEquals(new URI("/test"), request.getURI()); + Assert.assertEquals(new HttpHost("bar", -1), context.getTargetHost()); + final CredentialsProvider credentialsProvider = context.getCredentialsProvider(); + Assert.assertNotNull(credentialsProvider); + final Credentials creds = credentialsProvider.getCredentials(new AuthScope("bar", -1, null)); + Assert.assertNotNull(creds); + Assert.assertEquals("somefella", creds.getUserPrincipal().getName()); + } + + @Test(expected = HttpException.class) + public void testPostProcessHttpException() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("/test")); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.doThrow(new HttpException("Ooopsie")).when(httpProcessor).process( + Mockito.same(response), Mockito.any()); + try { + protocolExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(response).close(); + throw ex; + } + } + + @Test(expected = IOException.class) + public void testPostProcessIOException() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("/test")); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.doThrow(new IOException("Ooopsie")).when(httpProcessor).process( + Mockito.same(response), Mockito.any()); + try { + protocolExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(response).close(); + throw ex; + } + } + + @Test(expected = RuntimeException.class) + public void testPostProcessRuntimeException() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("/test")); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.doThrow(new RuntimeException("Ooopsie")).when(httpProcessor).process( + Mockito.same(response), Mockito.any()); + try { + protocolExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(response).close(); + throw ex; + } } } diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java new file mode 100644 index 000000000..ebae9f65e --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java @@ -0,0 +1,370 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.execchain; + +import junit.framework.Assert; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolException; +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.RedirectException; +import org.apache.http.client.RedirectStrategy; +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.HttpRequestWrapper; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.auth.NTLMScheme; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; + +public class TestRedirectExec { + + @Mock + private ClientExecChain requestExecutor; + @Mock + private HttpRoutePlanner httpRoutePlanner; + @Mock + private RedirectStrategy redirectStrategy; + @Mock + private HttpExecutionAware execAware; + + private RedirectExec redirectExec; + private HttpHost target; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + redirectExec = new RedirectExec(requestExecutor, httpRoutePlanner, redirectStrategy); + target = new HttpHost("localhost", 80); + } + + @Test + public void testFundamentals() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + get.addHeader("header", "this"); + get.addHeader("header", "that"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + final HttpEntity entity1 = EntityBuilder.create() + .setStream(instream1) + .build(); + Mockito.when(response1.getEntity()).thenReturn(entity1); + final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class); + final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + final HttpEntity entity2 = EntityBuilder.create() + .setStream(instream2) + .build(); + Mockito.when(response2.getEntity()).thenReturn(entity2); + final HttpGet redirect = new HttpGet("http://localhost:80/redirect"); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + HttpRequestWrapperMatcher.same(redirect), + Mockito.any(), + Mockito.any())).thenReturn(response2); + Mockito.when(redirectStrategy.isRedirected( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(redirectStrategy.getRedirect( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(redirect); + Mockito.when(httpRoutePlanner.determineRoute( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(route); + + redirectExec.execute(route, request, context, execAware); + + final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass( + HttpRequestWrapper.class); + Mockito.verify(requestExecutor, Mockito.times(2)).execute( + Mockito.eq(route), + reqCaptor.capture(), + Mockito.same(context), + Mockito.same(execAware)); + + final List allValues = reqCaptor.getAllValues(); + Assert.assertNotNull(allValues); + Assert.assertEquals(2, allValues.size()); + Assert.assertSame(request, allValues.get(0)); + final HttpRequestWrapper redirectWrapper = allValues.get(1); + final Header[] headers = redirectWrapper.getHeaders("header"); + Assert.assertNotNull(headers); + Assert.assertEquals(2, headers.length); + Assert.assertEquals("this", headers[0].getValue()); + Assert.assertEquals("that", headers[1].getValue()); + + Mockito.verify(response1, Mockito.times(1)).close(); + Mockito.verify(instream1, Mockito.times(1)).close(); + Mockito.verify(response2, Mockito.never()).close(); + Mockito.verify(instream2, Mockito.never()).close(); + } + + @Test(expected = RedirectException.class) + public void testMaxRedirect() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + final RequestConfig config = RequestConfig.custom() + .setRedirectsEnabled(true) + .setMaxRedirects(3) + .build(); + context.setRequestConfig(config); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + final HttpGet redirect = new HttpGet("http://localhost:80/redirect"); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(redirectStrategy.isRedirected( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(redirectStrategy.getRedirect( + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(redirect); + Mockito.when(httpRoutePlanner.determineRoute( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(route); + + redirectExec.execute(route, request, context, execAware); + } + + @Test(expected = HttpException.class) + public void testRelativeRedirect() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class); + final HttpGet redirect = new HttpGet("/redirect"); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + HttpRequestWrapperMatcher.same(redirect), + Mockito.any(), + Mockito.any())).thenReturn(response2); + Mockito.when(redirectStrategy.isRedirected( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(redirectStrategy.getRedirect( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(redirect); + Mockito.when(httpRoutePlanner.determineRoute( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(route); + + redirectExec.execute(route, request, context, execAware); + } + + @Test + public void testCrossSiteRedirect() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + + final AuthState targetAuthState = new AuthState(); + targetAuthState.setState(AuthProtocolState.SUCCESS); + targetAuthState.update(new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + final AuthState proxyAuthState = new AuthState(); + proxyAuthState.setState(AuthProtocolState.SUCCESS); + proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass")); + context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState); + context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class); + final HttpGet redirect = new HttpGet("http://otherhost/redirect"); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + HttpRequestWrapperMatcher.same(redirect), + Mockito.any(), + Mockito.any())).thenReturn(response2); + Mockito.when(redirectStrategy.isRedirected( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.when(redirectStrategy.getRedirect( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(redirect); + Mockito.when(httpRoutePlanner.determineRoute( + Mockito.eq(target), + Mockito.any(), + Mockito.any())).thenReturn(new HttpRoute(new HttpHost("otherhost"))); + + redirectExec.execute(route, request, context, execAware); + + Assert.assertNotNull(context.getTargetAuthState()); + Assert.assertEquals(AuthProtocolState.UNCHALLENGED, context.getTargetAuthState().getState()); + Assert.assertEquals(null, context.getTargetAuthState().getAuthScheme()); + Assert.assertNotNull(context.getProxyAuthState()); + Assert.assertEquals(AuthProtocolState.UNCHALLENGED, context.getProxyAuthState().getState()); + Assert.assertEquals(null, context.getProxyAuthState().getAuthScheme()); + } + + @Test(expected = RuntimeException.class) + public void testRedirectRuntimeException() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(redirectStrategy.isRedirected( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.doThrow(new RuntimeException("Oppsie")).when(redirectStrategy.getRedirect( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())); + + try { + redirectExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(response1).close(); + throw ex; + } + } + + @Test(expected = ProtocolException.class) + public void testRedirectProtocolException() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpGet get = new HttpGet("/test"); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(get); + final HttpClientContext context = HttpClientContext.create(); + + final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class); + final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); + final HttpEntity entity1 = EntityBuilder.create() + .setStream(instream1) + .build(); + Mockito.when(response1.getEntity()).thenReturn(entity1); + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response1); + Mockito.when(redirectStrategy.isRedirected( + Mockito.same(request), + Mockito.same(response1), + Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.doThrow(new ProtocolException("Oppsie")).when(redirectStrategy).getRedirect( + Mockito.same(request), + Mockito.same(response1), + Mockito.any()); + + try { + redirectExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(instream1).close(); + Mockito.verify(response1).close(); + throw ex; + } + } + + static class HttpRequestWrapperMatcher extends ArgumentMatcher { + + private final HttpRequest original; + + HttpRequestWrapperMatcher(final HttpRequest original) { + super(); + this.original = original; + } + public boolean matches(final Object obj) { + final HttpRequestWrapper wrapper = (HttpRequestWrapper) obj; + return original == wrapper.getOriginal(); + } + + static HttpRequestWrapper same(final HttpRequest original) { + return Matchers.argThat(new HttpRequestWrapperMatcher(original)); + } + + } + +}