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.nio.client.integration;
28  
29  import java.io.IOException;
30  import java.net.InetSocketAddress;
31  import java.util.concurrent.ExecutionException;
32  import java.util.concurrent.Future;
33  
34  import org.apache.http.Consts;
35  import org.apache.http.HttpAsyncTestBase;
36  import org.apache.http.HttpEntity;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpHost;
39  import org.apache.http.HttpInetConnection;
40  import org.apache.http.HttpRequest;
41  import org.apache.http.HttpRequestInterceptor;
42  import org.apache.http.HttpResponse;
43  import org.apache.http.HttpResponseInterceptor;
44  import org.apache.http.HttpStatus;
45  import org.apache.http.HttpVersion;
46  import org.apache.http.ProtocolVersion;
47  import org.apache.http.auth.AuthScope;
48  import org.apache.http.auth.Credentials;
49  import org.apache.http.auth.UsernamePasswordCredentials;
50  import org.apache.http.client.CredentialsProvider;
51  import org.apache.http.client.config.RequestConfig;
52  import org.apache.http.client.methods.HttpGet;
53  import org.apache.http.client.methods.HttpPost;
54  import org.apache.http.client.methods.HttpPut;
55  import org.apache.http.config.ConnectionConfig;
56  import org.apache.http.impl.DefaultConnectionReuseStrategy;
57  import org.apache.http.impl.DefaultHttpResponseFactory;
58  import org.apache.http.impl.client.BasicCredentialsProvider;
59  import org.apache.http.impl.client.TargetAuthenticationStrategy;
60  import org.apache.http.impl.nio.DefaultNHttpServerConnection;
61  import org.apache.http.impl.nio.DefaultNHttpServerConnectionFactory;
62  import org.apache.http.impl.nio.client.HttpAsyncClients;
63  import org.apache.http.localserver.BasicAuthTokenExtractor;
64  import org.apache.http.localserver.RequestBasicAuth;
65  import org.apache.http.localserver.ResponseBasicUnauthorized;
66  import org.apache.http.message.BasicHeader;
67  import org.apache.http.message.BasicHttpResponse;
68  import org.apache.http.nio.NHttpConnectionFactory;
69  import org.apache.http.nio.entity.NByteArrayEntity;
70  import org.apache.http.nio.entity.NStringEntity;
71  import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
72  import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
73  import org.apache.http.nio.protocol.HttpAsyncExchange;
74  import org.apache.http.nio.protocol.HttpAsyncExpectationVerifier;
75  import org.apache.http.nio.protocol.HttpAsyncRequestHandlerMapper;
76  import org.apache.http.nio.protocol.HttpAsyncService;
77  import org.apache.http.nio.protocol.UriHttpAsyncRequestHandlerMapper;
78  import org.apache.http.nio.reactor.IOReactorStatus;
79  import org.apache.http.nio.reactor.ListenerEndpoint;
80  import org.apache.http.protocol.BasicHttpContext;
81  import org.apache.http.protocol.HTTP;
82  import org.apache.http.protocol.HttpContext;
83  import org.apache.http.protocol.HttpCoreContext;
84  import org.apache.http.protocol.HttpRequestHandler;
85  import org.apache.http.protocol.ImmutableHttpProcessor;
86  import org.apache.http.protocol.ResponseConnControl;
87  import org.apache.http.protocol.ResponseContent;
88  import org.apache.http.protocol.ResponseDate;
89  import org.apache.http.protocol.ResponseServer;
90  import org.junit.After;
91  import org.junit.Assert;
92  import org.junit.Before;
93  import org.junit.Test;
94  
95  public class TestClientAuthentication extends HttpAsyncTestBase {
96  
97      @Before
98      public void setUp() throws Exception {
99          initServer();
100         initConnectionManager();
101     }
102 
103     @After
104     public void tearDown() throws Exception {
105         shutDownClient();
106         shutDownServer();
107     }
108 
109     @Override
110     public void initServer() throws Exception {
111         super.initServer();
112         this.serverHttpProc = new ImmutableHttpProcessor(
113                 new HttpRequestInterceptor[] {
114                         new RequestBasicAuth()
115                 },
116                 new HttpResponseInterceptor[] {
117                         new ResponseDate(),
118                         new ResponseServer(),
119                         new ResponseContent(),
120                         new ResponseConnControl(),
121                         new ResponseBasicUnauthorized()
122                 }
123         );
124     }
125 
126     @Override
127     protected NHttpConnectionFactory<DefaultNHttpServerConnection> createServerConnectionFactory(
128             final ConnectionConfig config) throws Exception {
129         return new DefaultNHttpServerConnectionFactory(config);
130     }
131 
132     @Override
133     protected String getSchemeName() {
134         return "http";
135     }
136 
137     private HttpHost start(
138             final HttpAsyncRequestHandlerMapper requestHandlerResolver,
139             final HttpAsyncExpectationVerifier expectationVerifier) throws Exception {
140         final HttpAsyncService serviceHandler = new HttpAsyncService(
141                 this.serverHttpProc,
142                 DefaultConnectionReuseStrategy.INSTANCE,
143                 DefaultHttpResponseFactory.INSTANCE,
144                 requestHandlerResolver,
145                 expectationVerifier);
146         this.server.start(serviceHandler);
147         this.httpclient.start();
148 
149         final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
150         endpoint.waitFor();
151 
152         Assert.assertEquals("Test server status", IOReactorStatus.ACTIVE, this.server.getStatus());
153         final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
154         return new HttpHost("localhost", address.getPort(), getSchemeName());
155     }
156 
157     static class AuthHandler implements HttpRequestHandler {
158 
159         private final boolean keepAlive;
160 
161         AuthHandler(final boolean keepAlive) {
162             super();
163             this.keepAlive = keepAlive;
164         }
165 
166         AuthHandler() {
167             this(true);
168         }
169 
170         public void handle(
171                 final HttpRequest request,
172                 final HttpResponse response,
173                 final HttpContext context) throws HttpException, IOException {
174             final String creds = (String) context.getAttribute("creds");
175             if (creds == null || !creds.equals("test:test")) {
176                 response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
177             } else {
178                 response.setStatusCode(HttpStatus.SC_OK);
179                 final NStringEntity entity = new NStringEntity("success", Consts.ASCII);
180                 response.setEntity(entity);
181             }
182             response.setHeader(HTTP.CONN_DIRECTIVE,
183                     this.keepAlive ? HTTP.CONN_KEEP_ALIVE : HTTP.CONN_CLOSE);
184         }
185 
186     }
187 
188     static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy {
189 
190         private int count;
191 
192         public TestTargetAuthenticationStrategy() {
193             super();
194             this.count = 0;
195         }
196 
197         @Override
198         public boolean isAuthenticationRequested(
199                 final HttpHost authhost,
200                 final HttpResponse response,
201                 final HttpContext context) {
202             final boolean res = super.isAuthenticationRequested(authhost, response, context);
203             if (res) {
204                 synchronized (this) {
205                     this.count++;
206                 }
207             }
208             return res;
209         }
210 
211         public int getCount() {
212             synchronized (this) {
213                 return this.count;
214             }
215         }
216 
217     }
218 
219     static class AuthExpectationVerifier implements HttpAsyncExpectationVerifier {
220 
221         private final BasicAuthTokenExtractor authTokenExtractor;
222 
223         public AuthExpectationVerifier() {
224             super();
225             this.authTokenExtractor = new BasicAuthTokenExtractor();
226         }
227 
228         public void verify(
229                 final HttpAsyncExchange httpexchange,
230                 final HttpContext context) throws HttpException, IOException {
231             final HttpRequest request = httpexchange.getRequest();
232             ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
233             if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
234                 ver = HttpVersion.HTTP_1_1;
235             }
236             final String creds = this.authTokenExtractor.extract(request);
237             if (creds == null || !creds.equals("test:test")) {
238                 final HttpResponse response = new BasicHttpResponse(ver, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
239                 httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
240             } else {
241                 httpexchange.submitResponse();
242             }
243         }
244 
245     }
246 
247     static class TestCredentialsProvider implements CredentialsProvider {
248 
249         private final Credentials creds;
250         private AuthScope authscope;
251 
252         TestCredentialsProvider(final Credentials creds) {
253             super();
254             this.creds = creds;
255         }
256 
257         public void clear() {
258         }
259 
260         public Credentials getCredentials(final AuthScope authscope) {
261             this.authscope = authscope;
262             return this.creds;
263         }
264 
265         public void setCredentials(final AuthScope authscope, final Credentials credentials) {
266         }
267 
268         public AuthScope getAuthScope() {
269             return this.authscope;
270         }
271 
272     }
273 
274     @Test
275     public void testBasicAuthenticationNoCreds() throws Exception {
276         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
277         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
278 
279         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
280         this.httpclient = HttpAsyncClients.custom()
281             .setConnectionManager(this.connMgr)
282             .setDefaultCredentialsProvider(credsProvider)
283             .build();
284 
285         final HttpHost target = start(registry, null);
286 
287         final HttpGet httpget = new HttpGet("/");
288         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
289         final HttpResponse response = future.get();
290         Assert.assertNotNull(response);
291         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
292         final AuthScope authscope = credsProvider.getAuthScope();
293         Assert.assertNotNull(authscope);
294         Assert.assertEquals("test realm", authscope.getRealm());
295     }
296 
297     @Test
298     public void testBasicAuthenticationFailure() throws Exception {
299         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
300         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
301 
302         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
303                 new UsernamePasswordCredentials("test", "all-wrong"));
304         this.httpclient = HttpAsyncClients.custom()
305             .setConnectionManager(this.connMgr)
306             .setDefaultCredentialsProvider(credsProvider)
307             .build();
308 
309         final HttpHost target = start(registry, null);
310 
311         final HttpGet httpget = new HttpGet("/");
312         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
313         final HttpResponse response = future.get();
314         Assert.assertNotNull(response);
315         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
316         final AuthScope authscope = credsProvider.getAuthScope();
317         Assert.assertNotNull(authscope);
318         Assert.assertEquals("test realm", authscope.getRealm());
319     }
320 
321     @Test
322     public void testBasicAuthenticationSuccess() throws Exception {
323         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
324         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
325 
326         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
327                 new UsernamePasswordCredentials("test", "test"));
328         this.httpclient = HttpAsyncClients.custom()
329             .setConnectionManager(this.connMgr)
330             .setDefaultCredentialsProvider(credsProvider)
331             .build();
332 
333         final HttpHost target = start(registry, null);
334 
335         final HttpGet httpget = new HttpGet("/");
336         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
337         final HttpResponse response = future.get();
338         Assert.assertNotNull(response);
339         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
340         final AuthScope authscope = credsProvider.getAuthScope();
341         Assert.assertNotNull(authscope);
342         Assert.assertEquals("test realm", authscope.getRealm());
343     }
344 
345     @Test
346     public void testBasicAuthenticationSuccessNonPersistentConnection() throws Exception {
347         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
348         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler(false)));
349 
350         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
351                 new UsernamePasswordCredentials("test", "test"));
352         this.httpclient = HttpAsyncClients.custom()
353             .setConnectionManager(this.connMgr)
354             .setDefaultCredentialsProvider(credsProvider)
355             .build();
356 
357         final HttpHost target = start(registry, null);
358 
359         final HttpGet httpget = new HttpGet("/");
360         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
361         final HttpResponse response = future.get();
362         Assert.assertNotNull(response);
363         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
364         final AuthScope authscope = credsProvider.getAuthScope();
365         Assert.assertNotNull(authscope);
366         Assert.assertEquals("test realm", authscope.getRealm());
367     }
368 
369     @Test
370     public void testBasicAuthenticationSuccessWithNonRepeatableExpectContinue() throws Exception {
371         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
372         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
373         final AuthExpectationVerifier expectationVerifier = new AuthExpectationVerifier();
374 
375         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
376                 new UsernamePasswordCredentials("test", "test"));
377 
378         this.httpclient = HttpAsyncClients.custom()
379             .setConnectionManager(this.connMgr)
380             .setDefaultCredentialsProvider(credsProvider)
381             .build();
382 
383         final HttpHost target = start(registry, expectationVerifier);
384 
385         final HttpPut httpput = new HttpPut("/");
386 
387         final NByteArrayEntity entity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
388 
389             @Override
390             public boolean isRepeatable() {
391                 return false;
392             }
393 
394         };
395 
396         httpput.setEntity(entity);
397         httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
398 
399         final Future<HttpResponse> future = this.httpclient.execute(target, httpput, null);
400         final HttpResponse response = future.get();
401         Assert.assertNotNull(response);
402         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
403     }
404 
405     @Test(expected=ExecutionException.class)
406     public void testBasicAuthenticationFailureWithNonRepeatableEntityExpectContinueOff() throws Exception {
407         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
408         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
409 
410         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
411                 new UsernamePasswordCredentials("test", "test"));
412 
413         this.httpclient = HttpAsyncClients.custom()
414             .setConnectionManager(this.connMgr)
415             .setDefaultCredentialsProvider(credsProvider)
416             .build();
417 
418         final HttpHost target = start(registry, null);
419 
420         final HttpPut httpput = new HttpPut("/");
421 
422         final NByteArrayEntity requestEntity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
423 
424             @Override
425             public boolean isRepeatable() {
426                 return false;
427             }
428 
429         };
430 
431         httpput.setEntity(requestEntity);
432         httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(false).build());
433 
434         try {
435             final Future<HttpResponse> future = this.httpclient.execute(target, httpput, null);
436             future.get();
437             Assert.fail("ExecutionException should have been thrown");
438         } catch (final ExecutionException ex) {
439             final Throwable cause = ex.getCause();
440             Assert.assertNotNull(cause);
441             throw ex;
442         }
443     }
444 
445     @Test
446     public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception {
447         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
448         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
449 
450         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
451                 new UsernamePasswordCredentials("test", "test"));
452 
453         this.httpclient = HttpAsyncClients.custom()
454             .setConnectionManager(this.connMgr)
455             .setDefaultCredentialsProvider(credsProvider)
456             .build();
457 
458         final HttpHost target = start(registry, null);
459 
460         final HttpPost httppost = new HttpPost("/");
461         httppost.setEntity(new NStringEntity("some important stuff", Consts.ISO_8859_1));
462 
463         final Future<HttpResponse> future = this.httpclient.execute(target, httppost, null);
464         final HttpResponse response = future.get();
465         Assert.assertNotNull(response);
466         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
467         final AuthScope authscope = credsProvider.getAuthScope();
468         Assert.assertNotNull(authscope);
469         Assert.assertEquals("test realm", authscope.getRealm());
470     }
471 
472     @Test
473     public void testBasicAuthenticationCredentialsCaching() throws Exception {
474         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
475         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
476 
477         final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
478         credsProvider.setCredentials(AuthScope.ANY,
479                 new UsernamePasswordCredentials("test", "test"));
480 
481         final TestTargetAuthenticationStrategy authStrategy = new TestTargetAuthenticationStrategy();
482 
483         this.httpclient = HttpAsyncClients.custom()
484             .setConnectionManager(this.connMgr)
485             .setTargetAuthenticationStrategy(authStrategy)
486             .setDefaultCredentialsProvider(credsProvider)
487             .build();
488 
489         final HttpHost target = start(registry, null);
490 
491         final HttpContext context = new BasicHttpContext();
492 
493         final HttpGet httpget1 = new HttpGet("/");
494         final Future<HttpResponse> future1 = this.httpclient.execute(target, httpget1, context, null);
495         final HttpResponse response1 = future1.get();
496         Assert.assertNotNull(response1);
497         Assert.assertEquals(HttpStatus.SC_OK, response1.getStatusLine().getStatusCode());
498 
499         final HttpGet httpget2 = new HttpGet("/");
500         final Future<HttpResponse> future2 = this.httpclient.execute(target, httpget2, context, null);
501         final HttpResponse response2 = future2.get();
502         Assert.assertNotNull(response2);
503         Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
504 
505         Assert.assertEquals(1, authStrategy.getCount());
506     }
507 
508     @Test
509     public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
510         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
511         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
512 
513         this.httpclient = HttpAsyncClients.custom()
514             .setConnectionManager(this.connMgr)
515             .build();
516 
517         final HttpHost target = start(registry, null);
518 
519         final HttpGet httpget = new HttpGet("http://test:test@" +  target.toHostString() + "/");
520         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
521         final HttpResponse response = future.get();
522         Assert.assertNotNull(response);
523         final HttpEntity entity = response.getEntity();
524         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
525         Assert.assertNotNull(entity);
526     }
527 
528     @Test
529     public void testAuthenticationUserinfoInRequestFailure() throws Exception {
530         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
531         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
532 
533         this.httpclient = HttpAsyncClients.custom()
534             .setConnectionManager(this.connMgr)
535             .build();
536 
537         final HttpHost target = start(registry, null);
538 
539         final HttpGet httpget = new HttpGet("http://test:all-wrong@" +  target.toHostString() + "/");
540 
541         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
542         final HttpResponse response = future.get();
543         Assert.assertNotNull(response);
544         final HttpEntity entity = response.getEntity();
545         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
546         Assert.assertNotNull(entity);
547     }
548 
549     private static class RedirectHandler implements HttpRequestHandler {
550 
551         public RedirectHandler() {
552             super();
553         }
554 
555         public void handle(
556                 final HttpRequest request,
557                 final HttpResponse response,
558                 final HttpContext context) throws HttpException, IOException {
559             final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
560             final String localhost = conn.getLocalAddress().getHostName();
561             final int port = conn.getLocalPort();
562             response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
563             response.addHeader(new BasicHeader("Location",
564                     "http://test:test@" + localhost + ":" + port + "/"));
565         }
566 
567     }
568 
569     @Test
570     public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
571         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
572         registry.register("*", new BasicAsyncRequestHandler(new AuthHandler()));
573         registry.register("/thatway", new BasicAsyncRequestHandler(new RedirectHandler()));
574 
575         this.httpclient = HttpAsyncClients.custom()
576             .setConnectionManager(this.connMgr)
577             .build();
578 
579         final HttpHost target = start(registry, null);
580 
581         final HttpGet httpget = new HttpGet("http://test:test@" +  target.toHostString() + "/thatway");
582         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
583         final HttpResponse response = future.get();
584         Assert.assertNotNull(response);
585         final HttpEntity entity = response.getEntity();
586         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
587         Assert.assertNotNull(entity);
588     }
589 
590 }