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