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 import java.io.InputStream;
33 import java.net.URI;
34 import java.util.Collections;
35 import java.util.Map;
36
37 import org.apache.hc.client5.http.AuthenticationStrategy;
38 import org.apache.hc.client5.http.HttpRoute;
39 import org.apache.hc.client5.http.auth.AuthChallenge;
40 import org.apache.hc.client5.http.auth.AuthExchange;
41 import org.apache.hc.client5.http.auth.AuthScheme;
42 import org.apache.hc.client5.http.auth.AuthScope;
43 import org.apache.hc.client5.http.auth.ChallengeType;
44 import org.apache.hc.client5.http.auth.Credentials;
45 import org.apache.hc.client5.http.auth.CredentialsProvider;
46 import org.apache.hc.client5.http.auth.StandardAuthScheme;
47 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
48 import org.apache.hc.client5.http.classic.ExecChain;
49 import org.apache.hc.client5.http.classic.ExecRuntime;
50 import org.apache.hc.client5.http.classic.methods.HttpGet;
51 import org.apache.hc.client5.http.classic.methods.HttpPost;
52 import org.apache.hc.client5.http.entity.EntityBuilder;
53 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
54 import org.apache.hc.client5.http.impl.auth.BasicScheme;
55 import org.apache.hc.client5.http.impl.auth.NTLMScheme;
56 import org.apache.hc.client5.http.protocol.HttpClientContext;
57 import org.apache.hc.core5.http.ClassicHttpRequest;
58 import org.apache.hc.core5.http.ClassicHttpResponse;
59 import org.apache.hc.core5.http.EntityDetails;
60 import org.apache.hc.core5.http.HttpException;
61 import org.apache.hc.core5.http.HttpHeaders;
62 import org.apache.hc.core5.http.HttpHost;
63 import org.apache.hc.core5.http.HttpResponse;
64 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
65 import org.apache.hc.core5.http.protocol.HttpContext;
66 import org.apache.hc.core5.http.protocol.HttpProcessor;
67 import org.junit.Assert;
68 import org.junit.Before;
69 import org.junit.Test;
70 import org.mockito.Mock;
71 import org.mockito.Mockito;
72 import org.mockito.MockitoAnnotations;
73 import org.mockito.invocation.InvocationOnMock;
74 import org.mockito.stubbing.Answer;
75
76 @SuppressWarnings({"static-access"})
77 public class TestProtocolExec {
78
79 @Mock
80 private HttpProcessor httpProcessor;
81 @Mock
82 private AuthenticationStrategy targetAuthStrategy;
83 @Mock
84 private AuthenticationStrategy proxyAuthStrategy;
85 @Mock
86 private ExecChain chain;
87 @Mock
88 private ExecRuntime execRuntime;
89
90 private ProtocolExec protocolExec;
91 private HttpHost target;
92 private HttpHost proxy;
93
94 @Before
95 public void setup() throws Exception {
96 MockitoAnnotations.initMocks(this);
97 protocolExec = new ProtocolExec(httpProcessor, targetAuthStrategy, proxyAuthStrategy);
98 target = new HttpHost("foo", 80);
99 proxy = new HttpHost("bar", 8888);
100 }
101
102 @Test
103 public void testFundamentals() throws Exception {
104 final HttpRoute route = new HttpRoute(target);
105 final ClassicHttpRequest request = new HttpGet("/test");
106 final HttpClientContext context = HttpClientContext.create();
107
108 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
109
110 Mockito.when(chain.proceed(
111 Mockito.<ClassicHttpRequest>any(),
112 Mockito.<ExecChain.Scope>any())).thenReturn(response);
113
114 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
115 protocolExec.execute(request, scope, chain);
116
117 Mockito.verify(httpProcessor).process(request, null, context);
118 Mockito.verify(chain).proceed(request, scope);
119 Mockito.verify(httpProcessor).process(response, null, context);
120
121 Assert.assertEquals(route, context.getHttpRoute());
122 Assert.assertSame(request, context.getRequest());
123 Assert.assertSame(response, context.getResponse());
124 }
125
126 @Test
127 public void testUserInfoInRequestURI() throws Exception {
128 final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080));
129 final ClassicHttpRequest request = new HttpGet("http://somefella:secret@bar/test");
130 final HttpClientContext context = HttpClientContext.create();
131 context.setCredentialsProvider(new BasicCredentialsProvider());
132
133 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
134 Mockito.when(chain.proceed(
135 Mockito.<ClassicHttpRequest>any(),
136 Mockito.<ExecChain.Scope>any())).thenReturn(response);
137
138 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
139 protocolExec.execute(request, scope, chain);
140 Assert.assertEquals(new URI("http://bar/test"), request.getUri());
141 final CredentialsProvider credentialsProvider = context.getCredentialsProvider();
142 final Credentials creds = credentialsProvider.getCredentials(new AuthScope(null, "bar", -1, null, null), null);
143 Assert.assertNotNull(creds);
144 Assert.assertEquals("somefella", creds.getUserPrincipal().getName());
145 }
146
147 @Test(expected = HttpException.class)
148 public void testPostProcessHttpException() throws Exception {
149 final HttpRoute route = new HttpRoute(target);
150 final ClassicHttpRequest request = new HttpGet("/test");
151 final HttpClientContext context = HttpClientContext.create();
152
153 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
154
155 Mockito.when(chain.proceed(
156 Mockito.<ClassicHttpRequest>any(),
157 Mockito.<ExecChain.Scope>any())).thenReturn(response);
158 Mockito.doThrow(new HttpException("Ooopsie")).when(httpProcessor).process(
159 Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
160 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
161 try {
162 protocolExec.execute(request, scope, chain);
163 } catch (final Exception ex) {
164 Mockito.verify(execRuntime).discardEndpoint();
165 throw ex;
166 }
167 }
168
169 @Test(expected = IOException.class)
170 public void testPostProcessIOException() throws Exception {
171 final HttpRoute route = new HttpRoute(target);
172 final ClassicHttpRequest request = new HttpGet("/test");
173 final HttpClientContext context = HttpClientContext.create();
174
175 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
176 Mockito.when(chain.proceed(
177 Mockito.<ClassicHttpRequest>any(),
178 Mockito.<ExecChain.Scope>any())).thenReturn(response);
179 Mockito.doThrow(new IOException("Ooopsie")).when(httpProcessor).process(
180 Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
181 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
182 try {
183 protocolExec.execute(request, scope, chain);
184 } catch (final Exception ex) {
185 Mockito.verify(execRuntime).discardEndpoint();
186 throw ex;
187 }
188 }
189
190 @Test(expected = RuntimeException.class)
191 public void testPostProcessRuntimeException() throws Exception {
192 final HttpRoute route = new HttpRoute(target);
193 final ClassicHttpRequest request = new HttpGet("/test");
194 final HttpClientContext context = HttpClientContext.create();
195
196 final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
197 Mockito.when(chain.proceed(
198 Mockito.<ClassicHttpRequest>any(),
199 Mockito.<ExecChain.Scope>any())).thenReturn(response);
200 Mockito.doThrow(new RuntimeException("Ooopsie")).when(httpProcessor).process(
201 Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
202 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
203 try {
204 protocolExec.execute(request, scope, chain);
205 } catch (final Exception ex) {
206 Mockito.verify(execRuntime).discardEndpoint();
207 throw ex;
208 }
209 }
210
211 @Test
212 public void testExecRequestRetryOnAuthChallenge() throws Exception {
213 final HttpRoute route = new HttpRoute(target);
214 final HttpClientContext context = new HttpClientContext();
215 final ClassicHttpRequest request = new HttpGet("http://foo/test");
216 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
217 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
218 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
219 response1.setEntity(EntityBuilder.create()
220 .setStream(inStream1)
221 .build());
222 final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
223 final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
224 response2.setEntity(EntityBuilder.create()
225 .setStream(inStream2)
226 .build());
227
228 final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
229 credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
230 context.setCredentialsProvider(credentialsProvider);
231
232 Mockito.when(chain.proceed(
233 Mockito.same(request),
234 Mockito.<ExecChain.Scope>any())).thenReturn(response1, response2);
235 Mockito.when(targetAuthStrategy.select(
236 Mockito.eq(ChallengeType.TARGET),
237 Mockito.<Map<String, AuthChallenge>>any(),
238 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
239 Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
240
241 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
242 final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
243 Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
244 Mockito.verify(inStream1).close();
245 Mockito.verify(inStream2, Mockito.never()).close();
246
247 Assert.assertNotNull(finalResponse);
248 Assert.assertEquals(200, finalResponse.getCode());
249 }
250
251 @Test
252 public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
253 final HttpRoute route = new HttpRoute(target);
254 final ClassicHttpRequest request = new HttpGet("http://foo/test");
255 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
256 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
257 final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
258 response1.setEntity(EntityBuilder.create()
259 .setStream(inStream1)
260 .build());
261 final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
262 final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
263 response2.setEntity(EntityBuilder.create()
264 .setStream(inStream2)
265 .build());
266
267 final HttpClientContext context = new HttpClientContext();
268
269 final AuthExchange authExchange = new AuthExchange();
270 authExchange.setState(AuthExchange.State.SUCCESS);
271 authExchange.select(new NTLMScheme());
272 context.setAuthExchange(target, authExchange);
273
274 final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
275 credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
276 context.setCredentialsProvider(credentialsProvider);
277
278 Mockito.when(chain.proceed(
279 Mockito.same(request),
280 Mockito.<ExecChain.Scope>any())).thenReturn(response1, response2);
281
282 Mockito.when(targetAuthStrategy.select(
283 Mockito.eq(ChallengeType.TARGET),
284 Mockito.<Map<String, AuthChallenge>>any(),
285 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
286
287 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
288 final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
289 Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
290 Mockito.verify(execRuntime).disconnectEndpoint();
291 Mockito.verify(inStream2, Mockito.never()).close();
292
293 Assert.assertNotNull(finalResponse);
294 Assert.assertEquals(200, finalResponse.getCode());
295 Assert.assertNotNull(authExchange.getAuthScheme());
296 }
297
298 @Test
299 public void testExecEntityEnclosingRequest() throws Exception {
300 final HttpRoute route = new HttpRoute(target);
301 final HttpClientContext context = new HttpClientContext();
302 final HttpPost request = new HttpPost("http://foo/test");
303 final InputStream inStream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
304 request.setEntity(EntityBuilder.create()
305 .setStream(inStream0)
306 .build());
307 final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
308 response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
309 final InputStream inStream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
310 response1.setEntity(EntityBuilder.create()
311 .setStream(inStream1)
312 .build());
313
314 final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
315 credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
316 context.setCredentialsProvider(credentialsProvider);
317
318 Mockito.when(chain.proceed(
319 Mockito.same(request),
320 Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<HttpResponse>() {
321
322 @Override
323 public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
324 final Object[] args = invocationOnMock.getArguments();
325 final ClassicHttpRequest requestEE = (ClassicHttpRequest) args[0];
326 requestEE.getEntity().writeTo(new ByteArrayOutputStream());
327 return response1;
328 }
329
330 });
331
332 Mockito.when(targetAuthStrategy.select(
333 Mockito.eq(ChallengeType.TARGET),
334 Mockito.<Map<String, AuthChallenge>>any(),
335 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
336
337 final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
338 final ClassicHttpResponse response = protocolExec.execute(request, scope, chain);
339 Assert.assertEquals(401, response.getCode());
340 }
341
342 }