diff --git a/httpclient/src/main/java/org/apache/http/impl/execchain/MinimalClientExec.java b/httpclient/src/main/java/org/apache/http/impl/execchain/MinimalClientExec.java index 49f7f4586..469e395cb 100644 --- a/httpclient/src/main/java/org/apache/http/impl/execchain/MinimalClientExec.java +++ b/httpclient/src/main/java/org/apache/http/impl/execchain/MinimalClientExec.java @@ -156,11 +156,10 @@ public class MinimalClientExec implements ClientExecChain { timeout > 0 ? timeout : 0, context); this.connManager.routeComplete(managedConn, route, context); - } else { - final int timeout = config.getSocketTimeout(); - if (timeout >= 0) { - managedConn.setSocketTimeout(timeout); - } + } + final int timeout = config.getSocketTimeout(); + if (timeout >= 0) { + managedConn.setSocketTimeout(timeout); } HttpHost target = null; 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 0bdb56172..4d71fd99e 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 @@ -594,6 +594,66 @@ public class TestMainClientExec { mainClientExec.execute(route, request, context, execAware); } + @Test(expected=RuntimeException.class) + public void testExecRuntimeException() 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 RuntimeException("Ka-boom")); + + try { + mainClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + + @Test(expected=HttpException.class) + public void testExecHttpException() 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 HttpException("Ka-boom")); + + try { + mainClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + + @Test(expected=IOException.class) + public void testExecIOException() 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 IOException("Ka-boom")); + + try { + mainClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + @Test public void testEstablishDirectRoute() throws Exception { final AuthState authState = new AuthState(); diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestMinimalClientExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMinimalClientExec.java new file mode 100644 index 000000000..96dc4c6e4 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMinimalClientExec.java @@ -0,0 +1,353 @@ +/* + * ==================================================================== + * 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.ConnectionReuseStrategy; +import org.apache.http.HttpClientConnection; +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.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.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.impl.conn.ConnectionShutdownException; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.protocol.HttpRequestExecutor; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.lang.reflect.Proxy; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class TestMinimalClientExec { + + @Mock + private HttpRequestExecutor requestExecutor; + @Mock + private HttpClientConnectionManager connManager; + @Mock + private ConnectionReuseStrategy reuseStrategy; + @Mock + private ConnectionKeepAliveStrategy keepAliveStrategy; + @Mock + private HttpExecutionAware execAware; + @Mock + private ConnectionRequest connRequest; + @Mock + private HttpClientConnection managedConn; + + private MinimalClientExec minimalClientExec; + private HttpHost target; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + minimalClientExec = new MinimalClientExec( + requestExecutor, connManager, reuseStrategy, keepAliveStrategy); + target = new HttpHost("foo", 80); + + Mockito.when(connManager.requestConnection( + Mockito.any(), Mockito.any())).thenReturn(connRequest); + Mockito.when(connRequest.get( + Mockito.anyLong(), Mockito.any())).thenReturn(managedConn); + } + + @Test + 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 = minimalClientExec.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.assertSame(managedConn, context.getConnection()); + 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 = minimalClientExec.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 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 = minimalClientExec.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 testSocketTimeoutExistingConnection() throws Exception { + 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); + Mockito.when(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + + minimalClientExec.execute(route, request, context, execAware); + Mockito.verify(managedConn).setSocketTimeout(3000); + } + + @Test + public void testSocketTimeoutReset() 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(requestExecutor.execute( + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + + minimalClientExec.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 { + minimalClientExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(connRequest, Mockito.times(1)).cancel(); + 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()); + minimalClientExec.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)); + minimalClientExec.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()); + + minimalClientExec.execute(route, request, context, execAware); + } + + @Test(expected=RuntimeException.class) + public void testExecRuntimeException() 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 RuntimeException("Ka-boom")); + + try { + minimalClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + + @Test(expected=HttpException.class) + public void testExecHttpException() 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 HttpException("Ka-boom")); + + try { + minimalClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + + @Test(expected=IOException.class) + public void testExecIOException() 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 IOException("Ka-boom")); + + try { + minimalClientExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS); + + throw ex; + } + } + +} \ No newline at end of file diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestRetryExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRetryExec.java new file mode 100644 index 000000000..1fa69609e --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRetryExec.java @@ -0,0 +1,187 @@ +/* + * ==================================================================== + * 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.HttpEntityEnclosingRequest; +import org.apache.http.HttpHost; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.NonRepeatableRequestException; +import org.apache.http.client.entity.EntityBuilder; +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.conn.routing.HttpRoute; +import org.apache.http.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class TestRetryExec { + + @Mock + private ClientExecChain requestExecutor; + @Mock + private HttpRequestRetryHandler retryHandler; + @Mock + private HttpExecutionAware execAware; + + private RetryExec retryExec; + private HttpHost target; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + retryExec = new RetryExec(requestExecutor, retryHandler); + target = new HttpHost("localhost", 80); + } + + @Test(expected = IOException.class) + 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(); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocationOnMock) throws Throwable { + final Object[] args = invocationOnMock.getArguments(); + final HttpRequestWrapper wrapper = (HttpRequestWrapper) args[1]; + final Header[] headers = wrapper.getAllHeaders(); + Assert.assertEquals(2, headers.length); + Assert.assertEquals("this", headers[0].getValue()); + Assert.assertEquals("that", headers[1].getValue()); + wrapper.addHeader("Cookie", "monster"); + throw new IOException("Ka-boom"); + } + }); + Mockito.when(retryHandler.retryRequest( + Mockito.any(), + Mockito.eq(1), + Mockito.any())).thenReturn(Boolean.TRUE); + try { + retryExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(requestExecutor, Mockito.times(2)).execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.same(context), + Mockito.same(execAware)); + throw ex; + } + } + + @Test(expected = IOException.class) + public void testAbortedRequest() 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(); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenThrow(new IOException("Ka-boom")); + + Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE); + try { + retryExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(requestExecutor, Mockito.times(1)).execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.same(context), + Mockito.same(execAware)); + Mockito.verify(retryHandler, Mockito.never()).retryRequest( + Mockito.any(), + Mockito.anyInt(), + Mockito.any()); + + throw ex; + } + } + + @Test(expected = NonRepeatableRequestException.class) + public void testNonRepeatableRequest() throws Exception { + final HttpRoute route = new HttpRoute(target); + final HttpPost post = new HttpPost("/test"); + post.setEntity(EntityBuilder.create() + .setStream(new ByteArrayInputStream(new byte[]{})) + .build()); + final HttpRequestWrapper request = HttpRequestWrapper.wrap(post); + final HttpClientContext context = HttpClientContext.create(); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocationOnMock) throws Throwable { + final Object[] args = invocationOnMock.getArguments(); + final HttpEntityEnclosingRequest request = (HttpEntityEnclosingRequest) args[1]; + request.getEntity().writeTo(new ByteArrayOutputStream()); + throw new IOException("Ka-boom"); + } + }); + Mockito.when(retryHandler.retryRequest( + Mockito.any(), + Mockito.eq(1), + Mockito.any())).thenReturn(Boolean.TRUE); + try { + retryExec.execute(route, request, context, execAware); + } catch (IOException ex) { + Mockito.verify(requestExecutor, Mockito.times(1)).execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.same(context), + Mockito.same(execAware)); + + throw ex; + } + } + +} diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestServiceUnavailableRetryExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestServiceUnavailableRetryExec.java new file mode 100644 index 000000000..5d439bd00 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestServiceUnavailableRetryExec.java @@ -0,0 +1,117 @@ +/* + * ==================================================================== + * 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 org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.ServiceUnavailableRetryStrategy; +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.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +public class TestServiceUnavailableRetryExec { + + @Mock + private ClientExecChain requestExecutor; + @Mock + private ServiceUnavailableRetryStrategy retryStrategy; + @Mock + private HttpExecutionAware execAware; + + private ServiceUnavailableRetryExec retryExec; + private HttpHost target; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + retryExec = new ServiceUnavailableRetryExec(requestExecutor, retryStrategy); + target = new HttpHost("localhost", 80); + } + + @Test + public void testFundamentals() 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 response = Mockito.mock(CloseableHttpResponse.class); + + Mockito.when(requestExecutor.execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.any(), + Mockito.any())).thenReturn(response); + Mockito.when(retryStrategy.retryRequest( + Mockito.any(), + Mockito.anyInt(), + Mockito.any())).thenReturn(Boolean.TRUE, Boolean.FALSE); + Mockito.when(retryStrategy.getRetryInterval()).thenReturn(0L); + + retryExec.execute(route, request, context, execAware); + + Mockito.verify(requestExecutor, Mockito.times(2)).execute( + Mockito.eq(route), + Mockito.same(request), + Mockito.same(context), + Mockito.same(execAware)); + Mockito.verify(response, Mockito.times(1)).close(); + } + + @Test(expected = RuntimeException.class) + public void testStrategyRuntimeException() 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(retryStrategy).retryRequest( + Mockito.any(), + Mockito.anyInt(), + Mockito.any()); + try { + retryExec.execute(route, request, context, execAware); + } catch (Exception ex) { + Mockito.verify(response).close(); + throw ex; + } + } +}