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