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
28 package org.apache.hc.client5.http.impl.cache;
29
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.nio.charset.StandardCharsets;
37 import java.time.Instant;
38 import java.util.Collections;
39 import java.util.Map;
40
41 import org.apache.hc.client5.http.cache.HttpCacheEntry;
42 import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
43 import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
44 import org.apache.hc.client5.http.cache.Resource;
45 import org.apache.hc.client5.http.cache.ResourceIOException;
46 import org.apache.hc.core5.http.Header;
47 import org.apache.hc.core5.http.message.BasicHeader;
48 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
49 import static org.junit.jupiter.api.Assertions.assertEquals;
50 import static org.junit.jupiter.api.Assertions.assertNull;
51 import static org.junit.jupiter.api.Assertions.fail;
52
53 class HttpByteArrayCacheEntrySerializerTestUtils {
54 private final static String TEST_RESOURCE_DIR = "src/test/resources/";
55 static final String TEST_STORAGE_KEY = "xyzzy";
56
57
58
59
60 static class HttpCacheStorageEntryTestTemplate {
61 Resource resource;
62 Instant requestDate;
63 Instant responseDate;
64 int responseCode;
65 Header[] responseHeaders;
66 Map<String, String> variantMap;
67 String storageKey;
68
69
70
71
72
73
74 static HttpCacheStorageEntryTestTemplate makeDefault() {
75 return new HttpCacheStorageEntryTestTemplate(DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE);
76 }
77
78
79
80
81
82 HttpCacheStorageEntry toEntry() {
83 return new HttpCacheStorageEntry(storageKey,
84 new HttpCacheEntry(
85 requestDate,
86 responseDate,
87 responseCode,
88 responseHeaders,
89 resource,
90 variantMap));
91 }
92
93
94
95
96 private HttpCacheStorageEntryTestTemplate() {
97 }
98
99
100
101
102
103
104 private HttpCacheStorageEntryTestTemplate(final HttpCacheStorageEntryTestTemplate src) {
105 this.resource = src.resource;
106 this.requestDate = src.requestDate;
107 this.responseDate = src.responseDate;
108 this.responseCode = src.responseCode;
109 this.responseHeaders = src.responseHeaders;
110 this.variantMap = src.variantMap;
111 this.storageKey = src.storageKey;
112 }
113 }
114
115
116
117
118
119
120 private static final HttpCacheStorageEntryTestTemplate DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE = new HttpCacheStorageEntryTestTemplate();
121 static {
122 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.resource = new HeapResource("Hello World".getBytes(StandardCharsets.UTF_8));
123 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.requestDate = Instant.ofEpochMilli(165214800000L);
124 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseDate = Instant.ofEpochMilli(2611108800000L);
125 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseCode = 200;
126 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseHeaders = new Header[]{
127 new BasicHeader("Content-type", "text/html"),
128 new BasicHeader("Cache-control", "public, max-age=31536000"),
129 };
130 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.variantMap = Collections.emptyMap();
131 DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.storageKey = TEST_STORAGE_KEY;
132 }
133
134
135
136
137
138
139
140
141
142
143 static void testWithCache(final HttpCacheEntrySerializer<byte[]> serializer, final HttpCacheStorageEntry httpCacheStorageEntry) throws Exception {
144 final byte[] testBytes = serializer.serialize(httpCacheStorageEntry);
145 verifyHttpCacheEntryFromBytes(serializer, httpCacheStorageEntry, testBytes);
146 }
147
148
149
150
151
152
153
154
155
156 static void verifyHttpCacheEntryFromBytes(final HttpCacheEntrySerializer<byte[]> serializer, final HttpCacheStorageEntry httpCacheStorageEntry, final byte[] testBytes) throws Exception {
157 final HttpCacheStorageEntry testEntry = httpCacheStorageEntryFromBytes(serializer, testBytes);
158
159 assertCacheEntriesEqual(httpCacheStorageEntry, testEntry);
160 }
161
162
163
164
165
166
167
168
169
170
171 static void verifyHttpCacheEntryFromTestFile(final HttpCacheEntrySerializer<byte[]> serializer,
172 final HttpCacheStorageEntry httpCacheStorageEntry,
173 final String testFileName,
174 final boolean reserializeFiles) throws Exception {
175 if (reserializeFiles) {
176 final File toFile = makeTestFileObject(testFileName);
177 saveEntryToFile(serializer, httpCacheStorageEntry, toFile);
178 }
179
180 final byte[] bytes = readTestFileBytes(testFileName);
181
182 verifyHttpCacheEntryFromBytes(serializer, httpCacheStorageEntry, bytes);
183 }
184
185
186
187
188
189
190
191
192 static byte[] readTestFileBytes(final String testFileName) throws Exception {
193 final File testFile = makeTestFileObject(testFileName);
194 try(final FileInputStream testStream = new FileInputStream(testFile)) {
195 return readFullyStrict(testStream, testFile.length());
196 }
197 }
198
199
200
201
202
203
204
205
206 static HttpCacheStorageEntry httpCacheStorageEntryFromBytes(final HttpCacheEntrySerializer<byte[]> serializer, final byte[] testBytes) throws ResourceIOException {
207 return serializer.deserialize(testBytes);
208 }
209
210
211
212
213
214
215
216
217 static void assertCacheEntriesEqual(final HttpCacheStorageEntry expected, final HttpCacheStorageEntry actual) throws Exception {
218 assertEquals(expected.getKey(), actual.getKey());
219
220 final HttpCacheEntry expectedContent = expected.getContent();
221 final HttpCacheEntry actualContent = actual.getContent();
222
223 assertEquals(expectedContent.getRequestInstant(), actualContent.getRequestInstant());
224 assertEquals(expectedContent.getResponseInstant(), actualContent.getResponseInstant());
225
226 assertEquals(expectedContent.getStatus(), actualContent.getStatus());
227
228 assertArrayEquals(expectedContent.getVariantMap().keySet().toArray(), actualContent.getVariantMap().keySet().toArray());
229 for (final String key : expectedContent.getVariantMap().keySet()) {
230 assertEquals(expectedContent.getVariantMap().get(key), actualContent.getVariantMap().get(key), "Expected same variantMap values for key '" + key + "'");
231 }
232
233
234 for(final Header expectedHeader: expectedContent.getHeaders()) {
235 final Header actualHeader = actualContent.getFirstHeader(expectedHeader.getName());
236
237 if (actualHeader == null) {
238 if (expectedHeader.getName().equalsIgnoreCase("content-length")) {
239
240 } else {
241 fail("Expected header " + expectedHeader.getName() + " was not found");
242 }
243 } else {
244 assertEquals(expectedHeader.getName(), actualHeader.getName());
245 assertEquals(expectedHeader.getValue(), actualHeader.getValue());
246 }
247 }
248
249 if (expectedContent.getResource() == null) {
250 assertNull(actualContent.getResource(), "Expected null resource");
251 } else {
252 final byte[] expectedBytes = readFullyStrict(
253 expectedContent.getResource().getInputStream(),
254 (int) expectedContent.getResource().length()
255 );
256 final byte[] actualBytes = readFullyStrict(
257 actualContent.getResource().getInputStream(),
258 (int) actualContent.getResource().length()
259 );
260 assertArrayEquals(expectedBytes, actualBytes);
261 }
262 }
263
264
265
266
267
268
269
270 static File makeTestFileObject(final String testFileName) {
271 return new File(TEST_RESOURCE_DIR + testFileName);
272 }
273
274
275
276
277
278
279
280
281
282 static void saveEntryToFile(final HttpCacheEntrySerializer<byte[]> serializer, final HttpCacheStorageEntry httpCacheStorageEntry, final File outFile) throws Exception {
283 final byte[] bytes = serializer.serialize(httpCacheStorageEntry);
284
285 try (OutputStream out = new FileOutputStream(outFile)) {
286 out.write(bytes);
287 }
288 }
289
290
291
292
293
294
295
296
297
298
299 private static int readFully(final InputStream src, final byte[] dest) throws IOException {
300 final int destPos = 0;
301 final int length = dest.length;
302 int totalBytesRead = 0;
303 int lastBytesRead;
304
305 while (totalBytesRead < length && (lastBytesRead = src.read(dest, destPos + totalBytesRead, length - totalBytesRead)) != -1) {
306 totalBytesRead += lastBytesRead;
307 }
308 return totalBytesRead;
309 }
310
311
312
313
314
315
316
317
318
319
320
321 static byte[] readFullyStrict(final InputStream src, final long length) throws IOException {
322 if (length > Integer.MAX_VALUE) {
323 throw new IllegalArgumentException(String.format("Length %d is too large to fit in an array", length));
324 }
325 final int intLength = (int) length;
326 final byte[] dest = new byte[intLength];
327 final int bytesRead = readFully(src, dest);
328
329 if (bytesRead == intLength) {
330 return dest;
331 } else {
332 throw new IOException(String.format("Expected to read %d bytes but only got %d", intLength, bytesRead));
333 }
334 }
335 }