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.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"}) // test code
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 }