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.http.impl.execchain;
28
29 import org.apache.http.Header;
30 import org.apache.http.HttpEntity;
31 import org.apache.http.HttpException;
32 import org.apache.http.HttpHost;
33 import org.apache.http.HttpRequest;
34 import org.apache.http.HttpResponse;
35 import org.apache.http.ProtocolException;
36 import org.apache.http.auth.AuthProtocolState;
37 import org.apache.http.auth.AuthState;
38 import org.apache.http.auth.NTCredentials;
39 import org.apache.http.auth.UsernamePasswordCredentials;
40 import org.apache.http.client.RedirectException;
41 import org.apache.http.client.RedirectStrategy;
42 import org.apache.http.client.config.RequestConfig;
43 import org.apache.http.client.entity.EntityBuilder;
44 import org.apache.http.client.methods.CloseableHttpResponse;
45 import org.apache.http.client.methods.HttpExecutionAware;
46 import org.apache.http.client.methods.HttpGet;
47 import org.apache.http.client.methods.HttpRequestWrapper;
48 import org.apache.http.client.protocol.HttpClientContext;
49 import org.apache.http.conn.routing.HttpRoute;
50 import org.apache.http.conn.routing.HttpRoutePlanner;
51 import org.apache.http.impl.auth.BasicScheme;
52 import org.apache.http.impl.auth.NTLMScheme;
53 import org.junit.Assert;
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.ArgumentMatcher;
58 import org.mockito.Matchers;
59 import org.mockito.Mock;
60 import org.mockito.Mockito;
61 import org.mockito.MockitoAnnotations;
62
63 import java.io.ByteArrayInputStream;
64 import java.io.InputStream;
65 import java.util.List;
66
67 @SuppressWarnings({"boxing","static-access"})
68 public class TestRedirectExec {
69
70 @Mock
71 private ClientExecChain requestExecutor;
72 @Mock
73 private HttpRoutePlanner httpRoutePlanner;
74 @Mock
75 private RedirectStrategy redirectStrategy;
76 @Mock
77 private HttpExecutionAware execAware;
78
79 private RedirectExec redirectExec;
80 private HttpHost target;
81
82 @Before
83 public void setup() throws Exception {
84 MockitoAnnotations.initMocks(this);
85 redirectExec = new RedirectExec(requestExecutor, httpRoutePlanner, redirectStrategy);
86 target = new HttpHost("localhost", 80);
87 }
88
89 @Test
90 public void testFundamentals() throws Exception {
91 final HttpRoute route = new HttpRoute(target);
92 final HttpGet get = new HttpGet("/test");
93 get.addHeader("header", "this");
94 get.addHeader("header", "that");
95 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
96 final HttpClientContext context = HttpClientContext.create();
97
98 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
99 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
100 final HttpEntity entity1 = EntityBuilder.create()
101 .setStream(inStream1)
102 .build();
103 Mockito.when(response1.getEntity()).thenReturn(entity1);
104 final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class);
105 final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
106 final HttpEntity entity2 = EntityBuilder.create()
107 .setStream(inStream2)
108 .build();
109 Mockito.when(response2.getEntity()).thenReturn(entity2);
110 final HttpGet redirect = new HttpGet("http://localhost:80/redirect");
111
112 Mockito.when(requestExecutor.execute(
113 Mockito.eq(route),
114 Mockito.same(request),
115 Mockito.<HttpClientContext>any(),
116 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
117 Mockito.when(requestExecutor.execute(
118 Mockito.eq(route),
119 HttpRequestWrapperMatcher.same(redirect),
120 Mockito.<HttpClientContext>any(),
121 Mockito.<HttpExecutionAware>any())).thenReturn(response2);
122 Mockito.when(redirectStrategy.isRedirected(
123 Mockito.same(get),
124 Mockito.same(response1),
125 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
126 Mockito.when(redirectStrategy.getRedirect(
127 Mockito.same(get),
128 Mockito.same(response1),
129 Mockito.<HttpClientContext>any())).thenReturn(redirect);
130 Mockito.when(httpRoutePlanner.determineRoute(
131 Mockito.eq(target),
132 Mockito.<HttpRequestWrapper>any(),
133 Mockito.<HttpClientContext>any())).thenReturn(route);
134
135 redirectExec.execute(route, request, context, execAware);
136
137 final ArgumentCaptor<HttpRequestWrapper> reqCaptor = ArgumentCaptor.forClass(
138 HttpRequestWrapper.class);
139 Mockito.verify(requestExecutor, Mockito.times(2)).execute(
140 Mockito.eq(route),
141 reqCaptor.capture(),
142 Mockito.same(context),
143 Mockito.same(execAware));
144
145 final List<HttpRequestWrapper> allValues = reqCaptor.getAllValues();
146 Assert.assertNotNull(allValues);
147 Assert.assertEquals(2, allValues.size());
148 Assert.assertSame(request, allValues.get(0));
149 final HttpRequestWrapper redirectWrapper = allValues.get(1);
150 final Header[] headers = redirectWrapper.getHeaders("header");
151 Assert.assertNotNull(headers);
152 Assert.assertEquals(2, headers.length);
153 Assert.assertEquals("this", headers[0].getValue());
154 Assert.assertEquals("that", headers[1].getValue());
155
156 Mockito.verify(response1, Mockito.times(1)).close();
157 Mockito.verify(inStream1, Mockito.times(1)).close();
158 Mockito.verify(response2, Mockito.never()).close();
159 Mockito.verify(inStream2, Mockito.never()).close();
160 }
161
162 @Test(expected = RedirectException.class)
163 public void testMaxRedirect() throws Exception {
164 final HttpRoute route = new HttpRoute(target);
165 final HttpGet get = new HttpGet("/test");
166 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
167 final HttpClientContext context = HttpClientContext.create();
168 final RequestConfig config = RequestConfig.custom()
169 .setRedirectsEnabled(true)
170 .setMaxRedirects(3)
171 .build();
172 context.setRequestConfig(config);
173
174 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
175 final HttpGet redirect = new HttpGet("http://localhost:80/redirect");
176
177 Mockito.when(requestExecutor.execute(
178 Mockito.eq(route),
179 Mockito.<HttpRequestWrapper>any(),
180 Mockito.<HttpClientContext>any(),
181 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
182 Mockito.when(redirectStrategy.isRedirected(
183 Mockito.<HttpRequestWrapper>any(),
184 Mockito.<HttpResponse>any(),
185 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
186 Mockito.when(redirectStrategy.getRedirect(
187 Mockito.<HttpRequestWrapper>any(),
188 Mockito.<HttpResponse>any(),
189 Mockito.<HttpClientContext>any())).thenReturn(redirect);
190 Mockito.when(httpRoutePlanner.determineRoute(
191 Mockito.eq(target),
192 Mockito.<HttpRequestWrapper>any(),
193 Mockito.<HttpClientContext>any())).thenReturn(route);
194
195 redirectExec.execute(route, request, context, execAware);
196 }
197
198 @Test(expected = HttpException.class)
199 public void testRelativeRedirect() throws Exception {
200 final HttpRoute route = new HttpRoute(target);
201 final HttpGet get = new HttpGet("/test");
202 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
203 final HttpClientContext context = HttpClientContext.create();
204
205 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
206 final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class);
207 final HttpGet redirect = new HttpGet("/redirect");
208 Mockito.when(requestExecutor.execute(
209 Mockito.eq(route),
210 Mockito.same(request),
211 Mockito.<HttpClientContext>any(),
212 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
213 Mockito.when(requestExecutor.execute(
214 Mockito.eq(route),
215 HttpRequestWrapperMatcher.same(redirect),
216 Mockito.<HttpClientContext>any(),
217 Mockito.<HttpExecutionAware>any())).thenReturn(response2);
218 Mockito.when(redirectStrategy.isRedirected(
219 Mockito.same(get),
220 Mockito.same(response1),
221 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
222 Mockito.when(redirectStrategy.getRedirect(
223 Mockito.same(get),
224 Mockito.same(response1),
225 Mockito.<HttpClientContext>any())).thenReturn(redirect);
226 Mockito.when(httpRoutePlanner.determineRoute(
227 Mockito.eq(target),
228 Mockito.<HttpRequestWrapper>any(),
229 Mockito.<HttpClientContext>any())).thenReturn(route);
230
231 redirectExec.execute(route, request, context, execAware);
232 }
233
234 @Test
235 public void testCrossSiteRedirect() throws Exception {
236 final HttpRoute route = new HttpRoute(target);
237 final HttpGet get = new HttpGet("/test");
238 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
239 final HttpClientContext context = HttpClientContext.create();
240
241 final AuthState targetAuthState = new AuthState();
242 targetAuthState.setState(AuthProtocolState.SUCCESS);
243 targetAuthState.update(new BasicScheme(), new UsernamePasswordCredentials("user", "pass"));
244 final AuthState proxyAuthState = new AuthState();
245 proxyAuthState.setState(AuthProtocolState.SUCCESS);
246 proxyAuthState.update(new NTLMScheme(), new NTCredentials("user", "pass", null, null));
247 context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState);
248 context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
249
250 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
251 final CloseableHttpResponse response2 = Mockito.mock(CloseableHttpResponse.class);
252 final HttpGet redirect = new HttpGet("http://otherhost/redirect");
253 Mockito.when(requestExecutor.execute(
254 Mockito.eq(route),
255 Mockito.same(request),
256 Mockito.<HttpClientContext>any(),
257 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
258 Mockito.when(requestExecutor.execute(
259 Mockito.eq(route),
260 HttpRequestWrapperMatcher.same(redirect),
261 Mockito.<HttpClientContext>any(),
262 Mockito.<HttpExecutionAware>any())).thenReturn(response2);
263 Mockito.when(redirectStrategy.isRedirected(
264 Mockito.same(get),
265 Mockito.same(response1),
266 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
267 Mockito.when(redirectStrategy.getRedirect(
268 Mockito.same(get),
269 Mockito.same(response1),
270 Mockito.<HttpClientContext>any())).thenReturn(redirect);
271 Mockito.when(httpRoutePlanner.determineRoute(
272 Mockito.eq(target),
273 Mockito.<HttpRequestWrapper>any(),
274 Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(new HttpHost("otherhost", 80)));
275
276 redirectExec.execute(route, request, context, execAware);
277
278 Assert.assertNotNull(context.getTargetAuthState());
279 Assert.assertEquals(AuthProtocolState.UNCHALLENGED, context.getTargetAuthState().getState());
280 Assert.assertEquals(null, context.getTargetAuthState().getAuthScheme());
281 Assert.assertNotNull(context.getProxyAuthState());
282 Assert.assertEquals(AuthProtocolState.UNCHALLENGED, context.getProxyAuthState().getState());
283 Assert.assertEquals(null, context.getProxyAuthState().getAuthScheme());
284 }
285
286 @Test(expected = RuntimeException.class)
287 public void testRedirectRuntimeException() throws Exception {
288 final HttpRoute route = new HttpRoute(target);
289 final HttpGet get = new HttpGet("/test");
290 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
291 final HttpClientContext context = HttpClientContext.create();
292
293 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
294 Mockito.when(requestExecutor.execute(
295 Mockito.eq(route),
296 Mockito.same(request),
297 Mockito.<HttpClientContext>any(),
298 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
299 Mockito.when(redirectStrategy.isRedirected(
300 Mockito.same(request),
301 Mockito.same(response1),
302 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
303 Mockito.doThrow(new RuntimeException("Oppsie")).when(redirectStrategy.getRedirect(
304 Mockito.same(request),
305 Mockito.same(response1),
306 Mockito.<HttpClientContext>any()));
307
308 try {
309 redirectExec.execute(route, request, context, execAware);
310 } catch (final Exception ex) {
311 Mockito.verify(response1).close();
312 throw ex;
313 }
314 }
315
316 @Test(expected = ProtocolException.class)
317 public void testRedirectProtocolException() throws Exception {
318 final HttpRoute route = new HttpRoute(target);
319 final HttpGet get = new HttpGet("/test");
320 final HttpRequestWrapper request = HttpRequestWrapper.wrap(get);
321 final HttpClientContext context = HttpClientContext.create();
322
323 final CloseableHttpResponse response1 = Mockito.mock(CloseableHttpResponse.class);
324 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
325 final HttpEntity entity1 = EntityBuilder.create()
326 .setStream(inStream1)
327 .build();
328 Mockito.when(response1.getEntity()).thenReturn(entity1);
329 Mockito.when(requestExecutor.execute(
330 Mockito.eq(route),
331 Mockito.same(request),
332 Mockito.<HttpClientContext>any(),
333 Mockito.<HttpExecutionAware>any())).thenReturn(response1);
334 Mockito.when(redirectStrategy.isRedirected(
335 Mockito.same(get),
336 Mockito.same(response1),
337 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
338 Mockito.doThrow(new ProtocolException("Oppsie")).when(redirectStrategy).getRedirect(
339 Mockito.same(get),
340 Mockito.same(response1),
341 Mockito.<HttpClientContext>any());
342
343 try {
344 redirectExec.execute(route, request, context, execAware);
345 } catch (final Exception ex) {
346 Mockito.verify(inStream1).close();
347 Mockito.verify(response1).close();
348 throw ex;
349 }
350 }
351
352 static class HttpRequestWrapperMatcher extends ArgumentMatcher<HttpRequestWrapper> {
353
354 private final HttpRequest original;
355
356 HttpRequestWrapperMatcher(final HttpRequest original) {
357 super();
358 this.original = original;
359 }
360 @Override
361 public boolean matches(final Object obj) {
362 final HttpRequestWrapper wrapper = (HttpRequestWrapper) obj;
363 return original == wrapper.getOriginal();
364 }
365
366 static HttpRequestWrapper same(final HttpRequest original) {
367 return Matchers.argThat(new HttpRequestWrapperMatcher(original));
368 }
369
370 }
371
372 }