1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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"})
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 }