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.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 }