1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit;
16
17 import static org.apache.http.client.utils.DateUtils.formatDate;
18 import static org.htmlunit.HttpHeader.CACHE_CONTROL;
19 import static org.htmlunit.HttpHeader.ETAG;
20 import static org.htmlunit.HttpHeader.EXPIRES;
21 import static org.htmlunit.HttpHeader.IF_MODIFIED_SINCE;
22 import static org.htmlunit.HttpHeader.IF_NONE_MATCH;
23 import static org.htmlunit.HttpHeader.LAST_MODIFIED;
24
25 import java.net.URL;
26 import java.text.SimpleDateFormat;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.apache.commons.lang3.time.DateUtils;
35 import org.htmlunit.html.HtmlPage;
36 import org.htmlunit.http.HttpStatus;
37 import org.htmlunit.junit.BrowserRunner;
38 import org.htmlunit.junit.annotation.Alerts;
39 import org.htmlunit.util.MimeType;
40 import org.htmlunit.util.NameValuePair;
41 import org.htmlunit.util.mocks.WebResponseMock;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44
45
46
47
48
49
50
51
52
53
54
55
56 @RunWith(BrowserRunner.class)
57 public class CacheTest extends SimpleWebTestCase {
58
59 private static final long ONE_MINUTE = 60_000L;
60 private static final long ONE_HOUR = ONE_MINUTE * 60;
61
62 private final long now_ = new Date().getTime();
63 private final String tomorrow_ = formatDate(DateUtils.addDays(new Date(), 1));
64
65
66
67
68 @Test
69 public void isCacheableContent() {
70 final Cache cache = new Cache();
71 final Map<String, String> headers = new HashMap<>();
72 final WebResponse response = new WebResponseMock(null, headers);
73
74 assertFalse(cache.isCacheableContent(response));
75
76 headers.put(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT");
77 assertTrue(cache.isCacheableContent(response));
78
79 headers.put(LAST_MODIFIED, formatDate(DateUtils.addMinutes(new Date(), -5)));
80 assertTrue(cache.isCacheableContent(response));
81
82 headers.put(LAST_MODIFIED, formatDate(new Date()));
83 assertFalse(cache.isCacheableContent(response));
84
85 headers.put(LAST_MODIFIED, formatDate(DateUtils.addMinutes(new Date(), 10)));
86 assertFalse(cache.isCacheableContent(response));
87
88 headers.put(EXPIRES, formatDate(DateUtils.addMinutes(new Date(), 5)));
89 assertFalse(cache.isCacheableContent(response));
90
91 headers.put(EXPIRES, formatDate(DateUtils.addHours(new Date(), 1)));
92 assertTrue(cache.isCacheableContent(response));
93
94 headers.remove(LAST_MODIFIED);
95 assertTrue(cache.isCacheableContent(response));
96
97 headers.put(EXPIRES, "0");
98 assertFalse(cache.isCacheableContent(response));
99
100 headers.put(EXPIRES, "-1");
101 assertFalse(cache.isCacheableContent(response));
102
103 headers.put(CACHE_CONTROL, "no-store");
104 assertFalse(cache.isCacheableContent(response));
105 }
106
107
108
109
110 @Test
111 public void contentWithNoHeadersIsNotCached() {
112 assertFalse(Cache.isWithinCacheWindow(new WebResponseMock(null, null), now_, now_));
113 }
114
115
116
117
118 @Test
119 public void contentWithExpiryDateIsCached() {
120 final Map<String, String> headers = new HashMap<>();
121 headers.put(EXPIRES, tomorrow_);
122
123 assertTrue(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_, now_));
124 }
125
126
127
128
129 @Test
130 public void contentWithExpiryDateInFutureButShortMaxAgeIsNotInCacheWindow() {
131 final Map<String, String> headers = new HashMap<>();
132 headers.put(EXPIRES, tomorrow_);
133
134 headers.put(CACHE_CONTROL, "some-other-value, max-age=1");
135
136 assertFalse(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + ONE_MINUTE, now_));
137 }
138
139
140
141
142 @Test
143 public void contentWithExpiryDateInFutureButShortSMaxAgeIsNotInCacheWindow() {
144 final Map<String, String> headers = new HashMap<>();
145 headers.put(EXPIRES, tomorrow_);
146
147 headers.put(CACHE_CONTROL, "some-other-value, s-maxage=1");
148
149 assertFalse(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + ONE_MINUTE, now_));
150 }
151
152
153
154
155 @Test
156 public void contentWithBothMaxAgeAndSMaxUsesSMaxAsPriority() {
157 final Map<String, String> headers = new HashMap<>();
158 headers.put(CACHE_CONTROL, "some-other-value, max-age=1200, s-maxage=1");
159
160 assertFalse(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + ONE_MINUTE, now_));
161 }
162
163
164
165
166 @Test
167 public void contentWithMaxAgeInFutureWillBeCached() {
168 final Map<String, String> headers = new HashMap<>();
169 headers.put(CACHE_CONTROL, "some-other-value, max-age=1200");
170
171 assertTrue(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_, now_));
172
173 headers.clear();
174 headers.put(CACHE_CONTROL, "some-other-value, max-age=1200");
175
176 assertTrue(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + ONE_MINUTE, now_));
177 }
178
179
180
181
182 @Test
183 public void contentWithLongLastModifiedTimeComparedToNowIsCachedOnDownload() {
184 final Map<String, String> headers = new HashMap<>();
185 headers.put(LAST_MODIFIED, formatDate(DateUtils.addDays(new Date(), -1)));
186
187 assertTrue(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_, now_));
188 }
189
190
191
192
193 @Test
194 public void contentWithLastModifiedTimeIsCachedAfterAFewPercentOfCreationAge() {
195 final Map<String, String> headers = new HashMap<>();
196 headers.put(LAST_MODIFIED, formatDate(DateUtils.addDays(new Date(), -1)));
197
198 assertTrue(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + ONE_HOUR, now_));
199 }
200
201
202
203
204 @Test
205 public void contentWithLastModifiedTimeIsNotCachedAfterALongerPeriod() {
206 final Map<String, String> headers = new HashMap<>();
207 headers.put(LAST_MODIFIED, formatDate(DateUtils.addDays(new Date(), -1)));
208
209 assertFalse(Cache.isWithinCacheWindow(new WebResponseMock(null, headers), now_ + (ONE_HOUR * 5), now_));
210 }
211
212
213
214
215 @Test
216 public void usage() throws Exception {
217 final String content = DOCTYPE_HTML
218 + "<html><head><title>page 1</title>\n"
219 + "<script src='foo1.js'></script>\n"
220 + "<script src='foo2.js'></script>\n"
221 + "</head><body>\n"
222 + "<a href='page2.html'>to page 2</a>\n"
223 + "</body></html>";
224
225 final String content2 = DOCTYPE_HTML
226 + "<html><head><title>page 2</title>\n"
227 + "<script src='foo2.js'></script>\n"
228 + "</head><body>\n"
229 + "<a href='page1.html'>to page 1</a>\n"
230 + "</body></html>";
231
232 final String script1 = "alert('in foo1');";
233 final String script2 = "alert('in foo2');";
234
235 final WebClient webClient = getWebClient();
236 final MockWebConnection connection = new MockWebConnection();
237 webClient.setWebConnection(connection);
238
239 final URL urlPage1 = new URL(URL_FIRST, "page1.html");
240 connection.setResponse(urlPage1, content);
241 final URL urlPage2 = new URL(URL_FIRST, "page2.html");
242 connection.setResponse(urlPage2, content2);
243
244 final List<NameValuePair> headers = new ArrayList<>();
245 headers.add(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
246 connection.setResponse(new URL(URL_FIRST, "foo1.js"), script1, 200, "ok",
247 MimeType.TEXT_JAVASCRIPT, headers);
248 connection.setResponse(new URL(URL_FIRST, "foo2.js"), script2, 200, "ok",
249 MimeType.TEXT_JAVASCRIPT, headers);
250
251 final List<String> collectedAlerts = new ArrayList<>();
252 webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
253
254 final HtmlPage page1 = webClient.getPage(urlPage1);
255 final String[] expectedAlerts = {"in foo1", "in foo2"};
256 assertEquals(expectedAlerts, collectedAlerts);
257
258 collectedAlerts.clear();
259 page1.getAnchors().get(0).click();
260
261 assertEquals(new String[] {"in foo2"}, collectedAlerts);
262 assertEquals("no request for scripts should have been performed",
263 urlPage2, connection.getLastWebRequest().getUrl());
264 }
265
266
267
268
269 @Test
270 public void jsUrlEncoded() throws Exception {
271 final String content = DOCTYPE_HTML
272 + "<html>\n"
273 + "<head>\n"
274 + " <title>page 1</title>\n"
275 + " <script src='foo1.js'></script>\n"
276 + " <script src='foo2.js?foo[1]=bar/baz'></script>\n"
277 + "</head>\n"
278 + "<body>\n"
279 + " <a href='page2.html'>to page 2</a>\n"
280 + "</body>\n"
281 + "</html>";
282
283 final String content2 = DOCTYPE_HTML
284 + "<html>\n"
285 + "<head>\n"
286 + " <title>page 2</title>\n"
287 + " <script src='foo2.js?foo[1]=bar/baz'></script>\n"
288 + "</head>\n"
289 + "<body>\n"
290 + " <a href='page1.html'>to page 1</a>\n"
291 + "</body>\n"
292 + "</html>";
293
294 final String script1 = "alert('in foo1');";
295 final String script2 = "alert('in foo2');";
296
297 final URL urlPage1 = new URL(URL_FIRST, "page1.html");
298 getMockWebConnection().setResponse(urlPage1, content);
299 final URL urlPage2 = new URL(URL_FIRST, "page2.html");
300 getMockWebConnection().setResponse(urlPage2, content2);
301
302 final List<NameValuePair> headers = new ArrayList<>();
303 headers.add(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
304 getMockWebConnection().setResponse(new URL(URL_FIRST, "foo1.js"), script1,
305 200, "ok", MimeType.TEXT_JAVASCRIPT, headers);
306 getMockWebConnection().setDefaultResponse(script2, 200, "ok", MimeType.TEXT_JAVASCRIPT, headers);
307
308 final WebClient webClient = getWebClientWithMockWebConnection();
309
310 final List<String> collectedAlerts = new ArrayList<>();
311 webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
312
313 final HtmlPage page1 = webClient.getPage(urlPage1);
314 final String[] expectedAlerts = {"in foo1", "in foo2"};
315 assertEquals(expectedAlerts, collectedAlerts);
316
317 collectedAlerts.clear();
318 page1.getAnchors().get(0).click();
319
320 assertEquals(new String[] {"in foo2"}, collectedAlerts);
321 assertEquals("no request for scripts should have been performed",
322 urlPage2, getMockWebConnection().getLastWebRequest().getUrl());
323 }
324
325
326
327
328 @Test
329 public void cssUrlEncoded() throws Exception {
330 final String content = DOCTYPE_HTML
331 + "<html>\n"
332 + "<head>\n"
333 + " <title>page 1</title>\n"
334 + " <link href='foo1.css' type='text/css' rel='stylesheet'>\n"
335 + " <link href='foo2.js?foo[1]=bar/baz' type='text/css' rel='stylesheet'>\n"
336 + "</head>\n"
337 + "<body>\n"
338 + " <a href='page2.html'>to page 2</a>\n"
339 + " <script>\n"
340 + " var sheets = document.styleSheets;\n"
341 + " alert(sheets.length);\n"
342 + " var rules = sheets[0].cssRules || sheets[0].rules;\n"
343 + " alert(rules.length);\n"
344 + " rules = sheets[1].cssRules || sheets[1].rules;\n"
345 + " alert(rules.length);\n"
346 + " </script>\n"
347 + "</body>\n"
348 + "</html>";
349
350 final String content2 = DOCTYPE_HTML
351 + "<html>\n"
352 + "<head>\n"
353 + " <title>page 2</title>\n"
354 + " <link href='foo2.js?foo[1]=bar/baz' type='text/css' rel='stylesheet'>\n"
355 + "</head>\n"
356 + "<body>\n"
357 + " <a href='page1.html'>to page 1</a>\n"
358 + " <script>\n"
359 + " var sheets = document.styleSheets;\n"
360 + " alert(sheets.length);\n"
361 + " var rules = sheets[0].cssRules || sheets[0].rules;\n"
362 + " alert(rules.length);\n"
363 + " </script>\n"
364 + "</body>\n"
365 + "</html>";
366
367 final URL urlPage1 = new URL(URL_FIRST, "page1.html");
368 getMockWebConnection().setResponse(urlPage1, content);
369 final URL urlPage2 = new URL(URL_FIRST, "page2.html");
370 getMockWebConnection().setResponse(urlPage2, content2);
371
372 final List<NameValuePair> headers = new ArrayList<>();
373 headers.add(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
374 getMockWebConnection().setResponse(new URL(URL_FIRST, "foo1.js"), "",
375 200, "ok", MimeType.TEXT_CSS, headers);
376 getMockWebConnection().setDefaultResponse("", 200, "ok", MimeType.TEXT_CSS, headers);
377
378 final WebClient webClient = getWebClientWithMockWebConnection();
379
380 final List<String> collectedAlerts = new ArrayList<>();
381 webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
382
383 final HtmlPage page1 = webClient.getPage(urlPage1);
384 final String[] expectedAlerts = {"2", "0", "0"};
385 assertEquals(expectedAlerts, collectedAlerts);
386 assertEquals(3, getMockWebConnection().getRequestCount());
387
388 collectedAlerts.clear();
389 page1.getAnchors().get(0).click();
390
391 assertEquals(new String[] {"1", "0"}, collectedAlerts);
392 assertEquals(4, getMockWebConnection().getRequestCount());
393 assertEquals("no request for scripts should have been performed",
394 urlPage2, getMockWebConnection().getLastWebRequest().getUrl());
395 }
396
397
398
399
400 @Test
401 public void maxSizeMaintained() throws Exception {
402 final String html = DOCTYPE_HTML
403 + "<html><head><title>page 1</title>\n"
404 + "<script src='foo1.js' type='text/javascript'/>\n"
405 + "<script src='foo2.js' type='text/javascript'/>\n"
406 + "</head><body>abc</body></html>";
407
408 final WebClient client = getWebClient();
409 client.getCache().setMaxSize(1);
410
411 final MockWebConnection connection = new MockWebConnection();
412 client.setWebConnection(connection);
413
414 final URL pageUrl = new URL(URL_FIRST, "page1.html");
415 connection.setResponse(pageUrl, html);
416
417 final List<NameValuePair> headers =
418 Collections.singletonList(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
419 connection.setResponse(new URL(URL_FIRST, "foo1.js"), ";", 200, "ok", MimeType.TEXT_JAVASCRIPT, headers);
420 connection.setResponse(new URL(URL_FIRST, "foo2.js"), ";", 200, "ok", MimeType.TEXT_JAVASCRIPT, headers);
421
422 client.getPage(pageUrl);
423 assertEquals(1, client.getCache().getSize());
424
425 client.getCache().clear();
426 assertEquals(0, client.getCache().getSize());
427 }
428
429
430
431
432
433 @Test
434 public void cssIsCached() throws Exception {
435 final String html = DOCTYPE_HTML
436 + "<html><head><title>page 1</title>\n"
437 + "<style>.x { color: red; }</style>\n"
438 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
439 + "</head>\n"
440 + "<body onload='document.styleSheets.item(0); document.styleSheets.item(1);'>x</body>\n"
441 + "</html>";
442
443 final WebClient client = getWebClient();
444
445 final MockWebConnection connection = new MockWebConnection();
446 client.setWebConnection(connection);
447
448 final URL pageUrl = new URL(URL_FIRST, "page1.html");
449 connection.setResponse(pageUrl, html);
450
451 final List<NameValuePair> headers =
452 Collections.singletonList(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
453 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_CSS, headers);
454
455 client.getPage(pageUrl);
456 assertEquals(2, client.getCache().getSize());
457 }
458
459
460
461
462
463
464 @Test
465 public void cssIsCachedIfUrlWasRedirected() throws Exception {
466 final String html = DOCTYPE_HTML
467 + "<html><head><title>page 1</title>\n"
468 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
469 + "</head>\n"
470 + "<body onload='document.styleSheets.item(0); document.styleSheets.item(1);'>x</body>\n"
471 + "</html>";
472
473 final String css = ".x { color: red; }";
474
475 final WebClient client = getWebClient();
476
477 final MockWebConnection connection = new MockWebConnection();
478 client.setWebConnection(connection);
479
480 final URL pageUrl = new URL(URL_FIRST, "page1.html");
481 connection.setResponse(pageUrl, html);
482
483 final URL cssUrl = new URL(URL_FIRST, "foo.css");
484 final URL redirectUrl = new URL(URL_FIRST, "fooContent.css");
485
486 List<NameValuePair> headers = new ArrayList<>();
487 headers.add(new NameValuePair("Location", redirectUrl.toExternalForm()));
488 connection.setResponse(cssUrl, "", 301, "Redirect", null, headers);
489
490 headers = Collections.singletonList(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
491 connection.setResponse(redirectUrl, css, 200, "OK", MimeType.TEXT_CSS, headers);
492
493 client.getPage(pageUrl);
494 client.getPage(pageUrl);
495
496
497 assertEquals(4, connection.getRequestCount());
498
499 assertEquals(2, client.getCache().getSize());
500 }
501
502
503
504
505 @Test
506 public void cssFromCacheIsUsed() throws Exception {
507 final String html = DOCTYPE_HTML
508 + "<html><head><title>page 1</title>\n"
509 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
510 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
511 + "</head>\n"
512 + "<body>x</body>\n"
513 + "</html>";
514
515 final String css = ".x { color: red; }";
516
517 final WebClient client = getWebClient();
518
519 final MockWebConnection connection = new MockWebConnection();
520 client.setWebConnection(connection);
521
522 final URL pageUrl = new URL(URL_FIRST, "page1.html");
523 connection.setResponse(pageUrl, html);
524
525 final URL cssUrl = new URL(URL_FIRST, "foo.css");
526 final List<NameValuePair> headers = new ArrayList<>();
527 headers.add(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
528 connection.setResponse(cssUrl, css, 200, "OK", MimeType.TEXT_CSS, headers);
529
530 client.getPage(pageUrl);
531
532 assertEquals(2, connection.getRequestCount());
533 assertEquals(1, client.getCache().getSize());
534 }
535
536
537
538
539 @Test
540 public void cssManuallyAddeToCache() throws Exception {
541 final String html = DOCTYPE_HTML
542 + "<html><head>\n"
543 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
544 + "</head>\n"
545 + "<body>\n"
546 + "abc <div class='test'>def</div>\n"
547 + "</body>\n"
548 + "</html>";
549
550 final String css = ".test { visibility: hidden; }";
551
552 final WebClient client = getWebClient();
553
554 final MockWebConnection connection = new MockWebConnection();
555 client.setWebConnection(connection);
556
557 final URL pageUrl = new URL(URL_FIRST, "page1.html");
558 connection.setResponse(pageUrl, html);
559
560 final URL cssUrl = new URL(URL_FIRST, "foo.css");
561 final List<NameValuePair> headers = new ArrayList<>();
562 headers.add(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
563 final WebRequest request = new WebRequest(cssUrl);
564 final WebResponseData data = new WebResponseData(css.getBytes("UTF-8"),
565 HttpStatus.OK_200, HttpStatus.OK_200_MSG, headers);
566 final WebResponse response = new WebResponse(data, request, 100);
567 client.getCache().cacheIfPossible(new WebRequest(cssUrl), response, headers);
568
569 final HtmlPage page = client.getPage(pageUrl);
570 assertEquals("abc", page.asNormalizedText());
571
572 assertEquals(1, connection.getRequestCount());
573 assertEquals(1, client.getCache().getSize());
574 }
575
576
577
578
579
580 @Test
581 @Alerts({"hello", "hello"})
582 public void xhrContentCached() throws Exception {
583 final String html = DOCTYPE_HTML
584 + "<html><head><title>page 1</title>\n"
585 + "<script>\n"
586 + " function doTest() {\n"
587 + " var xhr = new XMLHttpRequest();\n"
588 + " xhr.open('GET', 'foo.txt', false);\n"
589 + " xhr.send('');\n"
590 + " alert(xhr.responseText);\n"
591 + " xhr.send('');\n"
592 + " alert(xhr.responseText);\n"
593 + " }\n"
594 + "</script>\n"
595 + "</head>\n"
596 + "<body onload='doTest()'>x</body>\n"
597 + "</html>";
598
599 final MockWebConnection connection = getMockWebConnection();
600
601 final List<NameValuePair> headers =
602 Collections.singletonList(new NameValuePair(LAST_MODIFIED, "Sun, 15 Jul 2007 20:46:27 GMT"));
603 connection.setResponse(new URL(URL_FIRST, "foo.txt"), "hello", 200, "OK", MimeType.TEXT_PLAIN, headers);
604
605 loadPageWithAlerts(html);
606
607 assertEquals(2, connection.getRequestCount());
608 }
609
610
611
612
613 @Test
614 public void testNoStoreCacheControl() throws Exception {
615 final String html = DOCTYPE_HTML
616 + "<html><head><title>page 1</title>\n"
617 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
618 + "</head>\n"
619 + "<body>x</body>\n"
620 + "</html>";
621
622 final WebClient client = getWebClient();
623
624 final MockWebConnection connection = new MockWebConnection();
625 client.setWebConnection(connection);
626
627 final List<NameValuePair> headers = new ArrayList<>();
628 headers.add(new NameValuePair(CACHE_CONTROL, "some-other-value, no-store"));
629
630 final URL pageUrl = new URL(URL_FIRST, "page1.html");
631 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
632 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_JAVASCRIPT, headers);
633
634 client.getPage(pageUrl);
635 assertEquals(0, client.getCache().getSize());
636 assertEquals(2, connection.getRequestCount());
637
638 client.getPage(pageUrl);
639 assertEquals(0, client.getCache().getSize());
640 assertEquals(4, connection.getRequestCount());
641 }
642
643
644
645
646 @Test
647 public void testNoCacheCacheControl() throws Exception {
648 final String html = DOCTYPE_HTML
649 + "<html><head><title>page 1</title>\n"
650 + "</head>\n"
651 + "<body>x</body>\n"
652 + "</html>";
653
654 final WebClient client = getWebClient();
655
656 final MockWebConnection connection = new MockWebConnection();
657 client.setWebConnection(connection);
658
659 final String date = "Thu, 02 Mar 2023 02:00:00 GMT";
660 final String etag = "foo";
661 final String lastModified = "Wed, 01 Mar 2023 01:00:00 GMT";
662
663 final List<NameValuePair> headers = new ArrayList<>();
664 headers.add(new NameValuePair("Date", date));
665 headers.add(new NameValuePair(CACHE_CONTROL, "some-other-value, no-cache"));
666 headers.add(new NameValuePair(ETAG, etag));
667 headers.add(new NameValuePair(LAST_MODIFIED, lastModified));
668
669 final URL pageUrl = new URL(URL_FIRST, "page1.html");
670 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
671
672 client.getPage(pageUrl);
673 assertEquals(1, client.getCache().getSize());
674
675 final String updatedDate = "Thu, 02 Mar 2023 02:00:10 GMT";
676 final List<NameValuePair> headers2 = new ArrayList<>();
677 headers2.add(new NameValuePair("Date", updatedDate));
678 headers2.add(new NameValuePair("Proxy-Authorization", "Basic YWxhZGRpbjpvcGVuc2VzYW1l"));
679 headers2.add(new NameValuePair("X-Content-Type-Options", "nosniff"));
680 connection.setResponse(pageUrl, html, 304, "Not Modified", "text/html;charset=ISO-8859-1", headers2);
681
682 client.getPage(pageUrl);
683 assertEquals(2, connection.getRequestCount());
684
685 final WebRequest lastRequest = connection.getLastWebRequest();
686 assertEquals(etag, lastRequest.getAdditionalHeader(IF_NONE_MATCH));
687 assertEquals(lastModified, lastRequest.getAdditionalHeader(IF_MODIFIED_SINCE));
688 assertEquals(1, client.getCache().getSize());
689
690 WebResponse cached = client.getCache().getCachedResponse(connection.getLastWebRequest());
691 assertEquals(updatedDate, cached.getResponseHeaderValue("Date"));
692 assertEquals(null, cached.getResponseHeaderValue("Proxy-Authorization"));
693 assertEquals(null, cached.getResponseHeaderValue("X-Content-Type-Options"));
694
695 final String updatedEtag = "bar";
696 final String updatedLastModified = "Wed, 01 Mar 2023 02:00:00 GMT";
697
698 final List<NameValuePair> headers3 = new ArrayList<>();
699 headers3.add(new NameValuePair(CACHE_CONTROL, "some-other-value, no-cache"));
700 headers3.add(new NameValuePair(ETAG, updatedEtag));
701 headers3.add(new NameValuePair(LAST_MODIFIED, updatedLastModified));
702 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers3);
703
704 client.getPage(pageUrl);
705 assertEquals(3, connection.getRequestCount());
706 assertEquals(1, client.getCache().getSize());
707
708 cached = client.getCache().getCachedResponse(connection.getLastWebRequest());
709 assertEquals(null, cached.getResponseHeaderValue("Date"));
710 assertEquals(updatedEtag, cached.getResponseHeaderValue(ETAG));
711 assertEquals(updatedLastModified, cached.getResponseHeaderValue(LAST_MODIFIED));
712 }
713
714
715
716
717 @Test
718 public void testMaxAgeCacheControl() throws Exception {
719 final String html = DOCTYPE_HTML
720 + "<html><head><title>page 1</title>\n"
721 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
722 + "</head>\n"
723 + "<body>x</body>\n"
724 + "</html>";
725
726 final WebClient client = getWebClient();
727
728 final MockWebConnection connection = new MockWebConnection();
729 client.setWebConnection(connection);
730
731 final List<NameValuePair> headers = new ArrayList<>();
732 headers.add(new NameValuePair(LAST_MODIFIED, "Tue, 20 Feb 2018 10:00:00 GMT"));
733 headers.add(new NameValuePair(CACHE_CONTROL, "some-other-value, max-age=1"));
734
735 final URL pageUrl = new URL(URL_FIRST, "page1.html");
736 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
737 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_JAVASCRIPT, headers);
738
739 client.getPage(pageUrl);
740 assertEquals(2, client.getCache().getSize());
741 assertEquals(2, connection.getRequestCount());
742
743 client.getPage(pageUrl);
744 assertEquals(2, client.getCache().getSize());
745 assertEquals(2, connection.getRequestCount());
746
747 Thread.sleep(2 * 1000);
748 client.getPage(pageUrl);
749 assertEquals(2, client.getCache().getSize());
750 assertEquals(4, connection.getRequestCount());
751
752
753 Thread.sleep(2 * 1000);
754 client.getCache().clearOutdated();
755 assertEquals(0, client.getCache().getSize());
756 assertEquals(4, connection.getRequestCount());
757 }
758
759
760
761
762 @Test
763 public void testSMaxageCacheControl() throws Exception {
764 final String html = DOCTYPE_HTML
765 + "<html><head><title>page 1</title>\n"
766 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
767 + "</head>\n"
768 + "<body>x</body>\n"
769 + "</html>";
770
771 final WebClient client = getWebClient();
772
773 final MockWebConnection connection = new MockWebConnection();
774 client.setWebConnection(connection);
775
776 final List<NameValuePair> headers = new ArrayList<>();
777 headers.add(new NameValuePair(LAST_MODIFIED, "Tue, 20 Feb 2018 10:00:00 GMT"));
778 headers.add(new NameValuePair(CACHE_CONTROL, "public, s-maxage=1, some-other-value, max-age=10"));
779
780 final URL pageUrl = new URL(URL_FIRST, "page1.html");
781 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
782 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_JAVASCRIPT, headers);
783
784 client.getPage(pageUrl);
785 assertEquals(2, client.getCache().getSize());
786 assertEquals(2, connection.getRequestCount());
787
788 client.getPage(pageUrl);
789 assertEquals(2, client.getCache().getSize());
790 assertEquals(2, connection.getRequestCount());
791
792 Thread.sleep(2 * 1000);
793 client.getPage(pageUrl);
794 assertEquals(2, client.getCache().getSize());
795 assertEquals(4, connection.getRequestCount());
796
797
798 Thread.sleep(2 * 1000);
799 client.getCache().clearOutdated();
800 assertEquals(0, client.getCache().getSize());
801 assertEquals(4, connection.getRequestCount());
802 }
803
804
805
806
807 @Test
808 public void testExpiresCacheControl() throws Exception {
809 final String html = DOCTYPE_HTML
810 + "<html><head><title>page 1</title>\n"
811 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
812 + "</head>\n"
813 + "<body>x</body>\n"
814 + "</html>";
815
816 final WebClient client = getWebClient();
817
818 final MockWebConnection connection = new MockWebConnection();
819 client.setWebConnection(connection);
820
821 final List<NameValuePair> headers = new ArrayList<>();
822 headers.add(new NameValuePair(LAST_MODIFIED, "Tue, 20 Feb 2018 10:00:00 GMT"));
823 final Date expi = new Date(System.currentTimeMillis() + 2 * 1000 + 10 * DateUtils.MILLIS_PER_MINUTE);
824 headers.add(new NameValuePair(EXPIRES, new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz").format(expi)));
825 headers.add(new NameValuePair(CACHE_CONTROL, "public, some-other-value"));
826
827 final URL pageUrl = new URL(URL_FIRST, "page1.html");
828 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
829 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_JAVASCRIPT, headers);
830
831 client.getPage(pageUrl);
832 assertEquals(2, client.getCache().getSize());
833 assertEquals(2, connection.getRequestCount());
834
835 client.getPage(pageUrl);
836 assertEquals(2, client.getCache().getSize());
837 assertEquals(2, connection.getRequestCount());
838
839 Thread.sleep(2 * 1000);
840 client.getPage(pageUrl);
841 assertEquals(0, client.getCache().getSize());
842 assertEquals(4, connection.getRequestCount());
843 }
844
845
846
847
848 @Test
849 public void testMaxAgeOverrulesExpiresCacheControl() throws Exception {
850 final String html = DOCTYPE_HTML
851 + "<html><head><title>page 1</title>\n"
852 + "<link rel='stylesheet' type='text/css' href='foo.css' />\n"
853 + "</head>\n"
854 + "<body>x</body>\n"
855 + "</html>";
856
857 final WebClient client = getWebClient();
858
859 final MockWebConnection connection = new MockWebConnection();
860 client.setWebConnection(connection);
861
862 final List<NameValuePair> headers = new ArrayList<>();
863 headers.add(new NameValuePair(LAST_MODIFIED, "Tue, 20 Feb 2018 10:00:00 GMT"));
864 headers.add(new NameValuePair(EXPIRES, "0"));
865 headers.add(new NameValuePair(CACHE_CONTROL, "max-age=20"));
866
867 final URL pageUrl = new URL(URL_FIRST, "page1.html");
868 connection.setResponse(pageUrl, html, 200, "OK", "text/html;charset=ISO-8859-1", headers);
869 connection.setResponse(new URL(URL_FIRST, "foo.css"), "", 200, "OK", MimeType.TEXT_JAVASCRIPT, headers);
870
871 client.getPage(pageUrl);
872 assertEquals(2, client.getCache().getSize());
873 assertEquals(2, connection.getRequestCount());
874
875 client.getPage(pageUrl);
876 assertEquals(2, client.getCache().getSize());
877 assertEquals(2, connection.getRequestCount());
878 }
879
880
881
882
883
884 @Test
885 public void cleanUpOverflow() throws Exception {
886 final WebRequest request1 = new WebRequest(URL_FIRST, HttpMethod.GET);
887
888 final Map<String, String> headers = new HashMap<>();
889 headers.put(HttpHeader.EXPIRES, formatDate(DateUtils.addHours(new Date(), 1)));
890
891 final WebResponseMock response1 = new WebResponseMock(request1, headers);
892
893 final WebRequest request2 = new WebRequest(URL_SECOND, HttpMethod.GET);
894 final WebResponseMock response2 = new WebResponseMock(request2, headers);
895
896 final Cache cache = new Cache();
897 cache.setMaxSize(1);
898 cache.cacheIfPossible(request1, response1, null);
899 assertEquals(0, response1.getCallCount("cleanUp"));
900 assertEquals(0, response2.getCallCount("cleanUp"));
901 assertEquals(6, response1.getCallCount("getResponseHeaderValue"));
902 assertEquals(0, response2.getCallCount("getResponseHeaderValue"));
903
904 Thread.sleep(10);
905 cache.cacheIfPossible(request2, response2, null);
906 assertEquals(1, response1.getCallCount("cleanUp"));
907 assertEquals(0, response2.getCallCount("cleanUp"));
908 assertEquals(6, response1.getCallCount("getResponseHeaderValue"));
909 assertEquals(6, response2.getCallCount("getResponseHeaderValue"));
910 }
911
912
913
914
915 @Test
916 public void cleanUpOnClear() {
917 final WebRequest request1 = new WebRequest(URL_FIRST, HttpMethod.GET);
918
919 final Map<String, String> headers = new HashMap<>();
920 headers.put(HttpHeader.EXPIRES, formatDate(DateUtils.addHours(new Date(), 1)));
921
922 final WebResponseMock response1 = new WebResponseMock(request1, headers);
923
924 final Cache cache = new Cache();
925 cache.cacheIfPossible(request1, response1, null);
926 assertEquals(0, response1.getCallCount("cleanUp"));
927 assertEquals(6, response1.getCallCount("getResponseHeaderValue"));
928
929 cache.clear();
930
931 assertEquals(1, response1.getCallCount("cleanUp"));
932 assertEquals(6, response1.getCallCount("getResponseHeaderValue"));
933 }
934 }