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