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