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.hc.client5.http.impl.auth;
28
29 import java.io.Serializable;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.concurrent.ConcurrentHashMap;
34
35 import org.apache.hc.client5.http.SchemePortResolver;
36 import org.apache.hc.client5.http.auth.AuthCache;
37 import org.apache.hc.client5.http.auth.AuthScheme;
38 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
39 import org.apache.hc.client5.http.impl.StateHolder;
40 import org.apache.hc.core5.annotation.Contract;
41 import org.apache.hc.core5.annotation.ThreadingBehavior;
42 import org.apache.hc.core5.http.HttpHost;
43 import org.apache.hc.core5.net.NamedEndpoint;
44 import org.apache.hc.core5.util.Args;
45 import org.apache.hc.core5.util.LangUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49
50
51
52
53
54
55
56
57
58
59 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
60 public class BasicAuthCache implements AuthCache {
61
62 private static final Logger LOG = LoggerFactory.getLogger(BasicAuthCache.class);
63
64 static class Key {
65
66 final String scheme;
67 final String host;
68 final int port;
69 final String pathPrefix;
70
71 Key(final String scheme, final String host, final int port, final String pathPrefix) {
72 Args.notBlank(scheme, "Scheme");
73 Args.notBlank(host, "Scheme");
74 this.scheme = scheme.toLowerCase(Locale.ROOT);
75 this.host = host.toLowerCase(Locale.ROOT);
76 this.port = port;
77 this.pathPrefix = pathPrefix;
78 }
79
80 @Override
81 public boolean equals(final Object obj) {
82 if (this == obj) {
83 return true;
84 }
85 if (obj instanceof Key) {
86 final Key that = (Key) obj;
87 return this.scheme.equals(that.scheme) &&
88 this.host.equals(that.host) &&
89 this.port == that.port &&
90 Objects.equals(this.pathPrefix, that.pathPrefix);
91 }
92 return false;
93 }
94
95 @Override
96 public int hashCode() {
97 int hash = LangUtils.HASH_SEED;
98 hash = LangUtils.hashCode(hash, this.scheme);
99 hash = LangUtils.hashCode(hash, this.host);
100 hash = LangUtils.hashCode(hash, this.port);
101 hash = LangUtils.hashCode(hash, this.pathPrefix);
102 return hash;
103 }
104
105 @Override
106 public String toString() {
107 final StringBuilder buf = new StringBuilder();
108 buf.append(scheme).append("://").append(host);
109 if (port >= 0) {
110 buf.append(":").append(port);
111 }
112 if (pathPrefix != null) {
113 if (!pathPrefix.startsWith("/")) {
114 buf.append("/");
115 }
116 buf.append(pathPrefix);
117 }
118 return buf.toString();
119 }
120 }
121
122 static class AuthData {
123
124 final Class<? extends AuthScheme> clazz;
125 final Object state;
126
127 public AuthData(final Class<? extends AuthScheme> clazz, final Object state) {
128 this.clazz = clazz;
129 this.state = state;
130 }
131
132 }
133
134 private final Map<Key, AuthData> map;
135 private final SchemePortResolver schemePortResolver;
136
137
138
139
140
141
142 public BasicAuthCache(final SchemePortResolver schemePortResolver) {
143 super();
144 this.map = new ConcurrentHashMap<>();
145 this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE;
146 }
147
148 public BasicAuthCache() {
149 this(null);
150 }
151
152 private Key key(final String scheme, final NamedEndpoint authority, final String pathPrefix) {
153 return new Key(scheme, authority.getHostName(), schemePortResolver.resolve(scheme, authority), pathPrefix);
154 }
155
156 private AuthData data(final AuthScheme authScheme) {
157 return new AuthData(authScheme.getClass(), ((StateHolder) authScheme).store());
158 }
159
160 @Override
161 public void put(final HttpHost host, final AuthScheme authScheme) {
162 put(host, null, authScheme);
163 }
164
165 @Override
166 public AuthScheme get(final HttpHost host) {
167 return get(host, null);
168 }
169
170 @Override
171 public void remove(final HttpHost host) {
172 remove(host, null);
173 }
174
175 @Override
176 public void put(final HttpHost host, final String pathPrefix, final AuthScheme authScheme) {
177 Args.notNull(host, "HTTP host");
178 if (authScheme == null) {
179 return;
180 }
181 if (authScheme instanceof StateHolder) {
182 this.map.put(key(host.getSchemeName(), host, pathPrefix), data(authScheme));
183 } else {
184 if (LOG.isDebugEnabled()) {
185 LOG.debug("Auth scheme {} cannot be cached", authScheme.getClass());
186 }
187 }
188 }
189
190 @Override
191 @SuppressWarnings("unchecked")
192 public AuthScheme get(final HttpHost host, final String pathPrefix) {
193 Args.notNull(host, "HTTP host");
194 final AuthData authData = this.map.get(key(host.getSchemeName(), host, pathPrefix));
195 if (authData != null) {
196 try {
197 final AuthScheme authScheme = authData.clazz.newInstance();
198 ((StateHolder<Object>) authScheme).restore(authData.state);
199 return authScheme;
200 } catch (final IllegalAccessException | InstantiationException ex) {
201 if (LOG.isWarnEnabled()) {
202 LOG.warn("Unexpected error while reading auth scheme state", ex);
203 }
204 }
205 }
206 return null;
207 }
208
209 @Override
210 public void remove(final HttpHost host, final String pathPrefix) {
211 Args.notNull(host, "HTTP host");
212 this.map.remove(key(host.getSchemeName(), host, pathPrefix));
213 }
214
215 @Override
216 public void clear() {
217 this.map.clear();
218 }
219
220 @Override
221 public String toString() {
222 return this.map.toString();
223 }
224
225 }