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.cache;
28
29 import java.net.URI;
30 import java.time.Instant;
31 import java.util.Collection;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.Locale;
35 import java.util.Set;
36
37 import org.apache.hc.client5.http.impl.cache.CacheKeyGenerator;
38 import org.apache.hc.client5.http.utils.DateUtils;
39 import org.apache.hc.core5.annotation.Contract;
40 import org.apache.hc.core5.annotation.ThreadingBehavior;
41 import org.apache.hc.core5.http.Header;
42 import org.apache.hc.core5.http.HttpHeaders;
43 import org.apache.hc.core5.http.HttpHost;
44 import org.apache.hc.core5.http.HttpMessage;
45 import org.apache.hc.core5.http.HttpRequest;
46 import org.apache.hc.core5.http.HttpResponse;
47 import org.apache.hc.core5.http.HttpStatus;
48 import org.apache.hc.core5.http.message.BasicHeader;
49 import org.apache.hc.core5.http.message.HeaderGroup;
50 import org.apache.hc.core5.http.message.MessageSupport;
51 import org.apache.hc.core5.util.Args;
52
53
54
55
56
57
58 @Contract(threading = ThreadingBehavior.IMMUTABLE)
59 public class HttpCacheEntryFactory {
60
61
62
63
64 public static final HttpCacheEntryFactory INSTANCE = new HttpCacheEntryFactory();
65
66 private static HeaderGroup headers(final Iterator<Header> it) {
67 final HeaderGroup headerGroup = new HeaderGroup();
68 while (it.hasNext()) {
69 headerGroup.addHeader(it.next());
70 }
71 return headerGroup;
72 }
73
74 HeaderGroup mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
75 final HeaderGroup headerGroup = new HeaderGroup();
76 for (final Iterator<Header> it = entry.headerIterator(); it.hasNext(); ) {
77 final Header entryHeader = it.next();
78 final String headerName = entryHeader.getName();
79 if (!response.containsHeader(headerName)) {
80 headerGroup.addHeader(entryHeader);
81 }
82 }
83 final Set<String> responseHopByHop = MessageSupport.hopByHopConnectionSpecific(response);
84 for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
85 final Header responseHeader = it.next();
86 final String headerName = responseHeader.getName();
87 if (!responseHopByHop.contains(headerName.toLowerCase(Locale.ROOT))) {
88 headerGroup.addHeader(responseHeader);
89 }
90 }
91 return headerGroup;
92 }
93
94
95
96
97 static HeaderGroup filterHopByHopHeaders(final HttpMessage message) {
98 final Set<String> hopByHop = MessageSupport.hopByHopConnectionSpecific(message);
99 final HeaderGroup headerGroup = new HeaderGroup();
100 for (final Iterator<Header> it = message.headerIterator(); it.hasNext(); ) {
101 final Header header = it.next();
102 if (!hopByHop.contains(header.getName())) {
103 headerGroup.addHeader(header);
104 }
105 }
106 return headerGroup;
107 }
108
109 static void ensureDate(final HeaderGroup headers, final Instant instant) {
110 if (!headers.containsHeader(HttpHeaders.DATE)) {
111 headers.addHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(instant)));
112 }
113 }
114
115
116
117
118
119
120
121
122
123
124 public HttpCacheEntry createRoot(final HttpCacheEntry latestVariant,
125 final Collection<String> variants) {
126 Args.notNull(latestVariant, "Request");
127 Args.notNull(variants, "Variants");
128 return new HttpCacheEntry(
129 latestVariant.getRequestInstant(),
130 latestVariant.getResponseInstant(),
131 latestVariant.getRequestMethod(),
132 latestVariant.getRequestURI(),
133 headers(latestVariant.requestHeaderIterator()),
134 latestVariant.getStatus(),
135 headers(latestVariant.headerIterator()),
136 null,
137 variants);
138 }
139
140
141
142
143
144
145
146
147
148
149
150 public HttpCacheEntry create(final Instant requestInstant,
151 final Instant responseInstant,
152 final HttpHost host,
153 final HttpRequest request,
154 final HttpResponse response,
155 final Resource resource) {
156 Args.notNull(requestInstant, "Request instant");
157 Args.notNull(responseInstant, "Response instant");
158 Args.notNull(host, "Host");
159 Args.notNull(request, "Request");
160 Args.notNull(response, "Origin response");
161 final String s = CacheKeyGenerator.getRequestUri(host, request);
162 final URI uri = CacheKeyGenerator.normalize(s);
163 final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
164
165 requestHeaders.removeHeaders(HttpHeaders.AUTHORIZATION);
166 final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
167 ensureDate(responseHeaders, responseInstant);
168 return new HttpCacheEntry(
169 requestInstant,
170 responseInstant,
171 request.getMethod(),
172 uri.toASCIIString(),
173 requestHeaders,
174 response.getCode(),
175 responseHeaders,
176 resource,
177 null);
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191 public HttpCacheEntry createUpdated(
192 final Instant requestInstant,
193 final Instant responseInstant,
194 final HttpHost host,
195 final HttpRequest request,
196 final HttpResponse response,
197 final HttpCacheEntry entry) {
198 Args.notNull(requestInstant, "Request instant");
199 Args.notNull(responseInstant, "Response instant");
200 Args.notNull(response, "Origin response");
201 Args.check(response.getCode() == HttpStatus.SC_NOT_MODIFIED,
202 "Response must have 304 status code");
203 Args.notNull(entry, "Cache entry");
204 if (HttpCacheEntry.isNewer(entry, response)) {
205 return entry;
206 }
207 final String s = CacheKeyGenerator.getRequestUri(host, request);
208 final URI uri = CacheKeyGenerator.normalize(s);
209 final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
210
211 requestHeaders.removeHeaders(HttpHeaders.AUTHORIZATION);
212 final HeaderGroup mergedHeaders = mergeHeaders(entry, response);
213 return new HttpCacheEntry(
214 requestInstant,
215 responseInstant,
216 request.getMethod(),
217 uri.toASCIIString(),
218 requestHeaders,
219 entry.getStatus(),
220 mergedHeaders,
221 entry.getResource(),
222 null);
223 }
224
225
226
227
228
229 public HttpCacheEntry copy(final HttpCacheEntry entry) {
230 if (entry == null) {
231 return null;
232 }
233 return new HttpCacheEntry(
234 entry.getRequestInstant(),
235 entry.getResponseInstant(),
236 entry.getRequestMethod(),
237 entry.getRequestURI(),
238 headers(entry.requestHeaderIterator()),
239 entry.getStatus(),
240 headers(entry.headerIterator()),
241 entry.getResource(),
242 entry.hasVariants() ? new HashSet<>(entry.getVariants()) : null);
243 }
244
245 }