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.nio.client.integration;
28
29 import java.io.IOException;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.concurrent.ExecutionException;
33 import java.util.concurrent.Future;
34
35 import org.apache.http.Consts;
36 import org.apache.http.localserver.HttpAsyncTestBase;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpException;
39 import org.apache.http.HttpHost;
40 import org.apache.http.HttpInetConnection;
41 import org.apache.http.HttpRequest;
42 import org.apache.http.HttpResponse;
43 import org.apache.http.HttpStatus;
44 import org.apache.http.HttpVersion;
45 import org.apache.http.ProtocolVersion;
46 import org.apache.http.auth.AuthScope;
47 import org.apache.http.auth.Credentials;
48 import org.apache.http.auth.UsernamePasswordCredentials;
49 import org.apache.http.client.CredentialsProvider;
50 import org.apache.http.client.config.RequestConfig;
51 import org.apache.http.client.methods.HttpGet;
52 import org.apache.http.client.methods.HttpPost;
53 import org.apache.http.client.methods.HttpPut;
54 import org.apache.http.client.protocol.HttpClientContext;
55 import org.apache.http.impl.client.BasicCredentialsProvider;
56 import org.apache.http.impl.client.TargetAuthenticationStrategy;
57 import org.apache.http.localserver.BasicAuthTokenExtractor;
58 import org.apache.http.localserver.RequestBasicAuth;
59 import org.apache.http.localserver.ResponseBasicUnauthorized;
60 import org.apache.http.message.BasicHeader;
61 import org.apache.http.message.BasicHttpResponse;
62 import org.apache.http.nio.entity.NByteArrayEntity;
63 import org.apache.http.nio.entity.NStringEntity;
64 import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
65 import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
66 import org.apache.http.nio.protocol.HttpAsyncExchange;
67 import org.apache.http.nio.protocol.HttpAsyncExpectationVerifier;
68 import org.apache.http.protocol.HTTP;
69 import org.apache.http.protocol.HttpContext;
70 import org.apache.http.protocol.HttpCoreContext;
71 import org.apache.http.protocol.HttpRequestHandler;
72 import org.junit.Assert;
73 import org.junit.Before;
74 import org.junit.Test;
75 import org.junit.runner.RunWith;
76 import org.junit.runners.Parameterized;
77
78 @RunWith(Parameterized.class)
79 public class TestClientAuthentication extends HttpAsyncTestBase {
80
81 @Parameterized.Parameters(name = "{0}")
82 public static Collection<Object[]> protocols() {
83 return Arrays.asList(new Object[][]{
84 {ProtocolScheme.http},
85 {ProtocolScheme.https},
86 });
87 }
88
89 public TestClientAuthentication(final ProtocolScheme scheme) {
90 super(scheme);
91 }
92
93 @Before @Override
94 public void setUp() throws Exception {
95 super.setUp();
96 this.serverBootstrap.addInterceptorFirst(new RequestBasicAuth());
97 this.serverBootstrap.addInterceptorLast(new ResponseBasicUnauthorized());
98 }
99
100 static class AuthHandler implements HttpRequestHandler {
101
102 private final boolean keepAlive;
103
104 AuthHandler(final boolean keepAlive) {
105 super();
106 this.keepAlive = keepAlive;
107 }
108
109 AuthHandler() {
110 this(true);
111 }
112
113 @Override
114 public void handle(
115 final HttpRequest request,
116 final HttpResponse response,
117 final HttpContext context) throws HttpException, IOException {
118 final String creds = (String) context.getAttribute("creds");
119 if (creds == null || !creds.equals("test:test")) {
120 response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
121 } else {
122 response.setStatusCode(HttpStatus.SC_OK);
123 final NStringEntity entity = new NStringEntity("success", Consts.ASCII);
124 response.setEntity(entity);
125 }
126 response.setHeader(HTTP.CONN_DIRECTIVE,
127 this.keepAlive ? HTTP.CONN_KEEP_ALIVE : HTTP.CONN_CLOSE);
128 }
129
130 }
131
132 static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy {
133
134 private int count;
135
136 public TestTargetAuthenticationStrategy() {
137 super();
138 this.count = 0;
139 }
140
141 @Override
142 public boolean isAuthenticationRequested(
143 final HttpHost authhost,
144 final HttpResponse response,
145 final HttpContext context) {
146 final boolean res = super.isAuthenticationRequested(authhost, response, context);
147 if (res) {
148 synchronized (this) {
149 this.count++;
150 }
151 }
152 return res;
153 }
154
155 public int getCount() {
156 synchronized (this) {
157 return this.count;
158 }
159 }
160
161 }
162
163 static class AuthExpectationVerifier implements HttpAsyncExpectationVerifier {
164
165 private final BasicAuthTokenExtractor authTokenExtractor;
166
167 public AuthExpectationVerifier() {
168 super();
169 this.authTokenExtractor = new BasicAuthTokenExtractor();
170 }
171
172 @Override
173 public void verify(
174 final HttpAsyncExchange httpexchange,
175 final HttpContext context) throws HttpException, IOException {
176 final HttpRequest request = httpexchange.getRequest();
177 ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
178 if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
179 ver = HttpVersion.HTTP_1_1;
180 }
181 final String creds = this.authTokenExtractor.extract(request);
182 if (creds == null || !creds.equals("test:test")) {
183 final HttpResponse response = new BasicHttpResponse(ver, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
184 httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
185 } else {
186 httpexchange.submitResponse();
187 }
188 }
189
190 }
191
192 static class TestCredentialsProvider implements CredentialsProvider {
193
194 private final Credentials creds;
195 private AuthScope authscope;
196
197 TestCredentialsProvider(final Credentials creds) {
198 super();
199 this.creds = creds;
200 }
201
202 @Override
203 public void clear() {
204 }
205
206 @Override
207 public Credentials getCredentials(final AuthScope authscope) {
208 this.authscope = authscope;
209 return this.creds;
210 }
211
212 @Override
213 public void setCredentials(final AuthScope authscope, final Credentials credentials) {
214 }
215
216 public AuthScope getAuthScope() {
217 return this.authscope;
218 }
219
220 }
221
222 @Test
223 public void testBasicAuthenticationNoCreds() throws Exception {
224 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
225 final HttpHost target = start();
226
227 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
228 final HttpClientContext context = HttpClientContext.create();
229 context.setCredentialsProvider(credsProvider);
230 final HttpGet httpget = new HttpGet("/");
231 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
232 final HttpResponse response = future.get();
233 Assert.assertNotNull(response);
234 Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
235 final AuthScope authscope = credsProvider.getAuthScope();
236 Assert.assertNotNull(authscope);
237 Assert.assertEquals("test realm", authscope.getRealm());
238 }
239
240 @Test
241 public void testBasicAuthenticationFailure() throws Exception {
242 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
243 final HttpHost target = start();
244
245 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
246 new UsernamePasswordCredentials("test", "all-wrong"));
247 final HttpClientContext context = HttpClientContext.create();
248 context.setCredentialsProvider(credsProvider);
249 final HttpGet httpget = new HttpGet("/");
250 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
251 final HttpResponse response = future.get();
252 Assert.assertNotNull(response);
253 Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
254 final AuthScope authscope = credsProvider.getAuthScope();
255 Assert.assertNotNull(authscope);
256 Assert.assertEquals("test realm", authscope.getRealm());
257 }
258
259 @Test
260 public void testBasicAuthenticationSuccess() throws Exception {
261 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
262 final HttpHost target = start();
263
264 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
265 new UsernamePasswordCredentials("test", "test"));
266 final HttpClientContext context = HttpClientContext.create();
267 context.setCredentialsProvider(credsProvider);
268 final HttpGet httpget = new HttpGet("/");
269 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
270 final HttpResponse response = future.get();
271 Assert.assertNotNull(response);
272 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
273 final AuthScope authscope = credsProvider.getAuthScope();
274 Assert.assertNotNull(authscope);
275 Assert.assertEquals("test realm", authscope.getRealm());
276 }
277
278 @Test
279 public void testBasicAuthenticationSuccessNonPersistentConnection() throws Exception {
280 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler(false)));
281 final HttpHost target = start();
282
283 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
284 new UsernamePasswordCredentials("test", "test"));
285 final HttpClientContext context = HttpClientContext.create();
286 context.setCredentialsProvider(credsProvider);
287
288 final HttpGet httpget = new HttpGet("/");
289 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
290 final HttpResponse response = future.get();
291 Assert.assertNotNull(response);
292 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
293 final AuthScope authscope = credsProvider.getAuthScope();
294 Assert.assertNotNull(authscope);
295 Assert.assertEquals("test realm", authscope.getRealm());
296 }
297
298 @Test
299 public void testBasicAuthenticationSuccessWithNonRepeatableExpectContinue() throws Exception {
300 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
301 this.serverBootstrap.setExpectationVerifier(new AuthExpectationVerifier());
302 final HttpHost target = start();
303
304 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
305 new UsernamePasswordCredentials("test", "test"));
306 final HttpClientContext context = HttpClientContext.create();
307 context.setCredentialsProvider(credsProvider);
308 final HttpPut httpput = new HttpPut("/");
309
310 final NByteArrayEntity entity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
311
312 @Override
313 public boolean isRepeatable() {
314 return false;
315 }
316
317 };
318
319 httpput.setEntity(entity);
320 httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
321
322 final Future<HttpResponse> future = this.httpclient.execute(target, httpput, context, null);
323 final HttpResponse response = future.get();
324 Assert.assertNotNull(response);
325 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
326 }
327
328 @Test(expected=ExecutionException.class)
329 public void testBasicAuthenticationFailureWithNonRepeatableEntityExpectContinueOff() throws Exception {
330 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
331 final HttpHost target = start();
332
333 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
334 new UsernamePasswordCredentials("test", "test"));
335 final HttpClientContext context = HttpClientContext.create();
336 context.setCredentialsProvider(credsProvider);
337 final HttpPut httpput = new HttpPut("/");
338
339 final NByteArrayEntity requestEntity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
340
341 @Override
342 public boolean isRepeatable() {
343 return false;
344 }
345
346 };
347
348 httpput.setEntity(requestEntity);
349 httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(false).build());
350
351 try {
352 final Future<HttpResponse> future = this.httpclient.execute(target, httpput, context, null);
353 future.get();
354 Assert.fail("ExecutionException should have been thrown");
355 } catch (final ExecutionException ex) {
356 final Throwable cause = ex.getCause();
357 Assert.assertNotNull(cause);
358 throw ex;
359 }
360 }
361
362 @Test
363 public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception {
364 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
365 final HttpHost target = start();
366
367 final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
368 new UsernamePasswordCredentials("test", "test"));
369 final HttpClientContext context = HttpClientContext.create();
370 context.setCredentialsProvider(credsProvider);
371 final HttpPost httppost = new HttpPost("/");
372 httppost.setEntity(new NStringEntity("some important stuff", Consts.ISO_8859_1));
373
374 final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
375 final HttpResponse response = future.get();
376 Assert.assertNotNull(response);
377 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
378 final AuthScope authscope = credsProvider.getAuthScope();
379 Assert.assertNotNull(authscope);
380 Assert.assertEquals("test realm", authscope.getRealm());
381 }
382
383 @Test
384 public void testBasicAuthenticationCredentialsCaching() throws Exception {
385 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
386 final TestTargetAuthenticationStrategy authStrategy = new TestTargetAuthenticationStrategy();
387 this.clientBuilder.setTargetAuthenticationStrategy(authStrategy);
388 final HttpHost target = start();
389
390 final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
391 credsProvider.setCredentials(AuthScope.ANY,
392 new UsernamePasswordCredentials("test", "test"));
393 final HttpClientContext context = HttpClientContext.create();
394 context.setCredentialsProvider(credsProvider);
395
396 final HttpGet httpget1 = new HttpGet("/");
397 final Future<HttpResponse> future1 = this.httpclient.execute(target, httpget1, context, null);
398 final HttpResponse response1 = future1.get();
399 Assert.assertNotNull(response1);
400 Assert.assertEquals(HttpStatus.SC_OK, response1.getStatusLine().getStatusCode());
401
402 final HttpGet httpget2 = new HttpGet("/");
403 final Future<HttpResponse> future2 = this.httpclient.execute(target, httpget2, context, null);
404 final HttpResponse response2 = future2.get();
405 Assert.assertNotNull(response2);
406 Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
407
408 Assert.assertEquals(1, authStrategy.getCount());
409 }
410
411 @Test
412 public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
413 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
414 final HttpHost target = start();
415
416 final HttpGet httpget = new HttpGet("http://test:test@" + target.toHostString() + "/");
417 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
418 final HttpResponse response = future.get();
419 Assert.assertNotNull(response);
420 final HttpEntity entity = response.getEntity();
421 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
422 Assert.assertNotNull(entity);
423 }
424
425 @Test
426 public void testAuthenticationUserinfoInRequestFailure() throws Exception {
427 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
428 final HttpHost target = start();
429
430 final HttpGet httpget = new HttpGet("http://test:all-wrong@" + target.toHostString() + "/");
431
432 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
433 final HttpResponse response = future.get();
434 Assert.assertNotNull(response);
435 final HttpEntity entity = response.getEntity();
436 Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
437 Assert.assertNotNull(entity);
438 }
439
440 private class RedirectHandler implements HttpRequestHandler {
441
442 public RedirectHandler() {
443 super();
444 }
445
446 @Override
447 public void handle(
448 final HttpRequest request,
449 final HttpResponse response,
450 final HttpContext context) throws HttpException, IOException {
451 final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
452 final int port = conn.getLocalPort();
453 response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
454 response.addHeader(new BasicHeader("Location", getSchemeName() + "://test:test@localhost:" + port + "/"));
455 }
456
457 }
458
459 @Test
460 public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
461 this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
462 this.serverBootstrap.registerHandler("/thatway", new BasicAsyncRequestHandler(new RedirectHandler()));
463 final HttpHost target = start();
464
465 final HttpGet httpget = new HttpGet(target.getSchemeName() + "://test:test@" + target.toHostString() + "/thatway");
466 final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
467 final HttpResponse response = future.get();
468 Assert.assertNotNull(response);
469 final HttpEntity entity = response.getEntity();
470 Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
471 Assert.assertNotNull(entity);
472 }
473
474 }