View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.classic;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.IOException;
32  
33  import org.apache.hc.client5.http.HttpRequestRetryStrategy;
34  import org.apache.hc.client5.http.HttpRoute;
35  import org.apache.hc.client5.http.classic.ExecChain;
36  import org.apache.hc.client5.http.classic.ExecRuntime;
37  import org.apache.hc.client5.http.classic.methods.HttpGet;
38  import org.apache.hc.client5.http.classic.methods.HttpPost;
39  import org.apache.hc.client5.http.entity.EntityBuilder;
40  import org.apache.hc.client5.http.protocol.HttpClientContext;
41  import org.apache.hc.core5.http.ClassicHttpRequest;
42  import org.apache.hc.core5.http.ClassicHttpResponse;
43  import org.apache.hc.core5.http.Header;
44  import org.apache.hc.core5.http.HttpHost;
45  import org.apache.hc.core5.http.HttpRequest;
46  import org.apache.hc.core5.http.HttpResponse;
47  import org.apache.hc.core5.http.protocol.HttpContext;
48  import org.apache.hc.core5.util.TimeValue;
49  import org.junit.Assert;
50  import org.junit.Before;
51  import org.junit.Test;
52  import org.mockito.Mock;
53  import org.mockito.Mockito;
54  import org.mockito.MockitoAnnotations;
55  import org.mockito.invocation.InvocationOnMock;
56  import org.mockito.stubbing.Answer;
57  
58  @SuppressWarnings({"boxing","static-access"}) // test code
59  public class TestHttpRequestRetryExec {
60  
61      @Mock
62      private HttpRequestRetryStrategy retryStrategy;
63      @Mock
64      private ExecChain chain;
65      @Mock
66      private ExecRuntime endpoint;
67  
68      private HttpRequestRetryExec retryExec;
69      private HttpHost target;
70  
71      @Before
72      public void setup() throws Exception {
73          MockitoAnnotations.initMocks(this);
74          retryExec = new HttpRequestRetryExec(retryStrategy);
75          target = new HttpHost("localhost", 80);
76      }
77  
78  
79      @Test
80      public void testFundamentals1() throws Exception {
81          final HttpRoute route = new HttpRoute(target);
82          final HttpGet request = new HttpGet("/test");
83          final HttpClientContext context = HttpClientContext.create();
84  
85          final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
86  
87          Mockito.when(chain.proceed(
88                  Mockito.same(request),
89                  Mockito.<ExecChain.Scope>any())).thenReturn(response);
90          Mockito.when(retryStrategy.retryRequest(
91                  Mockito.<HttpResponse>any(),
92                  Mockito.anyInt(),
93                  Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
94          Mockito.when(retryStrategy.getRetryInterval(
95                  Mockito.<HttpResponse>any(),
96                  Mockito.anyInt(),
97                  Mockito.<HttpContext>any())).thenReturn(TimeValue.ZERO_MILLISECONDS);
98  
99          final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context);
100         retryExec.execute(request, scope, chain);
101 
102         Mockito.verify(chain, Mockito.times(2)).proceed(
103                 Mockito.<ClassicHttpRequest>any(),
104                 Mockito.same(scope));
105         Mockito.verify(response, Mockito.times(1)).close();
106     }
107 
108     @Test(expected = RuntimeException.class)
109     public void testStrategyRuntimeException() throws Exception {
110         final HttpRoute route = new HttpRoute(target);
111         final ClassicHttpRequest request = new HttpGet("/test");
112         final HttpClientContext context = HttpClientContext.create();
113 
114         final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
115         Mockito.when(chain.proceed(
116                 Mockito.<ClassicHttpRequest>any(),
117                 Mockito.<ExecChain.Scope>any())).thenReturn(response);
118         Mockito.doThrow(new RuntimeException("Ooopsie")).when(retryStrategy).retryRequest(
119                 Mockito.<HttpResponse>any(),
120                 Mockito.anyInt(),
121                 Mockito.<HttpContext>any());
122         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context);
123         try {
124             retryExec.execute(request, scope, chain);
125         } catch (final Exception ex) {
126             Mockito.verify(response).close();
127             throw ex;
128         }
129     }
130 
131     @Test
132     public void testNonRepeatableEntityResponseReturnedImmediately() throws Exception {
133         final HttpRoute route = new HttpRoute(target);
134 
135         final HttpPost request = new HttpPost("/test");
136         request.setEntity(EntityBuilder.create()
137                 .setStream(new ByteArrayInputStream(new byte[]{}))
138                 .build());
139         final HttpClientContext context = HttpClientContext.create();
140 
141         final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
142         Mockito.when(chain.proceed(
143                 Mockito.<ClassicHttpRequest>any(),
144                 Mockito.<ExecChain.Scope>any())).thenReturn(response);
145         Mockito.when(retryStrategy.retryRequest(
146                 Mockito.<HttpResponse>any(),
147                 Mockito.anyInt(),
148                 Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
149 
150         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context);
151         final ClassicHttpResponse finalResponse = retryExec.execute(request, scope, chain);
152 
153         Assert.assertSame(response, finalResponse);
154         Mockito.verify(response, Mockito.times(0)).close();
155     }
156 
157     @Test(expected = IOException.class)
158     public void testFundamentals2() throws Exception {
159         final HttpRoute route = new HttpRoute(target);
160         final HttpGet originalRequest = new HttpGet("/test");
161         originalRequest.addHeader("header", "this");
162         originalRequest.addHeader("header", "that");
163         final HttpClientContext context = HttpClientContext.create();
164 
165         Mockito.when(chain.proceed(
166                 Mockito.<ClassicHttpRequest>any(),
167                 Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
168 
169             @Override
170             public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
171                 final Object[] args = invocationOnMock.getArguments();
172                 final ClassicHttpRequest wrapper = (ClassicHttpRequest) args[0];
173                 final Header[] headers = wrapper.getHeaders();
174                 Assert.assertEquals(2, headers.length);
175                 Assert.assertEquals("this", headers[0].getValue());
176                 Assert.assertEquals("that", headers[1].getValue());
177                 wrapper.addHeader("Cookie", "monster");
178                 throw new IOException("Ka-boom");
179             }
180 
181         });
182         Mockito.when(retryStrategy.retryRequest(
183                 Mockito.<HttpRequest>any(),
184                 Mockito.<IOException>any(),
185                 Mockito.eq(1),
186                 Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
187         final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
188         final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
189         try {
190             retryExec.execute(request, scope, chain);
191         } catch (final IOException ex) {
192             Mockito.verify(chain, Mockito.times(2)).proceed(
193                     Mockito.<ClassicHttpRequest>any(),
194                     Mockito.same(scope));
195             throw ex;
196         }
197     }
198 
199 
200     @Test(expected = IOException.class)
201     public void testAbortedRequest() throws Exception {
202         final HttpRoute route = new HttpRoute(target);
203         final HttpGet originalRequest = new HttpGet("/test");
204         final HttpClientContext context = HttpClientContext.create();
205 
206         Mockito.when(chain.proceed(
207                 Mockito.<ClassicHttpRequest>any(),
208                 Mockito.<ExecChain.Scope>any())).thenThrow(new IOException("Ka-boom"));
209         Mockito.when(endpoint.isExecutionAborted()).thenReturn(true);
210 
211         final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
212         final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
213         try {
214             retryExec.execute(request, scope, chain);
215         } catch (final IOException ex) {
216             Mockito.verify(chain, Mockito.times(1)).proceed(
217                     Mockito.same(request),
218                     Mockito.same(scope));
219             Mockito.verify(retryStrategy, Mockito.never()).retryRequest(
220                     Mockito.<HttpRequest>any(),
221                     Mockito.<IOException>any(),
222                     Mockito.anyInt(),
223                     Mockito.<HttpContext>any());
224 
225             throw ex;
226         }
227     }
228 
229     @Test(expected = IOException.class)
230     public void testNonRepeatableRequest() throws Exception {
231         final HttpRoute route = new HttpRoute(target);
232         final HttpPost originalRequest = new HttpPost("/test");
233         originalRequest.setEntity(EntityBuilder.create()
234                 .setStream(new ByteArrayInputStream(new byte[]{}))
235                 .build());
236         final HttpClientContext context = HttpClientContext.create();
237 
238         Mockito.when(chain.proceed(
239                 Mockito.<ClassicHttpRequest>any(),
240                 Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
241 
242             @Override
243             public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
244                 final Object[] args = invocationOnMock.getArguments();
245                 final ClassicHttpRequest req = (ClassicHttpRequest) args[0];
246                 req.getEntity().writeTo(new ByteArrayOutputStream());
247                 throw new IOException("Ka-boom");
248             }
249 
250         });
251         Mockito.when(retryStrategy.retryRequest(
252                 Mockito.<HttpRequest>any(),
253                 Mockito.<IOException>any(),
254                 Mockito.eq(1),
255                 Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
256         final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
257         final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
258         try {
259             retryExec.execute(request, scope, chain);
260         } catch (final IOException ex) {
261             Mockito.verify(chain, Mockito.times(1)).proceed(
262                     Mockito.same(request),
263                     Mockito.same(scope));
264 
265             throw ex;
266         }
267     }
268 
269 }