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.hc.client5.testing.async;
28  
29  import static org.hamcrest.MatcherAssert.assertThat;
30  
31  import java.security.SecureRandom;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.Queue;
35  import java.util.concurrent.ConcurrentLinkedQueue;
36  import java.util.concurrent.ExecutionException;
37  import java.util.concurrent.Future;
38  import java.util.concurrent.atomic.AtomicLong;
39  import java.util.function.Consumer;
40  import java.util.stream.Collectors;
41  
42  import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
43  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
44  import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
45  import org.apache.hc.client5.http.auth.AuthCache;
46  import org.apache.hc.client5.http.auth.AuthSchemeFactory;
47  import org.apache.hc.client5.http.auth.AuthScope;
48  import org.apache.hc.client5.http.auth.BearerToken;
49  import org.apache.hc.client5.http.auth.CredentialsProvider;
50  import org.apache.hc.client5.http.auth.StandardAuthScheme;
51  import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
52  import org.apache.hc.client5.http.config.RequestConfig;
53  import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
54  import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
55  import org.apache.hc.client5.http.impl.auth.BasicScheme;
56  import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
57  import org.apache.hc.client5.http.protocol.HttpClientContext;
58  import org.apache.hc.client5.testing.BasicTestAuthenticator;
59  import org.apache.hc.client5.testing.auth.Authenticator;
60  import org.apache.hc.client5.testing.auth.BearerAuthenticationHandler;
61  import org.apache.hc.client5.testing.extension.async.ClientProtocolLevel;
62  import org.apache.hc.client5.testing.extension.async.ServerProtocolLevel;
63  import org.apache.hc.client5.testing.extension.async.TestAsyncClient;
64  import org.apache.hc.client5.testing.extension.async.TestAsyncServerBootstrap;
65  import org.apache.hc.core5.http.ContentType;
66  import org.apache.hc.core5.http.HttpHeaders;
67  import org.apache.hc.core5.http.HttpHost;
68  import org.apache.hc.core5.http.HttpResponse;
69  import org.apache.hc.core5.http.HttpStatus;
70  import org.apache.hc.core5.http.ProtocolException;
71  import org.apache.hc.core5.http.URIScheme;
72  import org.apache.hc.core5.http.config.Registry;
73  import org.apache.hc.core5.http.config.RegistryBuilder;
74  import org.apache.hc.core5.http.support.BasicResponseBuilder;
75  import org.apache.hc.core5.net.URIAuthority;
76  import org.hamcrest.CoreMatchers;
77  import org.junit.jupiter.api.Assertions;
78  import org.junit.jupiter.api.Test;
79  import org.mockito.Mockito;
80  
81  abstract  class AbstractHttpAsyncClientAuthenticationTest extends AbstractIntegrationTestBase {
82  
83      public AbstractHttpAsyncClientAuthenticationTest(final URIScheme scheme, final ClientProtocolLevel clientProtocolLevel, final ServerProtocolLevel serverProtocolLevel) {
84          super(scheme, clientProtocolLevel, serverProtocolLevel);
85      }
86  
87      public void configureServerWithBasicAuth(final Authenticator authenticator,
88                                               final Consumer<TestAsyncServerBootstrap> serverCustomizer) {
89          configureServer(bootstrap -> {
90              bootstrap.setExchangeHandlerDecorator(requestHandler ->
91                      new AuthenticatingAsyncDecorator(requestHandler, authenticator));
92              serverCustomizer.accept(bootstrap);
93          });
94      }
95  
96      public void configureServerWithBasicAuth(final Consumer<TestAsyncServerBootstrap> serverCustomizer) {
97          configureServerWithBasicAuth(
98                  new BasicTestAuthenticator("test:test", "test realm"),
99                  serverCustomizer);
100     }
101 
102     @Test
103     void testBasicAuthenticationNoCreds() throws Exception {
104         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
105         final HttpHost target = startServer();
106 
107         final TestAsyncClient client = startClient();
108 
109         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
110         final HttpClientContext context = HttpClientContext.create();
111         context.setCredentialsProvider(credsProvider);
112 
113         final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
114                         .setHttpHost(target)
115                         .setPath("/")
116                         .build(), context, null);
117         final HttpResponse response = future.get();
118 
119         Assertions.assertNotNull(response);
120         Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
121         Mockito.verify(credsProvider).getCredentials(
122                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
123     }
124 
125     @Test
126     void testBasicAuthenticationFailure() throws Exception {
127         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
128         final HttpHost target = startServer();
129 
130         final TestAsyncClient client = startClient();
131 
132         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
133         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
134                 .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
135         final HttpClientContext context = HttpClientContext.create();
136         context.setCredentialsProvider(credsProvider);
137 
138         final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
139                         .setHttpHost(target)
140                         .setPath("/")
141                         .build(), context, null);
142         final HttpResponse response = future.get();
143 
144         Assertions.assertNotNull(response);
145         Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
146         Mockito.verify(credsProvider).getCredentials(
147                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
148     }
149 
150     @Test
151     void testBasicAuthenticationSuccess() throws Exception {
152         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
153         final HttpHost target = startServer();
154 
155         final TestAsyncClient client = startClient();
156 
157         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
158         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
159                 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
160         final HttpClientContext context = HttpClientContext.create();
161         context.setCredentialsProvider(credsProvider);
162 
163         final Future<SimpleHttpResponse> future = client.execute(
164                 SimpleRequestBuilder.get()
165                         .setHttpHost(target)
166                         .setPath("/")
167                         .build(), context, null);
168         final HttpResponse response = future.get();
169 
170         Assertions.assertNotNull(response);
171         Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
172         Mockito.verify(credsProvider).getCredentials(
173                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
174     }
175 
176     @Test
177     void testBasicAuthenticationWithEntitySuccess() throws Exception {
178         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
179         final HttpHost target = startServer();
180 
181         final TestAsyncClient client = startClient();
182 
183         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
184         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
185                 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
186         final HttpClientContext context = HttpClientContext.create();
187         context.setCredentialsProvider(credsProvider);
188         final Future<SimpleHttpResponse> future = client.execute(
189                 SimpleRequestBuilder.put()
190                         .setHttpHost(target)
191                         .setPath("/")
192                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
193                         .build(), context, null);
194         final HttpResponse response = future.get();
195 
196         Assertions.assertNotNull(response);
197         Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
198         Mockito.verify(credsProvider).getCredentials(
199                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
200     }
201 
202     @Test
203     void testBasicAuthenticationExpectationFailure() throws Exception {
204         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
205         final HttpHost target = startServer();
206 
207         final TestAsyncClient client = startClient();
208 
209         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
210         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
211                 .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
212         final HttpClientContext context = HttpClientContext.create();
213         context.setCredentialsProvider(credsProvider);
214         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
215         final Future<SimpleHttpResponse> future = client.execute(
216                 SimpleRequestBuilder.put()
217                         .setHttpHost(target)
218                         .setPath("/")
219                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
220                         .build(), context, null);
221         final HttpResponse response = future.get();
222 
223         Assertions.assertNotNull(response);
224         Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
225     }
226 
227     @Test
228     void testBasicAuthenticationExpectationSuccess() throws Exception {
229         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
230         final HttpHost target = startServer();
231 
232         final TestAsyncClient client = startClient();
233 
234         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
235         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
236                 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
237         final HttpClientContext context = HttpClientContext.create();
238         context.setCredentialsProvider(credsProvider);
239         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
240         final Future<SimpleHttpResponse> future = client.execute(
241                 SimpleRequestBuilder.put()
242                         .setHttpHost(target)
243                         .setPath("/")
244                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
245                         .build(), context, null);
246         final HttpResponse response = future.get();
247 
248         Assertions.assertNotNull(response);
249         Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
250         Mockito.verify(credsProvider).getCredentials(
251                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
252     }
253 
254     @Test
255     void testBasicAuthenticationCredentialsCaching() throws Exception {
256         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
257         final HttpHost target = startServer();
258 
259         final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
260         configureClient(builder ->  builder.setTargetAuthenticationStrategy(authStrategy));
261         final TestAsyncClient client = startClient();
262 
263         final HttpClientContext context = HttpClientContext.create();
264         context.setCredentialsProvider(CredentialsProviderBuilder.create()
265                 .add(target, "test", "test".toCharArray())
266                 .build());
267 
268         for (int i = 0; i < 5; i++) {
269             final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
270                     .setHttpHost(target)
271                     .setPath("/")
272                     .build(), context, null);
273             final HttpResponse response = future.get();
274             Assertions.assertNotNull(response);
275             Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
276         }
277 
278         Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
279     }
280 
281     @Test
282     void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception {
283         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
284         final HttpHost target = startServer();
285 
286         final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
287         final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
288 
289         configureClient(builder -> builder
290                 .setTargetAuthenticationStrategy(authStrategy)
291                 .addResponseInterceptorFirst((response, entity, context)
292                         -> responseQueue.add(BasicResponseBuilder.copy(response).build())));
293         final TestAsyncClient client = startClient();
294 
295         final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
296                 .add(target, "test", "test".toCharArray())
297                 .build();
298 
299         final AuthCache authCache = new BasicAuthCache();
300 
301         for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) {
302             final HttpClientContext context = HttpClientContext.create();
303             context.setAuthCache(authCache);
304             context.setCredentialsProvider(credentialsProvider);
305             final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
306                     .setHttpHost(target)
307                     .setPath(requestPath)
308                     .build(), context, null);
309             final HttpResponse response = future.get();
310             Assertions.assertNotNull(response);
311             Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
312         }
313 
314         // There should be only single auth strategy call for all successful message exchanges
315         Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
316 
317         assertThat(
318                 responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
319                 CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200)));
320 
321         responseQueue.clear();
322         authCache.clear();
323         Mockito.reset(authStrategy);
324 
325         for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/"}) {
326             final HttpClientContext context = HttpClientContext.create();
327             context.setCredentialsProvider(credentialsProvider);
328             context.setAuthCache(authCache);
329             final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
330                     .setHttpHost(target)
331                     .setPath(requestPath)
332                     .build(), context, null);
333             final HttpResponse response = future.get();
334             Assertions.assertNotNull(response);
335             Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
336         }
337 
338         // There should be an auth strategy call for all successful message exchanges
339         Mockito.verify(authStrategy, Mockito.times(3)).select(Mockito.any(), Mockito.any(), Mockito.any());
340 
341         assertThat(
342                 responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
343                 CoreMatchers.equalTo(Arrays.asList(401, 200, 401, 200, 401, 200)));
344     }
345 
346     @Test
347     void testAuthenticationUserinfoInRequestFailure() throws Exception {
348         configureServerWithBasicAuth(bootstrap -> bootstrap.register("*", AsyncEchoHandler::new));
349         final HttpHost target = startServer();
350 
351         final TestAsyncClient client = startClient();
352 
353         final HttpClientContext context = HttpClientContext.create();
354         final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
355                         .setScheme(target.getSchemeName())
356                         .setAuthority(new URIAuthority("test:test", target.getHostName(), target.getPort()))
357                         .setPath("/")
358                         .build(), context, null);
359         final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future.get());
360         assertThat(exception.getCause(), CoreMatchers.instanceOf(ProtocolException.class));
361     }
362 
363     @Test
364     void testReauthentication() throws Exception {
365         final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") {
366 
367             private final AtomicLong count = new AtomicLong(0);
368 
369             @Override
370             public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
371                 final boolean authenticated = super.authenticate(authority, requestUri, credentials);
372                 if (authenticated) {
373                     return this.count.incrementAndGet() % 4 != 0;
374                 }
375                 return false;
376             }
377         };
378 
379         configureServerWithBasicAuth(bootstrap -> bootstrap
380                 .register("*", AsyncEchoHandler::new)
381                 .setExchangeHandlerDecorator(exchangeHandler -> new AuthenticatingAsyncDecorator(exchangeHandler, authenticator) {
382 
383                     @Override
384                     protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
385                         unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE);
386                         unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
387                     }
388 
389                 }));
390         final HttpHost target = startServer();
391 
392         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
393         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
394                 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
395 
396         final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
397                 .register("MyBasic", context -> new BasicScheme() {
398 
399                     private static final long serialVersionUID = 1L;
400 
401                     @Override
402                     public String getName() {
403                         return "MyBasic";
404                     }
405 
406                 })
407                 .build();
408 
409         configureClient(builder -> builder.setDefaultAuthSchemeRegistry(authSchemeRegistry));
410         final TestAsyncClient client = startClient();
411 
412         final RequestConfig config = RequestConfig.custom()
413                 .setTargetPreferredAuthSchemes(Collections.singletonList("MyBasic"))
414                 .build();
415         final HttpClientContext context = HttpClientContext.create();
416         context.setCredentialsProvider(credsProvider);
417 
418         for (int i = 0; i < 10; i++) {
419             final SimpleHttpRequest request = SimpleRequestBuilder.get()
420                         .setHttpHost(target)
421                         .setPath("/")
422                         .build();
423             request.setConfig(config);
424             final Future<SimpleHttpResponse> future = client.execute(request, context, null);
425             final SimpleHttpResponse response = future.get();
426             Assertions.assertNotNull(response);
427             Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
428         }
429     }
430 
431     @Test
432     void testAuthenticationFallback() throws Exception {
433         configureServerWithBasicAuth(bootstrap -> bootstrap
434                 .register("*", AsyncEchoHandler::new)
435                 .setExchangeHandlerDecorator(exchangeHandler -> new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) {
436 
437                     @Override
438                     protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
439                         unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid");
440                     }
441 
442                 }));
443         final HttpHost target = startServer();
444 
445         final TestAsyncClient client = startClient();
446 
447         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
448         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
449                 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
450         final HttpClientContext context = HttpClientContext.create();
451         context.setCredentialsProvider(credsProvider);
452 
453         final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.get()
454                         .setHttpHost(target)
455                         .setPath("/")
456                         .build(), context, null);
457         final SimpleHttpResponse response = future.get();
458         Assertions.assertNotNull(response);
459         Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
460         Mockito.verify(credsProvider).getCredentials(
461                 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
462     }
463 
464     private final static String CHARS = "0123456789abcdef";
465 
466     @Test
467     void testBearerTokenAuthentication() throws Exception {
468         final SecureRandom secureRandom = SecureRandom.getInstanceStrong();
469         secureRandom.setSeed(System.currentTimeMillis());
470         final StringBuilder buf = new StringBuilder();
471         for (int i = 0; i < 16; i++) {
472             buf.append(CHARS.charAt(secureRandom.nextInt(CHARS.length() - 1)));
473         }
474         final String token = buf.toString();
475 
476         configureServerWithBasicAuth(bootstrap -> bootstrap
477                 .register("*", AsyncEchoHandler::new)
478                 .setExchangeHandlerDecorator(requestHandler ->
479                         new AuthenticatingAsyncDecorator(
480                                 requestHandler,
481                                 new BearerAuthenticationHandler(),
482                                 new BasicTestAuthenticator(token, "test realm"))));
483         final HttpHost target = startServer();
484 
485         final TestAsyncClient client = startClient();
486 
487         final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
488         final HttpClientContext context1 = HttpClientContext.create();
489         context1.setCredentialsProvider(credsProvider);
490 
491         final Future<SimpleHttpResponse> future1 = client.execute(SimpleRequestBuilder.get()
492                 .setHttpHost(target)
493                 .setPath("/")
494                 .build(), context1, null);
495         final SimpleHttpResponse response1 = future1.get();
496         Assertions.assertNotNull(response1);
497         Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode());
498         Mockito.verify(credsProvider).getCredentials(
499                 Mockito.eq(new AuthScope(target, "test realm", "bearer")), Mockito.any());
500 
501         final HttpClientContext context2 = HttpClientContext.create();
502         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
503                 .thenReturn(new BearerToken(token));
504         context2.setCredentialsProvider(credsProvider);
505 
506         final Future<SimpleHttpResponse> future2 = client.execute(SimpleRequestBuilder.get()
507                 .setHttpHost(target)
508                 .setPath("/")
509                 .build(), context2, null);
510         final SimpleHttpResponse response2 = future2.get();
511         Assertions.assertNotNull(response2);
512         Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode());
513 
514         final HttpClientContext context3 = HttpClientContext.create();
515         Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
516                 .thenReturn(new BearerToken(token + "-expired"));
517         context3.setCredentialsProvider(credsProvider);
518 
519         final Future<SimpleHttpResponse> future3 = client.execute(SimpleRequestBuilder.get()
520                 .setHttpHost(target)
521                 .setPath("/")
522                 .build(), context3, null);
523         final SimpleHttpResponse response3 = future3.get();
524         Assertions.assertNotNull(response3);
525         Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response3.getCode());
526     }
527 
528 }