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  
28  package org.apache.hc.client5.http.impl.auth;
29  
30  import org.apache.hc.client5.http.SchemePortResolver;
31  import org.apache.hc.client5.http.auth.AuthCache;
32  import org.apache.hc.client5.http.auth.AuthExchange;
33  import org.apache.hc.client5.http.auth.AuthScheme;
34  import org.apache.hc.client5.http.auth.AuthStateCacheable;
35  import org.apache.hc.client5.http.protocol.HttpClientContext;
36  import org.apache.hc.core5.annotation.Contract;
37  import org.apache.hc.core5.annotation.Internal;
38  import org.apache.hc.core5.annotation.ThreadingBehavior;
39  import org.apache.hc.core5.http.HttpHost;
40  import org.apache.hc.core5.http.protocol.HttpContext;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  /**
45   * Utility class that implements commons aspects of the client side authentication cache keeping.
46   *
47   * @since 5.2
48   */
49  @Internal
50  @Contract(threading = ThreadingBehavior.STATELESS)
51  public final class AuthCacheKeeper {
52  
53      private static final Logger LOG = LoggerFactory.getLogger(AuthCacheKeeper.class);
54  
55      private final SchemePortResolver schemePortResolver;
56  
57      public AuthCacheKeeper(final SchemePortResolver schemePortResolver) {
58          this.schemePortResolver = schemePortResolver;
59      }
60  
61      public void updateOnChallenge(final HttpHost host,
62                                    final String pathPrefix,
63                                    final AuthExchange authExchange,
64                                    final HttpContext context) {
65          clearCache(host, pathPrefix, HttpClientContext.adapt(context));
66      }
67  
68      public void updateOnNoChallenge(final HttpHost host,
69                                      final String pathPrefix,
70                                      final AuthExchange authExchange,
71                                      final HttpContext context) {
72          if (authExchange.getState() == AuthExchange.State.SUCCESS) {
73              updateCache(host, pathPrefix, authExchange.getAuthScheme(), HttpClientContext.adapt(context));
74          }
75      }
76  
77      public void updateOnResponse(final HttpHost host,
78                                   final String pathPrefix,
79                                   final AuthExchange authExchange,
80                                   final HttpContext context) {
81          if (authExchange.getState() == AuthExchange.State.FAILURE) {
82              clearCache(host, pathPrefix, HttpClientContext.adapt(context));
83          }
84      }
85  
86      public void loadPreemptively(final HttpHost host,
87                                   final String pathPrefix,
88                                   final AuthExchange authExchange,
89                                   final HttpContext context) {
90          if (authExchange.getState() == AuthExchange.State.UNCHALLENGED) {
91              AuthScheme authScheme = loadFromCache(host, pathPrefix, HttpClientContext.adapt(context));
92              if (authScheme == null && pathPrefix != null) {
93                  authScheme = loadFromCache(host, null, HttpClientContext.adapt(context));
94              }
95              if (authScheme != null) {
96                  authExchange.select(authScheme);
97              }
98          }
99      }
100 
101     private AuthScheme loadFromCache(final HttpHost host,
102                                      final String pathPrefix,
103                                      final HttpClientContext clientContext) {
104         final AuthCache authCache = clientContext.getAuthCache();
105         if (authCache != null) {
106             final AuthScheme authScheme = authCache.get(host, pathPrefix);
107             if (authScheme != null) {
108                 if (LOG.isDebugEnabled()) {
109                     final String exchangeId = clientContext.getExchangeId();
110                     LOG.debug("{} Re-using cached '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host,
111                             pathPrefix != null ? pathPrefix : "");
112                 }
113                 return authScheme;
114             }
115         }
116         return null;
117     }
118 
119     private void updateCache(final HttpHost host,
120                              final String pathPrefix,
121                              final AuthScheme authScheme,
122                              final HttpClientContext clientContext) {
123         final boolean cacheable = authScheme.getClass().getAnnotation(AuthStateCacheable.class) != null;
124         if (cacheable) {
125             AuthCache authCache = clientContext.getAuthCache();
126             if (authCache == null) {
127                 authCache = new BasicAuthCache(schemePortResolver);
128                 clientContext.setAuthCache(authCache);
129             }
130             if (LOG.isDebugEnabled()) {
131                 final String exchangeId = clientContext.getExchangeId();
132                 LOG.debug("{} Caching '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host,
133                         pathPrefix != null ? pathPrefix : "");
134             }
135             authCache.put(host, pathPrefix, authScheme);
136         }
137     }
138 
139     private void clearCache(final HttpHost host,
140                             final String pathPrefix,
141                             final HttpClientContext clientContext) {
142         final AuthCache authCache = clientContext.getAuthCache();
143         if (authCache != null) {
144             if (LOG.isDebugEnabled()) {
145                 final String exchangeId = clientContext.getExchangeId();
146                 LOG.debug("{} Clearing cached auth scheme for {}{}", exchangeId, host,
147                         pathPrefix != null ? pathPrefix : "");
148             }
149             authCache.remove(host, pathPrefix);
150         }
151     }
152 
153 }