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.cache;
28
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.net.SocketTimeoutException;
32 import java.util.Arrays;
33 import java.util.Date;
34 import java.util.Iterator;
35 import java.util.Random;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39 import org.apache.hc.client5.http.ClientProtocolException;
40 import org.apache.hc.client5.http.HttpRoute;
41 import org.apache.hc.client5.http.auth.StandardAuthScheme;
42 import org.apache.hc.client5.http.cache.HttpCacheEntry;
43 import org.apache.hc.client5.http.classic.ExecChain;
44 import org.apache.hc.client5.http.utils.DateUtils;
45 import org.apache.hc.core5.http.ClassicHttpRequest;
46 import org.apache.hc.core5.http.ClassicHttpResponse;
47 import org.apache.hc.core5.http.Header;
48 import org.apache.hc.core5.http.HeaderElement;
49 import org.apache.hc.core5.http.HeaderElements;
50 import org.apache.hc.core5.http.HttpHeaders;
51 import org.apache.hc.core5.http.HttpHost;
52 import org.apache.hc.core5.http.HttpStatus;
53 import org.apache.hc.core5.http.HttpVersion;
54 import org.apache.hc.core5.http.ProtocolVersion;
55 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
56 import org.apache.hc.core5.http.io.entity.StringEntity;
57 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
58 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
59 import org.apache.hc.core5.http.message.BasicHeader;
60 import org.apache.hc.core5.http.message.MessageSupport;
61 import org.apache.hc.core5.util.ByteArrayBuffer;
62 import org.easymock.Capture;
63 import org.easymock.EasyMock;
64 import org.junit.Assert;
65 import org.junit.Ignore;
66 import org.junit.Test;
67
68
69
70
71
72
73
74
75
76
77 public class TestProtocolRequirements extends AbstractProtocolTest {
78
79 @Test
80 public void testCacheMissOnGETUsesOriginResponse() throws Exception {
81 EasyMock.expect(
82 mockExecChain.proceed(
83 eqRequest(request),
84 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
85 replayMocks();
86
87 final ClassicHttpResponse result = execute(request);
88
89 verifyMocks();
90 Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104 @Test
105 public void testHigherMajorProtocolVersionsOnRequestSwitchToTunnelBehavior() throws Exception {
106
107
108
109 request = new BasicClassicHttpRequest("GET", "/foo");
110 request.setVersion(new ProtocolVersion("HTTP", 2, 13));
111
112 EasyMock.expect(
113 mockExecChain.proceed(
114 eqRequest(request),
115 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
116 replayMocks();
117
118 final ClassicHttpResponse result = execute(request);
119
120 verifyMocks();
121 Assert.assertSame(originResponse, result);
122 }
123
124 @Test
125 public void testHigher1_XProtocolVersionsDowngradeTo1_1() throws Exception {
126
127 request = new BasicClassicHttpRequest("GET", "/foo");
128 request.setVersion(new ProtocolVersion("HTTP", 1, 2));
129
130 final ClassicHttpRequest downgraded = new BasicClassicHttpRequest("GET", "/foo");
131 downgraded.setVersion(HttpVersion.HTTP_1_1);
132
133 EasyMock.expect(
134 mockExecChain.proceed(
135 eqRequest(downgraded),
136 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
137
138 replayMocks();
139 final ClassicHttpResponse result = execute(request);
140
141 verifyMocks();
142 Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
143 }
144
145
146
147
148
149
150
151
152
153
154 @Test
155 public void testRequestsWithLowerProtocolVersionsGetUpgradedTo1_1() throws Exception {
156
157 request = new BasicClassicHttpRequest("GET", "/foo");
158 request.setVersion(new ProtocolVersion("HTTP", 1, 0));
159 final ClassicHttpRequest upgraded = new BasicClassicHttpRequest("GET", "/foo");
160 upgraded.setVersion(HttpVersion.HTTP_1_1);
161
162 EasyMock.expect(
163 mockExecChain.proceed(
164 eqRequest(upgraded),
165 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
166 replayMocks();
167
168 final ClassicHttpResponse result = execute(request);
169
170 verifyMocks();
171 Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
172 }
173
174
175
176
177
178
179
180
181
182 @Test
183 public void testLowerOriginResponsesUpgradedToOurVersion1_1() throws Exception {
184 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
185 originResponse.setVersion(new ProtocolVersion("HTTP", 1, 2));
186 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
187 originResponse.setHeader("Server", "MockOrigin/1.0");
188 originResponse.setEntity(body);
189
190
191
192 EasyMock.expect(
193 mockExecChain.proceed(
194 EasyMock.isA(ClassicHttpRequest.class),
195 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
196 replayMocks();
197
198 final ClassicHttpResponse result = execute(request);
199
200 verifyMocks();
201 Assert.assertEquals(HttpVersion.HTTP_1_1, result.getVersion());
202 }
203
204 @Test
205 public void testResponseToA1_0RequestShouldUse1_1() throws Exception {
206 request = new BasicClassicHttpRequest("GET", "/foo");
207 request.setVersion(new ProtocolVersion("HTTP", 1, 0));
208
209 EasyMock.expect(
210 mockExecChain.proceed(
211 EasyMock.isA(ClassicHttpRequest.class),
212 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
213 replayMocks();
214
215 final ClassicHttpResponse result = execute(request);
216
217 verifyMocks();
218 Assert.assertEquals(HttpVersion.HTTP_1_1, result.getVersion());
219 }
220
221
222
223
224
225 @Test
226 public void testForwardsUnknownHeadersOnRequestsFromHigherProtocolVersions() throws Exception {
227 request = new BasicClassicHttpRequest("GET", "/foo");
228 request.setVersion(new ProtocolVersion("HTTP", 1, 2));
229 request.removeHeaders("Connection");
230 request.addHeader("X-Unknown-Header", "some-value");
231
232 final ClassicHttpRequest downgraded = new BasicClassicHttpRequest("GET", "/foo");
233 downgraded.setVersion(HttpVersion.HTTP_1_1);
234 downgraded.removeHeaders("Connection");
235 downgraded.addHeader("X-Unknown-Header", "some-value");
236
237 EasyMock.expect(
238 mockExecChain.proceed(
239 eqRequest(downgraded),
240 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
241 replayMocks();
242
243 execute(request);
244
245 verifyMocks();
246 }
247
248
249
250
251
252 @Test
253 public void testTransferCodingsAreNotSentToAnHTTP_1_0Client() throws Exception {
254
255 originResponse.setHeader("Transfer-Encoding", "identity");
256
257 final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/foo");
258 originalRequest.setVersion(new ProtocolVersion("HTTP", 1, 0));
259
260 EasyMock.expect(
261 mockExecChain.proceed(
262 EasyMock.isA(ClassicHttpRequest.class),
263 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
264 replayMocks();
265
266 final ClassicHttpResponse result = execute(originalRequest);
267
268 verifyMocks();
269
270 Assert.assertNull(result.getFirstHeader("TE"));
271 Assert.assertNull(result.getFirstHeader("Transfer-Encoding"));
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 private void testOrderOfMultipleHeadersIsPreservedOnRequests(final String h, final ClassicHttpRequest request) throws Exception {
288 final Capture<ClassicHttpRequest> reqCapture = EasyMock.newCapture();
289
290 EasyMock.expect(
291 mockExecChain.proceed(
292 EasyMock.capture(reqCapture),
293 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
294 replayMocks();
295
296 execute(request);
297
298 verifyMocks();
299
300 final ClassicHttpRequest forwarded = reqCapture.getValue();
301 Assert.assertNotNull(forwarded);
302 final String expected = HttpTestUtils.getCanonicalHeaderValue(request, h);
303 final String actual = HttpTestUtils.getCanonicalHeaderValue(forwarded, h);
304 if (!actual.contains(expected)) {
305 Assert.assertEquals(expected, actual);
306 }
307
308 }
309
310 @Test
311 public void testOrderOfMultipleAcceptHeaderValuesIsPreservedOnRequests() throws Exception {
312 request.addHeader("Accept", "audio/*; q=0.2, audio/basic");
313 request.addHeader("Accept", "text/*, text/html, text/html;level=1, */*");
314 testOrderOfMultipleHeadersIsPreservedOnRequests("Accept", request);
315 }
316
317 @Test
318 public void testOrderOfMultipleAcceptCharsetHeadersIsPreservedOnRequests() throws Exception {
319 request.addHeader("Accept-Charset", "iso-8859-5");
320 request.addHeader("Accept-Charset", "unicode-1-1;q=0.8");
321 testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Charset", request);
322 }
323
324 @Test
325 public void testOrderOfMultipleAcceptEncodingHeadersIsPreservedOnRequests() throws Exception {
326 request.addHeader("Accept-Encoding", "identity");
327 request.addHeader("Accept-Encoding", "compress, gzip");
328 testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Encoding", request);
329 }
330
331 @Test
332 public void testOrderOfMultipleAcceptLanguageHeadersIsPreservedOnRequests() throws Exception {
333 request.addHeader("Accept-Language", "da, en-gb;q=0.8, en;q=0.7");
334 request.addHeader("Accept-Language", "i-cherokee");
335 testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Encoding", request);
336 }
337
338 @Test
339 public void testOrderOfMultipleAllowHeadersIsPreservedOnRequests() throws Exception {
340 final BasicClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/");
341 put.setEntity(body);
342 put.addHeader("Allow", "GET, HEAD");
343 put.addHeader("Allow", "DELETE");
344 put.addHeader("Content-Length", "128");
345 testOrderOfMultipleHeadersIsPreservedOnRequests("Allow", put);
346 }
347
348 @Test
349 public void testOrderOfMultipleCacheControlHeadersIsPreservedOnRequests() throws Exception {
350 request.addHeader("Cache-Control", "max-age=5");
351 request.addHeader("Cache-Control", "min-fresh=10");
352 testOrderOfMultipleHeadersIsPreservedOnRequests("Cache-Control", request);
353 }
354
355 @Test
356 public void testOrderOfMultipleContentEncodingHeadersIsPreservedOnRequests() throws Exception {
357 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
358 post.setEntity(body);
359 post.addHeader("Content-Encoding", "gzip");
360 post.addHeader("Content-Encoding", "compress");
361 post.addHeader("Content-Length", "128");
362 testOrderOfMultipleHeadersIsPreservedOnRequests("Content-Encoding", post);
363 }
364
365 @Test
366 public void testOrderOfMultipleContentLanguageHeadersIsPreservedOnRequests() throws Exception {
367 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
368 post.setEntity(body);
369 post.addHeader("Content-Language", "mi");
370 post.addHeader("Content-Language", "en");
371 post.addHeader("Content-Length", "128");
372 testOrderOfMultipleHeadersIsPreservedOnRequests("Content-Language", post);
373 }
374
375 @Test
376 public void testOrderOfMultipleExpectHeadersIsPreservedOnRequests() throws Exception {
377 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
378 post.setEntity(body);
379 post.addHeader("Expect", "100-continue");
380 post.addHeader("Expect", "x-expect=true");
381 post.addHeader("Content-Length", "128");
382 testOrderOfMultipleHeadersIsPreservedOnRequests("Expect", post);
383 }
384
385 @Test
386 public void testOrderOfMultiplePragmaHeadersIsPreservedOnRequests() throws Exception {
387 request.addHeader("Pragma", "no-cache");
388 request.addHeader("Pragma", "x-pragma-1, x-pragma-2");
389 testOrderOfMultipleHeadersIsPreservedOnRequests("Pragma", request);
390 }
391
392 @Test
393 public void testOrderOfMultipleViaHeadersIsPreservedOnRequests() throws Exception {
394 request.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
395 request.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy");
396 testOrderOfMultipleHeadersIsPreservedOnRequests("Via", request);
397 }
398
399 @Test
400 public void testOrderOfMultipleWarningHeadersIsPreservedOnRequests() throws Exception {
401 request.addHeader("Warning", "199 fred \"bargle\"");
402 request.addHeader("Warning", "199 barney \"bungle\"");
403 testOrderOfMultipleHeadersIsPreservedOnRequests("Warning", request);
404 }
405
406 private void testOrderOfMultipleHeadersIsPreservedOnResponses(final String h) throws Exception {
407 EasyMock.expect(
408 mockExecChain.proceed(
409 EasyMock.isA(ClassicHttpRequest.class),
410 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
411 replayMocks();
412
413 final ClassicHttpResponse result = execute(request);
414
415 verifyMocks();
416
417 Assert.assertNotNull(result);
418 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse, h), HttpTestUtils
419 .getCanonicalHeaderValue(result, h));
420
421 }
422
423 @Test
424 public void testOrderOfMultipleAllowHeadersIsPreservedOnResponses() throws Exception {
425 originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
426 originResponse.addHeader("Allow", "HEAD");
427 originResponse.addHeader("Allow", "DELETE");
428 testOrderOfMultipleHeadersIsPreservedOnResponses("Allow");
429 }
430
431 @Test
432 public void testOrderOfMultipleCacheControlHeadersIsPreservedOnResponses() throws Exception {
433 originResponse.addHeader("Cache-Control", "max-age=0");
434 originResponse.addHeader("Cache-Control", "no-store, must-revalidate");
435 testOrderOfMultipleHeadersIsPreservedOnResponses("Cache-Control");
436 }
437
438 @Test
439 public void testOrderOfMultipleContentEncodingHeadersIsPreservedOnResponses() throws Exception {
440 originResponse.addHeader("Content-Encoding", "gzip");
441 originResponse.addHeader("Content-Encoding", "compress");
442 testOrderOfMultipleHeadersIsPreservedOnResponses("Content-Encoding");
443 }
444
445 @Test
446 public void testOrderOfMultipleContentLanguageHeadersIsPreservedOnResponses() throws Exception {
447 originResponse.addHeader("Content-Language", "mi");
448 originResponse.addHeader("Content-Language", "en");
449 testOrderOfMultipleHeadersIsPreservedOnResponses("Content-Language");
450 }
451
452 @Test
453 public void testOrderOfMultiplePragmaHeadersIsPreservedOnResponses() throws Exception {
454 originResponse.addHeader("Pragma", "no-cache, x-pragma-2");
455 originResponse.addHeader("Pragma", "x-pragma-1");
456 testOrderOfMultipleHeadersIsPreservedOnResponses("Pragma");
457 }
458
459 @Test
460 public void testOrderOfMultipleViaHeadersIsPreservedOnResponses() throws Exception {
461 originResponse.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
462 originResponse.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy");
463 testOrderOfMultipleHeadersIsPreservedOnResponses("Via");
464 }
465
466 @Test
467 public void testOrderOfMultipleWWWAuthenticateHeadersIsPreservedOnResponses() throws Exception {
468 originResponse.addHeader("WWW-Authenticate", "x-challenge-1");
469 originResponse.addHeader("WWW-Authenticate", "x-challenge-2");
470 testOrderOfMultipleHeadersIsPreservedOnResponses("WWW-Authenticate");
471 }
472
473
474
475
476
477
478
479
480
481 private void testUnknownResponseStatusCodeIsNotCached(final int code) throws Exception {
482
483 emptyMockCacheExpectsNoPuts();
484
485 originResponse = new BasicClassicHttpResponse(code, "Moo");
486 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
487 originResponse.setHeader("Server", "MockOrigin/1.0");
488 originResponse.setHeader("Cache-Control", "max-age=3600");
489 originResponse.setEntity(body);
490
491 EasyMock.expect(
492 mockExecChain.proceed(
493 EasyMock.isA(ClassicHttpRequest.class),
494 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
495
496 replayMocks();
497
498 execute(request);
499
500
501 verifyMocks();
502 }
503
504 @Test
505 public void testUnknownResponseStatusCodesAreNotCached() throws Exception {
506 for (int i = 102; i <= 199; i++) {
507 testUnknownResponseStatusCodeIsNotCached(i);
508 }
509 for (int i = 207; i <= 299; i++) {
510 testUnknownResponseStatusCodeIsNotCached(i);
511 }
512 for (int i = 308; i <= 399; i++) {
513 testUnknownResponseStatusCodeIsNotCached(i);
514 }
515 for (int i = 418; i <= 499; i++) {
516 testUnknownResponseStatusCodeIsNotCached(i);
517 }
518 for (int i = 506; i <= 999; i++) {
519 testUnknownResponseStatusCodeIsNotCached(i);
520 }
521 }
522
523
524
525
526
527
528
529 @Test
530 public void testUnknownHeadersOnRequestsAreForwarded() throws Exception {
531 request.addHeader("X-Unknown-Header", "blahblah");
532 final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
533 EasyMock.expect(
534 mockExecChain.proceed(
535 EasyMock.capture(reqCap),
536 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
537
538 replayMocks();
539
540 execute(request);
541
542 verifyMocks();
543 final ClassicHttpRequest forwarded = reqCap.getValue();
544 final Header[] hdrs = forwarded.getHeaders("X-Unknown-Header");
545 Assert.assertEquals(1, hdrs.length);
546 Assert.assertEquals("blahblah", hdrs[0].getValue());
547 }
548
549 @Test
550 public void testUnknownHeadersOnResponsesAreForwarded() throws Exception {
551 originResponse.addHeader("X-Unknown-Header", "blahblah");
552 EasyMock.expect(
553 mockExecChain.proceed(
554 EasyMock.isA(ClassicHttpRequest.class),
555 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
556
557 replayMocks();
558
559 final ClassicHttpResponse result = execute(request);
560
561 verifyMocks();
562 final Header[] hdrs = result.getHeaders("X-Unknown-Header");
563 Assert.assertEquals(1, hdrs.length);
564 Assert.assertEquals("blahblah", hdrs[0].getValue());
565 }
566
567
568
569
570
571
572
573
574 @Test
575 public void testRequestsExpecting100ContinueBehaviorShouldSetExpectHeader() throws Exception {
576 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
577 post.setHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
578 post.setHeader("Content-Length", "128");
579 post.setEntity(new StringEntity("whatever"));
580
581 final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
582
583 EasyMock.expect(
584 mockExecChain.proceed(
585 EasyMock.capture(reqCap),
586 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
587
588 replayMocks();
589
590 execute(post);
591
592 verifyMocks();
593
594 final ClassicHttpRequest forwarded = reqCap.getValue();
595 boolean foundExpect = false;
596 final Iterator<HeaderElement> it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT);
597 while (it.hasNext()) {
598 final HeaderElement elt = it.next();
599 if ("100-continue".equalsIgnoreCase(elt.getName())) {
600 foundExpect = true;
601 break;
602 }
603 }
604 Assert.assertTrue(foundExpect);
605 }
606
607
608
609
610
611
612
613
614 @Test
615 public void testRequestsNotExpecting100ContinueBehaviorShouldNotSetExpectContinueHeader() throws Exception {
616 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
617 post.setHeader("Content-Length", "128");
618 post.setEntity(new StringEntity("whatever"));
619
620 final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
621
622 EasyMock.expect(
623 mockExecChain.proceed(
624 EasyMock.capture(reqCap),
625 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
626
627 replayMocks();
628
629 execute(post);
630
631 verifyMocks();
632
633 final ClassicHttpRequest forwarded = reqCap.getValue();
634 boolean foundExpect = false;
635 final Iterator<HeaderElement> it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT);
636 while (it.hasNext()) {
637 final HeaderElement elt = it.next();
638 if ("100-continue".equalsIgnoreCase(elt.getName())) {
639 foundExpect = true;
640 break;
641 }
642 }
643 Assert.assertFalse(foundExpect);
644 }
645
646
647
648
649
650
651
652
653
654
655 @Test
656 public void testExpectHeadersAreForwardedOnRequests() throws Exception {
657
658
659
660
661
662
663
664 }
665
666
667
668
669
670
671
672
673
674
675 @Test
676 public void test100ContinueResponsesAreNotForwardedTo1_0ClientsWhoDidNotAskForThem() throws Exception {
677
678 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
679 post.setVersion(new ProtocolVersion("HTTP", 1, 0));
680 post.setEntity(body);
681 post.setHeader("Content-Length", "128");
682
683 originResponse = new BasicClassicHttpResponse(100, "Continue");
684 EasyMock.expect(
685 mockExecChain.proceed(
686 EasyMock.isA(ClassicHttpRequest.class),
687 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
688 replayMocks();
689
690 try {
691
692
693 execute(post);
694 Assert.fail("should have thrown an exception");
695 } catch (final ClientProtocolException expected) {
696 }
697
698 verifyMocks();
699 }
700
701
702
703
704
705
706 @Test
707 public void testResponsesToOPTIONSAreNotCacheable() throws Exception {
708 emptyMockCacheExpectsNoPuts();
709 request = new BasicClassicHttpRequest("OPTIONS", "/");
710 originResponse.addHeader("Cache-Control", "max-age=3600");
711
712 EasyMock.expect(
713 mockExecChain.proceed(
714 EasyMock.isA(ClassicHttpRequest.class),
715 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
716
717 replayMocks();
718
719 execute(request);
720
721 verifyMocks();
722 }
723
724
725
726
727
728
729
730 @Test
731 public void test200ResponseToOPTIONSWithNoBodyShouldIncludeContentLengthZero() throws Exception {
732
733 request = new BasicClassicHttpRequest("OPTIONS", "/");
734 originResponse.setEntity(null);
735 originResponse.setHeader("Content-Length", "0");
736
737 EasyMock.expect(
738 mockExecChain.proceed(
739 EasyMock.isA(ClassicHttpRequest.class),
740 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
741 replayMocks();
742
743 final ClassicHttpResponse result = execute(request);
744
745 verifyMocks();
746 final Header contentLength = result.getFirstHeader("Content-Length");
747 Assert.assertNotNull(contentLength);
748 Assert.assertEquals("0", contentLength.getValue());
749 }
750
751
752
753
754
755
756
757
758
759
760 @Test
761 public void testDoesNotForwardOPTIONSWhenMaxForwardsIsZeroOnAbsoluteURIRequest() throws Exception {
762 request = new BasicClassicHttpRequest("OPTIONS", "*");
763 request.setHeader("Max-Forwards", "0");
764
765 replayMocks();
766 execute(request);
767 verifyMocks();
768 }
769
770
771
772
773
774
775
776 @Test
777 public void testDecrementsMaxForwardsWhenForwardingOPTIONSRequest() throws Exception {
778
779 request = new BasicClassicHttpRequest("OPTIONS", "*");
780 request.setHeader("Max-Forwards", "7");
781
782 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
783
784 EasyMock.expect(
785 mockExecChain.proceed(
786 EasyMock.capture(cap),
787 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
788
789 replayMocks();
790 execute(request);
791 verifyMocks();
792
793 final ClassicHttpRequest captured = cap.getValue();
794 Assert.assertEquals("6", captured.getFirstHeader("Max-Forwards").getValue());
795 }
796
797
798
799
800
801
802
803 @Test
804 public void testDoesNotAddAMaxForwardsHeaderToForwardedOPTIONSRequests() throws Exception {
805 request = new BasicClassicHttpRequest("OPTIONS", "/");
806 final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
807 EasyMock.expect(
808 mockExecChain.proceed(
809 EasyMock.capture(reqCap),
810 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
811
812 replayMocks();
813 execute(request);
814 verifyMocks();
815
816 final ClassicHttpRequest forwarded = reqCap.getValue();
817 Assert.assertNull(forwarded.getFirstHeader("Max-Forwards"));
818 }
819
820
821
822
823
824
825
826 @Test
827 public void testResponseToAHEADRequestMustNotHaveABody() throws Exception {
828 request = new BasicClassicHttpRequest("HEAD", "/");
829 EasyMock.expect(
830 mockExecChain.proceed(
831 EasyMock.isA(ClassicHttpRequest.class),
832 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
833
834 replayMocks();
835
836 final ClassicHttpResponse result = execute(request);
837
838 verifyMocks();
839
840 Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
841 }
842
843
844
845
846
847
848
849
850
851 private void testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale(final String eHeader,
852 final String oldVal, final String newVal) throws Exception {
853
854
855 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
856 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
857 resp1.addHeader("Cache-Control", "max-age=3600");
858 resp1.setHeader(eHeader, oldVal);
859
860
861 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("HEAD", "/");
862 req2.addHeader("Cache-Control", "no-cache");
863 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
864 resp2.setEntity(null);
865 resp2.setHeader(eHeader, newVal);
866
867
868 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
869 req3.addHeader("Cache-Control", "max-stale=0");
870 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
871 resp3.setHeader(eHeader, newVal);
872
873 EasyMock.expect(
874 mockExecChain.proceed(
875 eqRequest(req1),
876 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
877 EasyMock.expect(
878 mockExecChain.proceed(
879 eqRequest(req2),
880 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
881 EasyMock.expect(
882 mockExecChain.proceed(
883 EasyMock.isA(ClassicHttpRequest.class),
884 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
885
886 replayMocks();
887
888 execute(req1);
889 execute(req2);
890 execute(req3);
891
892 verifyMocks();
893 }
894
895 @Test
896 public void testHEADResponseWithUpdatedContentLengthFieldMakeACacheEntryStale() throws Exception {
897 testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Content-Length", "128", "127");
898 }
899
900 @Test
901 public void testHEADResponseWithUpdatedContentMD5FieldMakeACacheEntryStale() throws Exception {
902 testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Content-MD5",
903 "Q2hlY2sgSW50ZWdyaXR5IQ==", "Q2hlY2sgSW50ZWdyaXR5IR==");
904
905 }
906
907 @Test
908 public void testHEADResponseWithUpdatedETagFieldMakeACacheEntryStale() throws Exception {
909 testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("ETag", "\"etag1\"",
910 "\"etag2\"");
911 }
912
913 @Test
914 public void testHEADResponseWithUpdatedLastModifiedFieldMakeACacheEntryStale() throws Exception {
915 final Date now = new Date();
916 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
917 final Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
918 testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Last-Modified", DateUtils
919 .formatDate(tenSecondsAgo), DateUtils.formatDate(sixSecondsAgo));
920 }
921
922
923
924
925
926
927
928 @Test
929 public void testResponsesToPOSTWithoutCacheControlOrExpiresAreNotCached() throws Exception {
930 emptyMockCacheExpectsNoPuts();
931
932 final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
933 post.setHeader("Content-Length", "128");
934 post.setEntity(HttpTestUtils.makeBody(128));
935
936 originResponse.removeHeaders("Cache-Control");
937 originResponse.removeHeaders("Expires");
938
939 EasyMock.expect(
940 mockExecChain.proceed(
941 EasyMock.isA(ClassicHttpRequest.class),
942 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
943
944 replayMocks();
945
946 execute(post);
947
948 verifyMocks();
949 }
950
951
952
953
954
955
956 @Test
957 public void testResponsesToPUTsAreNotCached() throws Exception {
958 emptyMockCacheExpectsNoPuts();
959
960 final BasicClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/");
961 put.setEntity(HttpTestUtils.makeBody(128));
962 put.addHeader("Content-Length", "128");
963
964 originResponse.setHeader("Cache-Control", "max-age=3600");
965
966 EasyMock.expect(
967 mockExecChain.proceed(
968 EasyMock.isA(ClassicHttpRequest.class),
969 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
970
971 replayMocks();
972
973 execute(put);
974
975 verifyMocks();
976 }
977
978
979
980
981
982
983 @Test
984 public void testResponsesToDELETEsAreNotCached() throws Exception {
985 emptyMockCacheExpectsNoPuts();
986
987 request = new BasicClassicHttpRequest("DELETE", "/");
988 originResponse.setHeader("Cache-Control", "max-age=3600");
989
990 EasyMock.expect(
991 mockExecChain.proceed(
992 EasyMock.isA(ClassicHttpRequest.class),
993 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
994
995 replayMocks();
996
997 execute(request);
998
999 verifyMocks();
1000 }
1001
1002
1003
1004
1005
1006
1007 @Test
1008 public void testResponsesToTRACEsAreNotCached() throws Exception {
1009 emptyMockCacheExpectsNoPuts();
1010
1011 request = new BasicClassicHttpRequest("TRACE", "/");
1012 originResponse.setHeader("Cache-Control", "max-age=3600");
1013
1014 EasyMock.expect(
1015 mockExecChain.proceed(
1016 EasyMock.isA(ClassicHttpRequest.class),
1017 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1018
1019 replayMocks();
1020
1021 execute(request);
1022
1023 verifyMocks();
1024 }
1025
1026
1027
1028
1029
1030
1031
1032 @Test
1033 public void test204ResponsesDoNotContainMessageBodies() throws Exception {
1034 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
1035 originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
1036
1037 EasyMock.expect(
1038 mockExecChain.proceed(
1039 EasyMock.isA(ClassicHttpRequest.class),
1040 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1041
1042 replayMocks();
1043
1044 final ClassicHttpResponse result = execute(request);
1045
1046 verifyMocks();
1047 }
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068 @Test
1069 public void test206ResponseGeneratedFromCacheMustHaveContentRangeOrMultipartByteRangesContentType() throws Exception {
1070
1071 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1072 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1073 resp1.setHeader("ETag", "\"etag\"");
1074 resp1.setHeader("Cache-Control", "max-age=3600");
1075
1076 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1077 req2.setHeader("Range", "bytes=0-50");
1078
1079 backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
1080
1081 replayMocks();
1082 execute(req1);
1083 final ClassicHttpResponse result = execute(req2);
1084 verifyMocks();
1085
1086 if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
1087 if (result.getFirstHeader("Content-Range") == null) {
1088 final HeaderElement elt = MessageSupport.parse(result.getFirstHeader("Content-Type"))[0];
1089 Assert.assertTrue("multipart/byteranges".equalsIgnoreCase(elt.getName()));
1090 Assert.assertNotNull(elt.getParameterByName("boundary"));
1091 Assert.assertNotNull(elt.getParameterByName("boundary").getValue());
1092 Assert.assertFalse("".equals(elt.getParameterByName("boundary").getValue().trim()));
1093 }
1094 }
1095 }
1096
1097 @Test
1098 public void test206ResponseGeneratedFromCacheMustHaveABodyThatMatchesContentLengthHeaderIfPresent() throws Exception {
1099
1100 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1101 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1102 resp1.setHeader("ETag", "\"etag\"");
1103 resp1.setHeader("Cache-Control", "max-age=3600");
1104
1105 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1106 req2.setHeader("Range", "bytes=0-50");
1107
1108 backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
1109
1110 replayMocks();
1111 execute(req1);
1112 final ClassicHttpResponse result = execute(req2);
1113 verifyMocks();
1114
1115 if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
1116 final Header h = result.getFirstHeader("Content-Length");
1117 if (h != null) {
1118 final int contentLength = Integer.parseInt(h.getValue());
1119 int bytesRead = 0;
1120 final InputStream i = result.getEntity().getContent();
1121 while ((i.read()) != -1) {
1122 bytesRead++;
1123 }
1124 i.close();
1125 Assert.assertEquals(contentLength, bytesRead);
1126 }
1127 }
1128 }
1129
1130 @Test
1131 public void test206ResponseGeneratedFromCacheMustHaveDateHeader() throws Exception {
1132 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1133 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1134 resp1.setHeader("ETag", "\"etag\"");
1135 resp1.setHeader("Cache-Control", "max-age=3600");
1136
1137 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1138 req2.setHeader("Range", "bytes=0-50");
1139
1140 backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
1141
1142 replayMocks();
1143 execute(req1);
1144 final ClassicHttpResponse result = execute(req2);
1145 verifyMocks();
1146
1147 if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
1148 Assert.assertNotNull(result.getFirstHeader("Date"));
1149 }
1150 }
1151
1152 @Test
1153 public void test206ResponseReturnedToClientMustHaveDateHeader() throws Exception {
1154 request.addHeader("Range", "bytes=0-50");
1155 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
1156 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
1157 originResponse.setHeader("Server", "MockOrigin/1.0");
1158 originResponse.setEntity(HttpTestUtils.makeBody(500));
1159 originResponse.setHeader("Content-Range", "bytes 0-499/1234");
1160 originResponse.removeHeaders("Date");
1161
1162 EasyMock.expect(
1163 mockExecChain.proceed(
1164 EasyMock.isA(ClassicHttpRequest.class),
1165 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1166
1167 replayMocks();
1168
1169 final ClassicHttpResponse result = execute(request);
1170 Assert.assertTrue(result.getCode() != HttpStatus.SC_PARTIAL_CONTENT
1171 || result.getFirstHeader("Date") != null);
1172
1173 verifyMocks();
1174 }
1175
1176 @Test
1177 public void test206ContainsETagIfA200ResponseWouldHaveIncludedIt() throws Exception {
1178 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1179
1180 originResponse.addHeader("Cache-Control", "max-age=3600");
1181 originResponse.addHeader("ETag", "\"etag1\"");
1182
1183 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1184 req2.addHeader("Range", "bytes=0-50");
1185
1186 backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
1187
1188 replayMocks();
1189
1190 execute(req1);
1191 final ClassicHttpResponse result = execute(req2);
1192
1193 verifyMocks();
1194
1195 if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
1196 Assert.assertNotNull(result.getFirstHeader("ETag"));
1197 }
1198 }
1199
1200 @Test
1201 public void test206ContainsContentLocationIfA200ResponseWouldHaveIncludedIt() throws Exception {
1202 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1203
1204 originResponse.addHeader("Cache-Control", "max-age=3600");
1205 originResponse.addHeader("Content-Location", "http://foo.example.com/other/url");
1206
1207 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1208 req2.addHeader("Range", "bytes=0-50");
1209
1210 backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
1211
1212 replayMocks();
1213
1214 execute(req1);
1215 final ClassicHttpResponse result = execute(req2);
1216
1217 verifyMocks();
1218
1219 if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
1220 Assert.assertNotNull(result.getFirstHeader("Content-Location"));
1221 }
1222 }
1223
1224 @Test
1225 public void test206ResponseIncludesVariantHeadersIfValueMightDiffer() throws Exception {
1226
1227 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1228 req1.addHeader("Accept-Encoding", "gzip");
1229
1230 final Date now = new Date();
1231 final Date inOneHour = new Date(now.getTime() + 3600 * 1000L);
1232 originResponse.addHeader("Cache-Control", "max-age=3600");
1233 originResponse.addHeader("Expires", DateUtils.formatDate(inOneHour));
1234 originResponse.addHeader("Vary", "Accept-Encoding");
1235
1236 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1237 req2.addHeader("Cache-Control", "no-cache");
1238 req2.addHeader("Accept-Encoding", "gzip");
1239 final Date nextSecond = new Date(now.getTime() + 1000L);
1240 final Date inTwoHoursPlusASec = new Date(now.getTime() + 2 * 3600 * 1000L + 1000L);
1241
1242 final ClassicHttpResponse originResponse2 = HttpTestUtils.make200Response();
1243 originResponse2.setHeader("Date", DateUtils.formatDate(nextSecond));
1244 originResponse2.setHeader("Cache-Control", "max-age=7200");
1245 originResponse2.setHeader("Expires", DateUtils.formatDate(inTwoHoursPlusASec));
1246 originResponse2.setHeader("Vary", "Accept-Encoding");
1247
1248 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
1249 req3.addHeader("Range", "bytes=0-50");
1250 req3.addHeader("Accept-Encoding", "gzip");
1251
1252 backendExpectsAnyRequest().andReturn(originResponse);
1253 backendExpectsAnyRequestAndReturn(originResponse2).times(1, 2);
1254
1255 replayMocks();
1256
1257 execute(req1);
1258 execute(req2);
1259 final ClassicHttpResponse result = execute(req3);
1260
1261 verifyMocks();
1262
1263 if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
1264 Assert.assertNotNull(result.getFirstHeader("Expires"));
1265 Assert.assertNotNull(result.getFirstHeader("Cache-Control"));
1266 Assert.assertNotNull(result.getFirstHeader("Vary"));
1267 }
1268 }
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278 @Test
1279 public void test206ResponseToConditionalRangeRequestDoesNotIncludeOtherEntityHeaders() throws Exception {
1280
1281 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1282
1283 final Date now = new Date();
1284 final Date oneHourAgo = new Date(now.getTime() - 3600 * 1000L);
1285 originResponse = HttpTestUtils.make200Response();
1286 originResponse.addHeader("Allow", "GET,HEAD");
1287 originResponse.addHeader("Cache-Control", "max-age=3600");
1288 originResponse.addHeader("Content-Language", "en");
1289 originResponse.addHeader("Content-Encoding", "x-coding");
1290 originResponse.addHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
1291 originResponse.addHeader("Content-Length", "128");
1292 originResponse.addHeader("Content-Type", "application/octet-stream");
1293 originResponse.addHeader("Last-Modified", DateUtils.formatDate(oneHourAgo));
1294 originResponse.addHeader("ETag", "W/\"weak-tag\"");
1295
1296 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1297 req2.addHeader("If-Range", "W/\"weak-tag\"");
1298 req2.addHeader("Range", "bytes=0-50");
1299
1300 backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
1301
1302 replayMocks();
1303
1304 execute(req1);
1305 final ClassicHttpResponse result = execute(req2);
1306
1307 verifyMocks();
1308
1309 if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
1310 Assert.assertNull(result.getFirstHeader("Allow"));
1311 Assert.assertNull(result.getFirstHeader("Content-Encoding"));
1312 Assert.assertNull(result.getFirstHeader("Content-Language"));
1313 Assert.assertNull(result.getFirstHeader("Content-MD5"));
1314 Assert.assertNull(result.getFirstHeader("Last-Modified"));
1315 }
1316 }
1317
1318
1319
1320
1321
1322
1323
1324
1325 @Test
1326 public void test206ResponseToIfRangeWithStrongValidatorReturnsAllEntityHeaders() throws Exception {
1327
1328 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1329
1330 final Date now = new Date();
1331 final Date oneHourAgo = new Date(now.getTime() - 3600 * 1000L);
1332 originResponse.addHeader("Allow", "GET,HEAD");
1333 originResponse.addHeader("Cache-Control", "max-age=3600");
1334 originResponse.addHeader("Content-Language", "en");
1335 originResponse.addHeader("Content-Encoding", "x-coding");
1336 originResponse.addHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
1337 originResponse.addHeader("Content-Length", "128");
1338 originResponse.addHeader("Content-Type", "application/octet-stream");
1339 originResponse.addHeader("Last-Modified", DateUtils.formatDate(oneHourAgo));
1340 originResponse.addHeader("ETag", "\"strong-tag\"");
1341
1342 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1343 req2.addHeader("If-Range", "\"strong-tag\"");
1344 req2.addHeader("Range", "bytes=0-50");
1345
1346 backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
1347
1348 replayMocks();
1349
1350 execute(req1);
1351 final ClassicHttpResponse result = execute(req2);
1352
1353 verifyMocks();
1354
1355 if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
1356 Assert.assertEquals("GET,HEAD", result.getFirstHeader("Allow").getValue());
1357 Assert.assertEquals("max-age=3600", result.getFirstHeader("Cache-Control").getValue());
1358 Assert.assertEquals("en", result.getFirstHeader("Content-Language").getValue());
1359 Assert.assertEquals("x-coding", result.getFirstHeader("Content-Encoding").getValue());
1360 Assert.assertEquals("Q2hlY2sgSW50ZWdyaXR5IQ==", result.getFirstHeader("Content-MD5")
1361 .getValue());
1362 Assert.assertEquals(originResponse.getFirstHeader("Last-Modified").getValue(), result
1363 .getFirstHeader("Last-Modified").getValue());
1364 }
1365 }
1366
1367
1368
1369
1370
1371
1372
1373
1374 @Test
1375 public void test206ResponseIsNotCombinedWithPreviousContentIfETagDoesNotMatch() throws Exception {
1376
1377 final Date now = new Date();
1378
1379 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1380 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1381 resp1.setHeader("Cache-Control", "max-age=3600");
1382 resp1.setHeader("ETag", "\"etag1\"");
1383 final byte[] bytes1 = new byte[128];
1384 Arrays.fill(bytes1, (byte) 1);
1385 resp1.setEntity(new ByteArrayEntity(bytes1, null));
1386
1387 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1388 req2.setHeader("Cache-Control", "no-cache");
1389 req2.setHeader("Range", "bytes=0-50");
1390
1391 final Date inOneSecond = new Date(now.getTime() + 1000L);
1392 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT,
1393 "Partial Content");
1394 resp2.setHeader("Date", DateUtils.formatDate(inOneSecond));
1395 resp2.setHeader("Server", resp1.getFirstHeader("Server").getValue());
1396 resp2.setHeader("ETag", "\"etag2\"");
1397 resp2.setHeader("Content-Range", "bytes 0-50/128");
1398 final byte[] bytes2 = new byte[51];
1399 Arrays.fill(bytes2, (byte) 2);
1400 resp2.setEntity(new ByteArrayEntity(bytes2, null));
1401
1402 final Date inTwoSeconds = new Date(now.getTime() + 2000L);
1403 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
1404 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
1405 resp3.setHeader("Date", DateUtils.formatDate(inTwoSeconds));
1406 resp3.setHeader("Cache-Control", "max-age=3600");
1407 resp3.setHeader("ETag", "\"etag2\"");
1408 final byte[] bytes3 = new byte[128];
1409 Arrays.fill(bytes3, (byte) 2);
1410 resp3.setEntity(new ByteArrayEntity(bytes3, null));
1411
1412 EasyMock.expect(
1413 mockExecChain.proceed(
1414 EasyMock.isA(ClassicHttpRequest.class),
1415 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
1416 EasyMock.expect(
1417 mockExecChain.proceed(
1418 EasyMock.isA(ClassicHttpRequest.class),
1419 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
1420 EasyMock.expect(
1421 mockExecChain.proceed(
1422 EasyMock.isA(ClassicHttpRequest.class),
1423 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3).times(0, 1);
1424 replayMocks();
1425
1426 execute(req1);
1427 execute(req2);
1428 final ClassicHttpResponse result = execute(req3);
1429
1430 verifyMocks();
1431
1432 final InputStream i = result.getEntity().getContent();
1433 int b;
1434 boolean found1 = false;
1435 boolean found2 = false;
1436 while ((b = i.read()) != -1) {
1437 if (b == 1) {
1438 found1 = true;
1439 }
1440 if (b == 2) {
1441 found2 = true;
1442 }
1443 }
1444 i.close();
1445 Assert.assertFalse(found1 && found2);
1446 }
1447
1448 @Test
1449 public void test206ResponseIsNotCombinedWithPreviousContentIfLastModifiedDoesNotMatch() throws Exception {
1450
1451 final Date now = new Date();
1452
1453 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1454 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1455 final Date oneHourAgo = new Date(now.getTime() - 3600L);
1456 resp1.setHeader("Cache-Control", "max-age=3600");
1457 resp1.setHeader("Last-Modified", DateUtils.formatDate(oneHourAgo));
1458 final byte[] bytes1 = new byte[128];
1459 Arrays.fill(bytes1, (byte) 1);
1460 resp1.setEntity(new ByteArrayEntity(bytes1, null));
1461
1462 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1463 req2.setHeader("Cache-Control", "no-cache");
1464 req2.setHeader("Range", "bytes=0-50");
1465
1466 final Date inOneSecond = new Date(now.getTime() + 1000L);
1467 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT,
1468 "Partial Content");
1469 resp2.setHeader("Date", DateUtils.formatDate(inOneSecond));
1470 resp2.setHeader("Server", resp1.getFirstHeader("Server").getValue());
1471 resp2.setHeader("Last-Modified", DateUtils.formatDate(now));
1472 resp2.setHeader("Content-Range", "bytes 0-50/128");
1473 final byte[] bytes2 = new byte[51];
1474 Arrays.fill(bytes2, (byte) 2);
1475 resp2.setEntity(new ByteArrayEntity(bytes2, null));
1476
1477 final Date inTwoSeconds = new Date(now.getTime() + 2000L);
1478 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
1479 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
1480 resp3.setHeader("Date", DateUtils.formatDate(inTwoSeconds));
1481 resp3.setHeader("Cache-Control", "max-age=3600");
1482 resp3.setHeader("ETag", "\"etag2\"");
1483 final byte[] bytes3 = new byte[128];
1484 Arrays.fill(bytes3, (byte) 2);
1485 resp3.setEntity(new ByteArrayEntity(bytes3, null));
1486
1487 EasyMock.expect(
1488 mockExecChain.proceed(
1489 EasyMock.isA(ClassicHttpRequest.class),
1490 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
1491 EasyMock.expect(
1492 mockExecChain.proceed(
1493 EasyMock.isA(ClassicHttpRequest.class),
1494 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
1495 EasyMock.expect(
1496 mockExecChain.proceed(
1497 EasyMock.isA(ClassicHttpRequest.class),
1498 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3).times(0, 1);
1499 replayMocks();
1500
1501 execute(req1);
1502 execute(req2);
1503 final ClassicHttpResponse result = execute(req3);
1504
1505 verifyMocks();
1506
1507 final InputStream i = result.getEntity().getContent();
1508 int b;
1509 boolean found1 = false;
1510 boolean found2 = false;
1511 while ((b = i.read()) != -1) {
1512 if (b == 1) {
1513 found1 = true;
1514 }
1515 if (b == 2) {
1516 found2 = true;
1517 }
1518 }
1519 i.close();
1520 Assert.assertFalse(found1 && found2);
1521 }
1522
1523
1524
1525
1526
1527
1528
1529 @Test
1530 public void test206ResponsesAreNotCachedIfTheCacheDoesNotSupportRangeAndContentRangeHeaders() throws Exception {
1531
1532 if (!supportsRangeAndContentRangeHeaders(impl)) {
1533 emptyMockCacheExpectsNoPuts();
1534
1535 request = new BasicClassicHttpRequest("GET", "/");
1536 request.addHeader("Range", "bytes=0-50");
1537
1538 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT,"Partial Content");
1539 originResponse.setHeader("Content-Range", "bytes 0-50/128");
1540 originResponse.setHeader("Cache-Control", "max-age=3600");
1541 final byte[] bytes = new byte[51];
1542 new Random().nextBytes(bytes);
1543 originResponse.setEntity(new ByteArrayEntity(bytes, null));
1544
1545 EasyMock.expect(
1546 mockExecChain.proceed(
1547 EasyMock.isA(ClassicHttpRequest.class),
1548 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1549
1550 replayMocks();
1551 execute(request);
1552 verifyMocks();
1553 }
1554 }
1555
1556
1557
1558
1559
1560
1561
1562 @Test
1563 public void test303ResponsesAreNotCached() throws Exception {
1564 emptyMockCacheExpectsNoPuts();
1565
1566 request = new BasicClassicHttpRequest("GET", "/");
1567
1568 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_SEE_OTHER, "See Other");
1569 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
1570 originResponse.setHeader("Server", "MockServer/1.0");
1571 originResponse.setHeader("Cache-Control", "max-age=3600");
1572 originResponse.setHeader("Content-Type", "application/x-cachingclient-test");
1573 originResponse.setHeader("Location", "http://foo.example.com/other");
1574 originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
1575
1576 EasyMock.expect(
1577 mockExecChain.proceed(
1578 EasyMock.isA(ClassicHttpRequest.class),
1579 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1580
1581 replayMocks();
1582 execute(request);
1583 verifyMocks();
1584 }
1585
1586
1587
1588
1589
1590
1591
1592 @Test
1593 public void test304ResponseDoesNotContainABody() throws Exception {
1594 request.setHeader("If-None-Match", "\"etag\"");
1595
1596 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,"Not Modified");
1597 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
1598 originResponse.setHeader("Server", "MockServer/1.0");
1599 originResponse.setHeader("Content-Length", "128");
1600 originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
1601
1602 EasyMock.expect(
1603 mockExecChain.proceed(
1604 EasyMock.isA(ClassicHttpRequest.class),
1605 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1606
1607 replayMocks();
1608
1609 final ClassicHttpResponse result = execute(request);
1610
1611 verifyMocks();
1612 }
1613
1614
1615
1616
1617
1618
1619
1620
1621 @Test
1622 public void test304ResponseWithDateHeaderForwardedFromOriginIncludesDateHeader() throws Exception {
1623
1624 request.setHeader("If-None-Match", "\"etag\"");
1625
1626 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,"Not Modified");
1627 originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
1628 originResponse.setHeader("Server", "MockServer/1.0");
1629 originResponse.setHeader("ETag", "\"etag\"");
1630
1631 EasyMock.expect(
1632 mockExecChain.proceed(
1633 EasyMock.isA(ClassicHttpRequest.class),
1634 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1635 replayMocks();
1636
1637 final ClassicHttpResponse result = execute(request);
1638
1639 verifyMocks();
1640 Assert.assertNotNull(result.getFirstHeader("Date"));
1641 }
1642
1643 @Test
1644 public void test304ResponseGeneratedFromCacheIncludesDateHeader() throws Exception {
1645
1646 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1647 originResponse.setHeader("Cache-Control", "max-age=3600");
1648 originResponse.setHeader("ETag", "\"etag\"");
1649
1650 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1651 req2.setHeader("If-None-Match", "\"etag\"");
1652
1653 EasyMock.expect(
1654 mockExecChain.proceed(
1655 EasyMock.isA(ClassicHttpRequest.class),
1656 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
1657 replayMocks();
1658
1659 execute(req1);
1660 final ClassicHttpResponse result = execute(req2);
1661
1662 verifyMocks();
1663 if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
1664 Assert.assertNotNull(result.getFirstHeader("Date"));
1665 }
1666 }
1667
1668
1669
1670
1671
1672
1673
1674
1675 @Test
1676 public void test304ResponseGeneratedFromCacheIncludesEtagIfOriginResponseDid() throws Exception {
1677 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1678 originResponse.setHeader("Cache-Control", "max-age=3600");
1679 originResponse.setHeader("ETag", "\"etag\"");
1680
1681 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1682 req2.setHeader("If-None-Match", "\"etag\"");
1683
1684 EasyMock.expect(
1685 mockExecChain.proceed(
1686 EasyMock.isA(ClassicHttpRequest.class),
1687 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
1688 replayMocks();
1689
1690 execute(req1);
1691 final ClassicHttpResponse result = execute(req2);
1692
1693 verifyMocks();
1694 if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
1695 Assert.assertNotNull(result.getFirstHeader("ETag"));
1696 }
1697 }
1698
1699 @Test
1700 public void test304ResponseGeneratedFromCacheIncludesContentLocationIfOriginResponseDid() throws Exception {
1701 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1702 originResponse.setHeader("Cache-Control", "max-age=3600");
1703 originResponse.setHeader("Content-Location", "http://foo.example.com/other");
1704 originResponse.setHeader("ETag", "\"etag\"");
1705
1706 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1707 req2.setHeader("If-None-Match", "\"etag\"");
1708
1709 EasyMock.expect(
1710 mockExecChain.proceed(
1711 EasyMock.isA(ClassicHttpRequest.class),
1712 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
1713 replayMocks();
1714
1715 execute(req1);
1716 final ClassicHttpResponse result = execute(req2);
1717
1718 verifyMocks();
1719 if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
1720 Assert.assertNotNull(result.getFirstHeader("Content-Location"));
1721 }
1722 }
1723
1724
1725
1726
1727
1728
1729
1730
1731 @Test
1732 public void test304ResponseGeneratedFromCacheIncludesExpiresCacheControlAndOrVaryIfResponseMightDiffer() throws Exception {
1733
1734 final Date now = new Date();
1735 final Date inTwoHours = new Date(now.getTime() + 2 * 3600 * 1000L);
1736
1737 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1738 req1.setHeader("Accept-Encoding", "gzip");
1739
1740 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1741 resp1.setHeader("ETag", "\"v1\"");
1742 resp1.setHeader("Cache-Control", "max-age=7200");
1743 resp1.setHeader("Expires", DateUtils.formatDate(inTwoHours));
1744 resp1.setHeader("Vary", "Accept-Encoding");
1745 resp1.setEntity(HttpTestUtils.makeBody(entityLength));
1746
1747 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1748 req1.setHeader("Accept-Encoding", "gzip");
1749 req1.setHeader("Cache-Control", "no-cache");
1750
1751 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
1752 resp2.setHeader("ETag", "\"v2\"");
1753 resp2.setHeader("Cache-Control", "max-age=3600");
1754 resp2.setHeader("Expires", DateUtils.formatDate(inTwoHours));
1755 resp2.setHeader("Vary", "Accept-Encoding");
1756 resp2.setEntity(HttpTestUtils.makeBody(entityLength));
1757
1758 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
1759 req3.setHeader("Accept-Encoding", "gzip");
1760 req3.setHeader("If-None-Match", "\"v2\"");
1761
1762 EasyMock.expect(
1763 mockExecChain.proceed(
1764 EasyMock.isA(ClassicHttpRequest.class),
1765 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
1766 EasyMock.expect(
1767 mockExecChain.proceed(
1768 EasyMock.isA(ClassicHttpRequest.class),
1769 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(1, 2);
1770 replayMocks();
1771
1772 execute(req1);
1773 execute(req2);
1774 final ClassicHttpResponse result = execute(req3);
1775
1776 verifyMocks();
1777
1778 if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
1779 Assert.assertNotNull(result.getFirstHeader("Expires"));
1780 Assert.assertNotNull(result.getFirstHeader("Cache-Control"));
1781 Assert.assertNotNull(result.getFirstHeader("Vary"));
1782 }
1783 }
1784
1785
1786
1787
1788
1789
1790
1791
1792 @Test
1793 public void test304GeneratedFromCacheOnWeakValidatorDoesNotIncludeOtherEntityHeaders() throws Exception {
1794
1795 final Date now = new Date();
1796 final Date oneHourAgo = new Date(now.getTime() - 3600 * 1000L);
1797
1798 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1799
1800 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1801 resp1.setHeader("ETag", "W/\"v1\"");
1802 resp1.setHeader("Allow", "GET,HEAD");
1803 resp1.setHeader("Content-Encoding", "x-coding");
1804 resp1.setHeader("Content-Language", "en");
1805 resp1.setHeader("Content-Length", "128");
1806 resp1.setHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
1807 resp1.setHeader("Content-Type", "application/octet-stream");
1808 resp1.setHeader("Last-Modified", DateUtils.formatDate(oneHourAgo));
1809 resp1.setHeader("Cache-Control", "max-age=7200");
1810
1811 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1812 req2.setHeader("If-None-Match", "W/\"v1\"");
1813
1814 EasyMock.expect(
1815 mockExecChain.proceed(
1816 EasyMock.isA(ClassicHttpRequest.class),
1817 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1, 2);
1818 replayMocks();
1819
1820 execute(req1);
1821 final ClassicHttpResponse result = execute(req2);
1822
1823 verifyMocks();
1824
1825 if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
1826 Assert.assertNull(result.getFirstHeader("Allow"));
1827 Assert.assertNull(result.getFirstHeader("Content-Encoding"));
1828 Assert.assertNull(result.getFirstHeader("Content-Length"));
1829 Assert.assertNull(result.getFirstHeader("Content-MD5"));
1830 Assert.assertNull(result.getFirstHeader("Content-Type"));
1831 Assert.assertNull(result.getFirstHeader("Last-Modified"));
1832 }
1833 }
1834
1835
1836
1837
1838
1839
1840
1841
1842 @Test
1843 public void testNotModifiedOfNonCachedEntityShouldRevalidateWithUnconditionalGET() throws Exception {
1844
1845 final Date now = new Date();
1846
1847
1848 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
1849 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
1850 resp1.setHeader("ETag", "\"etag1\"");
1851 resp1.setHeader("Cache-Control", "max-age=3600");
1852
1853
1854 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
1855 req2.setHeader("Cache-Control", "max-age=0,max-stale=0");
1856
1857
1858 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,
1859 "Not Modified");
1860 resp2.setHeader("Date", DateUtils.formatDate(now));
1861 resp2.setHeader("Server", "MockServer/1.0");
1862 resp2.setHeader("ETag", "\"etag2\"");
1863
1864
1865 final ClassicHttpRequest conditionalValidation = new BasicClassicHttpRequest("GET", "/");
1866 conditionalValidation.setHeader("If-None-Match", "\"etag1\"");
1867
1868
1869 final ClassicHttpRequest unconditionalValidation = new BasicClassicHttpRequest("GET", "/");
1870
1871 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
1872 resp1.setHeader("ETag", "\"etag2\"");
1873 resp1.setHeader("Cache-Control", "max-age=3600");
1874
1875 EasyMock.expect(
1876 mockExecChain.proceed(
1877 EasyMock.isA(ClassicHttpRequest.class),
1878 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
1879
1880
1881 EasyMock.expect(
1882 mockExecChain.proceed(
1883 eqRequest(conditionalValidation),
1884 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0, 1);
1885 EasyMock.expect(
1886 mockExecChain.proceed(
1887 eqRequest(unconditionalValidation),
1888 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
1889 replayMocks();
1890
1891 execute(req1);
1892 execute(req2);
1893
1894 verifyMocks();
1895 }
1896
1897
1898
1899
1900
1901
1902
1903
1904 @Test
1905 public void testCacheEntryIsUpdatedWithNewFieldValuesIn304Response() throws Exception {
1906
1907 final Date now = new Date();
1908 final Date inFiveSeconds = new Date(now.getTime() + 5000L);
1909
1910 final ClassicHttpRequest initialRequest = new BasicClassicHttpRequest("GET", "/");
1911
1912 final ClassicHttpResponse cachedResponse = HttpTestUtils.make200Response();
1913 cachedResponse.setHeader("Cache-Control", "max-age=3600");
1914 cachedResponse.setHeader("ETag", "\"etag\"");
1915
1916 final ClassicHttpRequest secondRequest = new BasicClassicHttpRequest("GET", "/");
1917 secondRequest.setHeader("Cache-Control", "max-age=0,max-stale=0");
1918
1919 final ClassicHttpRequest conditionalValidationRequest = new BasicClassicHttpRequest("GET", "/");
1920 conditionalValidationRequest.setHeader("If-None-Match", "\"etag\"");
1921
1922 final ClassicHttpRequest unconditionalValidationRequest = new BasicClassicHttpRequest("GET", "/");
1923
1924
1925 final ClassicHttpResponse conditionalResponse = HttpTestUtils.make304Response();
1926 conditionalResponse.setHeader("Date", DateUtils.formatDate(inFiveSeconds));
1927 conditionalResponse.setHeader("Server", "MockUtils/1.0");
1928 conditionalResponse.setHeader("ETag", "\"etag\"");
1929 conditionalResponse.setHeader("X-Extra", "junk");
1930
1931
1932 final ClassicHttpResponse unconditionalResponse = HttpTestUtils.make200Response();
1933 unconditionalResponse.setHeader("Date", DateUtils.formatDate(inFiveSeconds));
1934 unconditionalResponse.setHeader("ETag", "\"etag\"");
1935
1936 final Capture<ClassicHttpRequest> cap1 = EasyMock.newCapture();
1937 final Capture<ClassicHttpRequest> cap2 = EasyMock.newCapture();
1938
1939 EasyMock.expect(
1940 mockExecChain.proceed(
1941 EasyMock.isA(ClassicHttpRequest.class),
1942 EasyMock.isA(ExecChain.Scope.class))).andReturn(cachedResponse);
1943 EasyMock.expect(
1944 mockExecChain.proceed(
1945 EasyMock.and(eqRequest(conditionalValidationRequest), EasyMock.capture(cap1)),
1946 EasyMock.isA(ExecChain.Scope.class))).andReturn(conditionalResponse).times(0, 1);
1947 EasyMock.expect(
1948 mockExecChain.proceed(
1949 EasyMock.and(eqRequest(unconditionalValidationRequest), EasyMock.capture(cap2)),
1950 EasyMock.isA(ExecChain.Scope.class))).andReturn(unconditionalResponse).times(0, 1);
1951
1952 replayMocks();
1953
1954 execute(initialRequest);
1955 final ClassicHttpResponse result = execute(secondRequest);
1956
1957 verifyMocks();
1958
1959 Assert.assertTrue((cap1.hasCaptured() && !cap2.hasCaptured())
1960 || (!cap1.hasCaptured() && cap2.hasCaptured()));
1961
1962 if (cap1.hasCaptured()) {
1963 Assert.assertEquals(DateUtils.formatDate(inFiveSeconds), result.getFirstHeader("Date")
1964 .getValue());
1965 Assert.assertEquals("junk", result.getFirstHeader("X-Extra").getValue());
1966 }
1967 }
1968
1969
1970
1971
1972
1973
1974
1975
1976 @Test
1977 public void testMustIncludeWWWAuthenticateHeaderOnAnOrigin401Response() throws Exception {
1978 originResponse = new BasicClassicHttpResponse(401, "Unauthorized");
1979 originResponse.setHeader("WWW-Authenticate", "x-scheme x-param");
1980
1981 EasyMock.expect(
1982 mockExecChain.proceed(
1983 EasyMock.isA(ClassicHttpRequest.class),
1984 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
1985 replayMocks();
1986
1987 final ClassicHttpResponse result = execute(request);
1988 if (result.getCode() == 401) {
1989 Assert.assertNotNull(result.getFirstHeader("WWW-Authenticate"));
1990 }
1991
1992 verifyMocks();
1993 }
1994
1995
1996
1997
1998
1999
2000
2001 @Test
2002 public void testMustIncludeAllowHeaderFromAnOrigin405Response() throws Exception {
2003 originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
2004 originResponse.setHeader("Allow", "GET, HEAD");
2005
2006 backendExpectsAnyRequest().andReturn(originResponse);
2007
2008 replayMocks();
2009
2010 final ClassicHttpResponse result = execute(request);
2011 if (result.getCode() == 405) {
2012 Assert.assertNotNull(result.getFirstHeader("Allow"));
2013 }
2014
2015 verifyMocks();
2016 }
2017
2018
2019
2020
2021
2022
2023
2024
2025 @Test
2026 public void testMustIncludeProxyAuthenticateHeaderFromAnOrigin407Response() throws Exception {
2027 originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required");
2028 originResponse.setHeader("Proxy-Authenticate", "x-scheme x-param");
2029
2030 EasyMock.expect(
2031 mockExecChain.proceed(
2032 EasyMock.isA(ClassicHttpRequest.class),
2033 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
2034 replayMocks();
2035
2036 final ClassicHttpResponse result = execute(request);
2037 if (result.getCode() == 407) {
2038 Assert.assertNotNull(result.getFirstHeader("Proxy-Authenticate"));
2039 }
2040
2041 verifyMocks();
2042 }
2043
2044
2045
2046
2047
2048
2049
2050 @Test
2051 public void testMustNotAddMultipartByteRangeContentTypeTo416Response() throws Exception {
2052 originResponse = new BasicClassicHttpResponse(416, "Requested Range Not Satisfiable");
2053
2054 EasyMock.expect(
2055 mockExecChain.proceed(
2056 EasyMock.isA(ClassicHttpRequest.class),
2057 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
2058
2059 replayMocks();
2060 final ClassicHttpResponse result = execute(request);
2061 verifyMocks();
2062
2063 if (result.getCode() == 416) {
2064 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
2065 while (it.hasNext()) {
2066 final HeaderElement elt = it.next();
2067 Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
2068 }
2069 }
2070 }
2071
2072 @Test
2073 public void testMustNotUseMultipartByteRangeContentTypeOnCacheGenerated416Responses() throws Exception {
2074
2075 originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
2076 originResponse.setHeader("Content-Length", "128");
2077 originResponse.setHeader("Cache-Control", "max-age=3600");
2078
2079 final ClassicHttpRequest rangeReq = new BasicClassicHttpRequest("GET", "/");
2080 rangeReq.setHeader("Range", "bytes=1000-1200");
2081
2082 final ClassicHttpResponse orig416 = new BasicClassicHttpResponse(416,
2083 "Requested Range Not Satisfiable");
2084
2085 EasyMock.expect(
2086 mockExecChain.proceed(
2087 EasyMock.isA(ClassicHttpRequest.class),
2088 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
2089
2090
2091 EasyMock.expect(
2092 mockExecChain.proceed(
2093 EasyMock.isA(ClassicHttpRequest.class),
2094 EasyMock.isA(ExecChain.Scope.class))).andReturn(orig416).times(0, 1);
2095
2096 replayMocks();
2097 execute(request);
2098 final ClassicHttpResponse result = execute(rangeReq);
2099 verifyMocks();
2100
2101
2102 if (result.getCode() == 416) {
2103 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
2104 while (it.hasNext()) {
2105 final HeaderElement elt = it.next();
2106 Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
2107 }
2108 }
2109 }
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139 @Test
2140 public void testMustReturnACacheEntryIfItCanRevalidateIt() throws Exception {
2141
2142 final Date now = new Date();
2143 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2144 final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
2145 final Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
2146
2147 final Header[] hdrs = new Header[] {
2148 new BasicHeader("Date", DateUtils.formatDate(nineSecondsAgo)),
2149 new BasicHeader("Cache-Control", "max-age=0"),
2150 new BasicHeader("ETag", "\"etag\""),
2151 new BasicHeader("Content-Length", "128")
2152 };
2153
2154 final byte[] bytes = new byte[128];
2155 new Random().nextBytes(bytes);
2156
2157 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
2158
2159 impl = new CachingExec(mockCache, null, config);
2160
2161 request = new BasicClassicHttpRequest("GET", "/thing");
2162
2163 final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/thing");
2164 validate.setHeader("If-None-Match", "\"etag\"");
2165
2166 final ClassicHttpResponse notModified = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
2167 notModified.setHeader("Date", DateUtils.formatDate(now));
2168 notModified.setHeader("ETag", "\"etag\"");
2169
2170 EasyMock.expect(
2171 mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request)))
2172 .andReturn(entry);
2173 EasyMock.expect(
2174 mockExecChain.proceed(
2175 eqRequest(validate),
2176 EasyMock.isA(ExecChain.Scope.class))).andReturn(notModified);
2177 EasyMock.expect(mockCache.updateCacheEntry(
2178 EasyMock.eq(host),
2179 eqRequest(request),
2180 EasyMock.eq(entry),
2181 eqResponse(notModified),
2182 EasyMock.isA(Date.class),
2183 EasyMock.isA(Date.class)))
2184 .andReturn(HttpTestUtils.makeCacheEntry());
2185
2186 replayMocks();
2187 execute(request);
2188 verifyMocks();
2189 }
2190
2191 @Test
2192 public void testMustReturnAFreshEnoughCacheEntryIfItHasIt() throws Exception {
2193
2194 final Date now = new Date();
2195 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2196 final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
2197 final Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
2198
2199 final Header[] hdrs = new Header[] {
2200 new BasicHeader("Date", DateUtils.formatDate(nineSecondsAgo)),
2201 new BasicHeader("Cache-Control", "max-age=3600"),
2202 new BasicHeader("Content-Length", "128")
2203 };
2204
2205 final byte[] bytes = new byte[128];
2206 new Random().nextBytes(bytes);
2207
2208 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
2209
2210 impl = new CachingExec(mockCache, null, config);
2211 request = new BasicClassicHttpRequest("GET", "/thing");
2212
2213 EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
2214
2215 replayMocks();
2216 final ClassicHttpResponse result = execute(request);
2217 verifyMocks();
2218
2219 Assert.assertEquals(200, result.getCode());
2220 }
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236 @Test
2237 public void testMustServeAppropriateErrorOrWarningIfNoOriginCommunicationPossible() throws Exception {
2238
2239 final Date now = new Date();
2240 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2241 final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
2242 final Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
2243
2244 final Header[] hdrs = new Header[] {
2245 new BasicHeader("Date", DateUtils.formatDate(nineSecondsAgo)),
2246 new BasicHeader("Cache-Control", "max-age=0"),
2247 new BasicHeader("Content-Length", "128"),
2248 new BasicHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo))
2249 };
2250
2251 final byte[] bytes = new byte[128];
2252 new Random().nextBytes(bytes);
2253
2254 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
2255
2256 impl = new CachingExec(mockCache, null, config);
2257 request = new BasicClassicHttpRequest("GET", "/thing");
2258
2259 EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
2260 EasyMock.expect(
2261 mockExecChain.proceed(
2262 EasyMock.isA(ClassicHttpRequest.class),
2263 EasyMock.isA(ExecChain.Scope.class))).andThrow(
2264 new IOException("can't talk to origin!")).anyTimes();
2265
2266 replayMocks();
2267
2268 final ClassicHttpResponse result = execute(request);
2269
2270 verifyMocks();
2271
2272 final int status = result.getCode();
2273 if (status == 200) {
2274 boolean foundWarning = false;
2275 for (final Header h : result.getHeaders("Warning")) {
2276 if (h.getValue().split(" ")[0].equals("111")) {
2277 foundWarning = true;
2278 }
2279 }
2280 Assert.assertTrue(foundWarning);
2281 } else {
2282 Assert.assertTrue(status >= 500 && status <= 599);
2283 }
2284 }
2285
2286
2287
2288
2289
2290
2291
2292
2293 @Test
2294 public void testAttachesWarningHeaderWhenGeneratingStaleResponse() throws Exception {
2295
2296 }
2297
2298
2299
2300
2301
2302
2303
2304 @Test
2305 public void test1xxWarningsAreDeletedAfterSuccessfulRevalidation() throws Exception {
2306
2307 final Date now = new Date();
2308 final Date tenSecondsAgo = new Date(now.getTime() - 25 * 1000L);
2309 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2310 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2311 resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
2312 resp1.setHeader("ETag", "\"etag\"");
2313 resp1.setHeader("Cache-Control", "max-age=5");
2314 resp1.setHeader("Warning", "110 squid \"stale stuff\"");
2315 resp1.setHeader("Via", "1.1 fred");
2316
2317 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2318
2319 final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
2320 validate.setHeader("If-None-Match", "\"etag\"");
2321
2322 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,
2323 "Not Modified");
2324 resp2.setHeader("Date", DateUtils.formatDate(now));
2325 resp2.setHeader("Server", "MockServer/1.0");
2326 resp2.setHeader("ETag", "\"etag\"");
2327 resp2.setHeader("Via", "1.1 fred");
2328
2329 backendExpectsAnyRequestAndReturn(resp1);
2330 EasyMock.expect(
2331 mockExecChain.proceed(
2332 eqRequest(validate),
2333 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
2334
2335 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
2336
2337 replayMocks();
2338
2339 final ClassicHttpResponse stale = execute(req1);
2340 Assert.assertNotNull(stale.getFirstHeader("Warning"));
2341
2342 final ClassicHttpResponse result1 = execute(req2);
2343 final ClassicHttpResponse result2 = execute(req3);
2344
2345 verifyMocks();
2346
2347 boolean found1xxWarning = false;
2348 final Iterator<HeaderElement> it = MessageSupport.iterate(result1, HttpHeaders.WARNING);
2349 while (it.hasNext()) {
2350 final HeaderElement elt = it.next();
2351 if (elt.getName().startsWith("1")) {
2352 found1xxWarning = true;
2353 }
2354 }
2355 final Iterator<HeaderElement> it2 = MessageSupport.iterate(result2, HttpHeaders.WARNING);
2356 while (it2.hasNext()) {
2357 final HeaderElement elt = it2.next();
2358 if (elt.getName().startsWith("1")) {
2359 found1xxWarning = true;
2360 }
2361 }
2362 Assert.assertFalse(found1xxWarning);
2363 }
2364
2365
2366
2367
2368
2369
2370
2371
2372 @Test
2373 public void test2xxWarningsAreNotDeletedAfterSuccessfulRevalidation() throws Exception {
2374 final Date now = new Date();
2375 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2376 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2377 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2378 resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
2379 resp1.setHeader("ETag", "\"etag\"");
2380 resp1.setHeader("Cache-Control", "max-age=5");
2381 resp1.setHeader("Via", "1.1 xproxy");
2382 resp1.setHeader("Warning", "214 xproxy \"transformed stuff\"");
2383
2384 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2385
2386 final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
2387 validate.setHeader("If-None-Match", "\"etag\"");
2388
2389 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,
2390 "Not Modified");
2391 resp2.setHeader("Date", DateUtils.formatDate(now));
2392 resp2.setHeader("Server", "MockServer/1.0");
2393 resp2.setHeader("ETag", "\"etag\"");
2394 resp1.setHeader("Via", "1.1 xproxy");
2395
2396 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
2397
2398 backendExpectsAnyRequestAndReturn(resp1);
2399
2400 EasyMock.expect(
2401 mockExecChain.proceed(
2402 eqRequest(validate),
2403 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
2404
2405 replayMocks();
2406
2407 final ClassicHttpResponse stale = execute(req1);
2408 Assert.assertNotNull(stale.getFirstHeader("Warning"));
2409
2410 final ClassicHttpResponse result1 = execute(req2);
2411 final ClassicHttpResponse result2 = execute(req3);
2412
2413 verifyMocks();
2414
2415 boolean found214Warning = false;
2416 final Iterator<HeaderElement> it = MessageSupport.iterate(result1, HttpHeaders.WARNING);
2417 while (it.hasNext()) {
2418 final HeaderElement elt = it.next();
2419 final String[] parts = elt.getName().split(" ");
2420 if ("214".equals(parts[0])) {
2421 found214Warning = true;
2422 }
2423 }
2424 Assert.assertTrue(found214Warning);
2425
2426 found214Warning = false;
2427 final Iterator<HeaderElement> it2 = MessageSupport.iterate(result2, HttpHeaders.WARNING);
2428 while (it2.hasNext()) {
2429 final HeaderElement elt = it2.next();
2430 final String[] parts = elt.getName().split(" ");
2431 if ("214".equals(parts[0])) {
2432 found214Warning = true;
2433 }
2434 }
2435 Assert.assertTrue(found214Warning);
2436 }
2437
2438
2439
2440
2441
2442
2443
2444
2445 @Test
2446 public void testAgeHeaderPopulatedFromCacheEntryCurrentAge() throws Exception {
2447
2448 final Date now = new Date();
2449 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2450 final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
2451 final Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
2452
2453 final Header[] hdrs = new Header[] {
2454 new BasicHeader("Date", DateUtils.formatDate(nineSecondsAgo)),
2455 new BasicHeader("Cache-Control", "max-age=3600"),
2456 new BasicHeader("Content-Length", "128")
2457 };
2458
2459 final byte[] bytes = new byte[128];
2460 new Random().nextBytes(bytes);
2461
2462 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
2463
2464 impl = new CachingExec(mockCache, null, config);
2465 request = new BasicClassicHttpRequest("GET", "/thing");
2466
2467 EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
2468
2469 replayMocks();
2470 final ClassicHttpResponse result = execute(request);
2471 verifyMocks();
2472
2473 Assert.assertEquals(200, result.getCode());
2474 Assert.assertEquals("11", result.getFirstHeader("Age").getValue());
2475 }
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493 @Test
2494 public void testHeuristicCacheOlderThan24HoursHasWarningAttached() throws Exception {
2495
2496 final Date now = new Date();
2497 final Date thirtySixHoursAgo = new Date(now.getTime() - 36 * 3600 * 1000L);
2498 final Date oneYearAgo = new Date(now.getTime() - 365 * 24 * 3600 * 1000L);
2499 final Date requestTime = new Date(thirtySixHoursAgo.getTime() - 1000L);
2500 final Date responseTime = new Date(thirtySixHoursAgo.getTime() + 1000L);
2501
2502 final Header[] hdrs = new Header[] {
2503 new BasicHeader("Date", DateUtils.formatDate(thirtySixHoursAgo)),
2504 new BasicHeader("Cache-Control", "public"),
2505 new BasicHeader("Last-Modified", DateUtils.formatDate(oneYearAgo)),
2506 new BasicHeader("Content-Length", "128")
2507 };
2508
2509 final byte[] bytes = new byte[128];
2510 new Random().nextBytes(bytes);
2511
2512 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes);
2513
2514 impl = new CachingExec(mockCache, null, config);
2515
2516 request = new BasicClassicHttpRequest("GET", "/thing");
2517
2518 final ClassicHttpResponse validated = HttpTestUtils.make200Response();
2519 validated.setHeader("Cache-Control", "public");
2520 validated.setHeader("Last-Modified", DateUtils.formatDate(oneYearAgo));
2521 validated.setHeader("Content-Length", "128");
2522 validated.setEntity(new ByteArrayEntity(bytes, null));
2523
2524 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry();
2525
2526 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
2527
2528 mockCache.flushCacheEntriesInvalidatedByExchange(
2529 EasyMock.isA(HttpHost.class),
2530 EasyMock.isA(ClassicHttpRequest.class),
2531 EasyMock.isA(ClassicHttpResponse.class));
2532 EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
2533 EasyMock.expect(
2534 mockExecChain.proceed(
2535 EasyMock.capture(cap),
2536 EasyMock.isA(ExecChain.Scope.class))).andReturn(validated).times(0, 1);
2537 EasyMock.expect(mockCache.getCacheEntry(
2538 EasyMock.isA(HttpHost.class),
2539 EasyMock.isA(ClassicHttpRequest.class))).andReturn(entry).times(0, 1);
2540 EasyMock.expect(mockCache.createCacheEntry(
2541 EasyMock.isA(HttpHost.class),
2542 EasyMock.isA(ClassicHttpRequest.class),
2543 eqCloseableResponse(validated),
2544 EasyMock.isA(ByteArrayBuffer.class),
2545 EasyMock.isA(Date.class),
2546 EasyMock.isA(Date.class))).andReturn(cacheEntry).times(0, 1);
2547
2548 replayMocks();
2549 final ClassicHttpResponse result = execute(request);
2550 verifyMocks();
2551
2552 Assert.assertEquals(200, result.getCode());
2553 if (!cap.hasCaptured()) {
2554
2555 boolean found113Warning = false;
2556 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.WARNING);
2557 while (it.hasNext()) {
2558 final HeaderElement elt = it.next();
2559 final String[] parts = elt.getName().split(" ");
2560 if ("113".equals(parts[0])) {
2561 found113Warning = true;
2562 break;
2563 }
2564 }
2565 Assert.assertTrue(found113Warning);
2566 }
2567 }
2568
2569
2570
2571
2572
2573
2574
2575
2576 @Test
2577 public void testKeepsMostRecentDateHeaderForFreshResponse() throws Exception {
2578
2579 final Date now = new Date();
2580 final Date inFiveSecond = new Date(now.getTime() + 5 * 1000L);
2581
2582
2583 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2584
2585 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2586 resp1.setHeader("Date", DateUtils.formatDate(inFiveSecond));
2587 resp1.setHeader("ETag", "\"etag1\"");
2588 resp1.setHeader("Cache-Control", "max-age=3600");
2589 resp1.setHeader("Content-Length", "128");
2590
2591
2592 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2593 req2.setHeader("Cache-Control", "no-cache");
2594
2595 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
2596 resp2.setHeader("Date", DateUtils.formatDate(now));
2597 resp2.setHeader("ETag", "\"etag2\"");
2598 resp2.setHeader("Cache-Control", "max-age=3600");
2599 resp2.setHeader("Content-Length", "128");
2600
2601 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
2602
2603 EasyMock.expect(
2604 mockExecChain.proceed(
2605 eqRequest(req1),
2606 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
2607
2608 backendExpectsAnyRequestAndReturn(resp2);
2609
2610 replayMocks();
2611 execute(req1);
2612 execute(req2);
2613 final ClassicHttpResponse result = execute(req3);
2614 verifyMocks();
2615 Assert.assertEquals("\"etag1\"", result.getFirstHeader("ETag").getValue());
2616 }
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632 private ClassicHttpResponse testRequestWithWeakETagValidatorIsNotAllowed(final String header) throws Exception {
2633 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
2634 EasyMock.expect(
2635 mockExecChain.proceed(
2636 EasyMock.capture(cap),
2637 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(0, 1);
2638
2639 replayMocks();
2640 final ClassicHttpResponse response = execute(request);
2641 verifyMocks();
2642
2643
2644 if (cap.hasCaptured()) {
2645 final ClassicHttpRequest forwarded = cap.getValue();
2646 final Header h = forwarded.getFirstHeader(header);
2647 if (h != null) {
2648 Assert.assertFalse(h.getValue().startsWith("W/"));
2649 }
2650 }
2651 return response;
2652
2653 }
2654
2655 @Test
2656 public void testSubrangeGETWithWeakETagIsNotAllowed() throws Exception {
2657 request = new BasicClassicHttpRequest("GET", "/");
2658 request.setHeader("Range", "bytes=0-500");
2659 request.setHeader("If-Range", "W/\"etag\"");
2660
2661 final ClassicHttpResponse response = testRequestWithWeakETagValidatorIsNotAllowed("If-Range");
2662 Assert.assertTrue(response.getCode() == HttpStatus.SC_BAD_REQUEST);
2663 }
2664
2665 @Test
2666 public void testPUTWithIfMatchWeakETagIsNotAllowed() throws Exception {
2667 final ClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/");
2668 put.setEntity(HttpTestUtils.makeBody(128));
2669 put.setHeader("Content-Length", "128");
2670 put.setHeader("If-Match", "W/\"etag\"");
2671 request = put;
2672
2673 testRequestWithWeakETagValidatorIsNotAllowed("If-Match");
2674 }
2675
2676 @Test
2677 public void testPUTWithIfNoneMatchWeakETagIsNotAllowed() throws Exception {
2678 final ClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/");
2679 put.setEntity(HttpTestUtils.makeBody(128));
2680 put.setHeader("Content-Length", "128");
2681 put.setHeader("If-None-Match", "W/\"etag\"");
2682 request = put;
2683
2684 testRequestWithWeakETagValidatorIsNotAllowed("If-None-Match");
2685 }
2686
2687 @Test
2688 public void testDELETEWithIfMatchWeakETagIsNotAllowed() throws Exception {
2689 request = new BasicClassicHttpRequest("DELETE", "/");
2690 request.setHeader("If-Match", "W/\"etag\"");
2691
2692 testRequestWithWeakETagValidatorIsNotAllowed("If-Match");
2693 }
2694
2695 @Test
2696 public void testDELETEWithIfNoneMatchWeakETagIsNotAllowed() throws Exception {
2697 request = new BasicClassicHttpRequest("DELETE", "/");
2698 request.setHeader("If-None-Match", "W/\"etag\"");
2699
2700 testRequestWithWeakETagValidatorIsNotAllowed("If-None-Match");
2701 }
2702
2703
2704
2705
2706
2707
2708
2709
2710 @Test
2711 public void testSubrangeGETMustUseStrongComparisonForCachedResponse() throws Exception {
2712 final Date now = new Date();
2713 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2714 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2715 resp1.setHeader("Date", DateUtils.formatDate(now));
2716 resp1.setHeader("Cache-Control", "max-age=3600");
2717 resp1.setHeader("ETag", "\"etag\"");
2718
2719
2720
2721
2722
2723
2724 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2725 req2.setHeader("Range", "bytes=0-50");
2726 req2.setHeader("If-Range", "W/\"etag\"");
2727
2728 EasyMock.expect(
2729 mockExecChain.proceed(
2730 EasyMock.isA(ClassicHttpRequest.class),
2731 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1, 2);
2732
2733 replayMocks();
2734 execute(req1);
2735 final ClassicHttpResponse result = execute(req2);
2736 verifyMocks();
2737
2738 Assert.assertFalse(HttpStatus.SC_PARTIAL_CONTENT == result.getCode());
2739 }
2740
2741
2742
2743
2744
2745
2746
2747
2748 @Test
2749 public void testValidationMustUseETagIfProvidedByOriginServer() throws Exception {
2750
2751 final Date now = new Date();
2752 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2753
2754 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2755 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2756 resp1.setHeader("Date", DateUtils.formatDate(now));
2757 resp1.setHeader("Cache-Control", "max-age=3600");
2758 resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
2759 resp1.setHeader("ETag", "W/\"etag\"");
2760
2761 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2762 req2.setHeader("Cache-Control", "max-age=0,max-stale=0");
2763
2764 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
2765 EasyMock.expect(
2766 mockExecChain.proceed(
2767 EasyMock.isA(ClassicHttpRequest.class),
2768 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
2769
2770 EasyMock.expect(
2771 mockExecChain.proceed(
2772 EasyMock.capture(cap),
2773 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
2774
2775 replayMocks();
2776 execute(req1);
2777 execute(req2);
2778 verifyMocks();
2779
2780 final ClassicHttpRequest validation = cap.getValue();
2781 boolean isConditional = false;
2782 final String[] conditionalHeaders = { "If-Range", "If-Modified-Since", "If-Unmodified-Since",
2783 "If-Match", "If-None-Match" };
2784
2785 for (final String ch : conditionalHeaders) {
2786 if (validation.getFirstHeader(ch) != null) {
2787 isConditional = true;
2788 break;
2789 }
2790 }
2791
2792 if (isConditional) {
2793 boolean foundETag = false;
2794 final Iterator<HeaderElement> it = MessageSupport.iterate(validation, HttpHeaders.IF_MATCH);
2795 while (it.hasNext()) {
2796 final HeaderElement elt = it.next();
2797 if ("W/\"etag\"".equals(elt.getName())) {
2798 foundETag = true;
2799 }
2800 }
2801 final Iterator<HeaderElement> it2 = MessageSupport.iterate(validation, HttpHeaders.IF_NONE_MATCH);
2802 while (it2.hasNext()) {
2803 final HeaderElement elt = it2.next();
2804 if ("W/\"etag\"".equals(elt.getName())) {
2805 foundETag = true;
2806 }
2807 }
2808 Assert.assertTrue(foundETag);
2809 }
2810 }
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821 @Test
2822 public void testConditionalRequestWhereNotAllValidatorsMatchCannotBeServedFromCache() throws Exception {
2823 final Date now = new Date();
2824 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2825 final Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L);
2826
2827 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2828 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2829 resp1.setHeader("Date", DateUtils.formatDate(now));
2830 resp1.setHeader("Cache-Control", "max-age=3600");
2831 resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
2832 resp1.setHeader("ETag", "W/\"etag\"");
2833
2834 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2835 req2.setHeader("If-None-Match", "W/\"etag\"");
2836 req2.setHeader("If-Modified-Since", DateUtils.formatDate(twentySecondsAgo));
2837
2838
2839 EasyMock.expect(
2840 mockExecChain.proceed(
2841 EasyMock.isA(ClassicHttpRequest.class),
2842 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(2);
2843
2844 replayMocks();
2845 execute(req1);
2846 final ClassicHttpResponse result = execute(req2);
2847 verifyMocks();
2848
2849 Assert.assertFalse(HttpStatus.SC_NOT_MODIFIED == result.getCode());
2850 }
2851
2852 @Test
2853 public void testConditionalRequestWhereAllValidatorsMatchMayBeServedFromCache() throws Exception {
2854 final Date now = new Date();
2855 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
2856
2857 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
2858 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
2859 resp1.setHeader("Date", DateUtils.formatDate(now));
2860 resp1.setHeader("Cache-Control", "max-age=3600");
2861 resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
2862 resp1.setHeader("ETag", "W/\"etag\"");
2863
2864 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
2865 req2.setHeader("If-None-Match", "W/\"etag\"");
2866 req2.setHeader("If-Modified-Since", DateUtils.formatDate(tenSecondsAgo));
2867
2868
2869 EasyMock.expect(
2870 mockExecChain.proceed(
2871 EasyMock.isA(ClassicHttpRequest.class),
2872 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1,2);
2873
2874 replayMocks();
2875 execute(req1);
2876 execute(req2);
2877 verifyMocks();
2878 }
2879
2880
2881
2882
2883
2884
2885
2886
2887 @Test
2888 public void testCacheWithoutSupportForRangeAndContentRangeHeadersDoesNotCacheA206Response() throws Exception {
2889
2890 if (!supportsRangeAndContentRangeHeaders(impl)) {
2891 emptyMockCacheExpectsNoPuts();
2892
2893 final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/");
2894 req.setHeader("Range", "bytes=0-50");
2895
2896 final ClassicHttpResponse resp = new BasicClassicHttpResponse(206, "Partial Content");
2897 resp.setHeader("Content-Range", "bytes 0-50/128");
2898 resp.setHeader("ETag", "\"etag\"");
2899 resp.setHeader("Cache-Control", "max-age=3600");
2900
2901 EasyMock.expect(mockExecChain.proceed(
2902 EasyMock.isA(ClassicHttpRequest.class),
2903 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp);
2904
2905 replayMocks();
2906 execute(req);
2907 verifyMocks();
2908 }
2909 }
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922 @Test
2923 public void test302ResponseWithoutExplicitCacheabilityIsNotReturnedFromCache() throws Exception {
2924 originResponse = new BasicClassicHttpResponse(302, "Temporary Redirect");
2925 originResponse.setHeader("Location", "http://foo.example.com/other");
2926 originResponse.removeHeaders("Expires");
2927 originResponse.removeHeaders("Cache-Control");
2928
2929 backendExpectsAnyRequest().andReturn(originResponse).times(2);
2930
2931 replayMocks();
2932 execute(request);
2933 execute(request);
2934 verifyMocks();
2935 }
2936
2937
2938
2939
2940
2941
2942 private void testDoesNotModifyHeaderFromOrigin(final String header, final String value) throws Exception {
2943 originResponse = HttpTestUtils.make200Response();
2944 originResponse.setHeader(header, value);
2945
2946 backendExpectsAnyRequest().andReturn(originResponse);
2947
2948 replayMocks();
2949 final ClassicHttpResponse result = execute(request);
2950 verifyMocks();
2951
2952 Assert.assertEquals(value, result.getFirstHeader(header).getValue());
2953 }
2954
2955 @Test
2956 public void testDoesNotModifyContentLocationHeaderFromOrigin() throws Exception {
2957
2958 final String url = "http://foo.example.com/other";
2959 testDoesNotModifyHeaderFromOrigin("Content-Location", url);
2960 }
2961
2962 @Test
2963 public void testDoesNotModifyContentMD5HeaderFromOrigin() throws Exception {
2964 testDoesNotModifyHeaderFromOrigin("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
2965 }
2966
2967 @Test
2968 public void testDoesNotModifyEtagHeaderFromOrigin() throws Exception {
2969 testDoesNotModifyHeaderFromOrigin("Etag", "\"the-etag\"");
2970 }
2971
2972 @Test
2973 public void testDoesNotModifyLastModifiedHeaderFromOrigin() throws Exception {
2974 final String lm = DateUtils.formatDate(new Date());
2975 testDoesNotModifyHeaderFromOrigin("Last-Modified", lm);
2976 }
2977
2978 private void testDoesNotAddHeaderToOriginResponse(final String header) throws Exception {
2979 originResponse.removeHeaders(header);
2980
2981 backendExpectsAnyRequest().andReturn(originResponse);
2982
2983 replayMocks();
2984 final ClassicHttpResponse result = execute(request);
2985 verifyMocks();
2986
2987 Assert.assertNull(result.getFirstHeader(header));
2988 }
2989
2990 @Test
2991 public void testDoesNotAddContentLocationToOriginResponse() throws Exception {
2992 testDoesNotAddHeaderToOriginResponse("Content-Location");
2993 }
2994
2995 @Test
2996 public void testDoesNotAddContentMD5ToOriginResponse() throws Exception {
2997 testDoesNotAddHeaderToOriginResponse("Content-MD5");
2998 }
2999
3000 @Test
3001 public void testDoesNotAddEtagToOriginResponse() throws Exception {
3002 testDoesNotAddHeaderToOriginResponse("ETag");
3003 }
3004
3005 @Test
3006 public void testDoesNotAddLastModifiedToOriginResponse() throws Exception {
3007 testDoesNotAddHeaderToOriginResponse("Last-Modified");
3008 }
3009
3010 private void testDoesNotModifyHeaderFromOriginOnCacheHit(final String header, final String value) throws Exception {
3011
3012 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3013 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3014
3015 originResponse = HttpTestUtils.make200Response();
3016 originResponse.setHeader("Cache-Control", "max-age=3600");
3017 originResponse.setHeader(header, value);
3018
3019 backendExpectsAnyRequest().andReturn(originResponse);
3020
3021 replayMocks();
3022 execute(req1);
3023 final ClassicHttpResponse result = execute(req2);
3024 verifyMocks();
3025
3026 Assert.assertEquals(value, result.getFirstHeader(header).getValue());
3027 }
3028
3029 @Test
3030 public void testDoesNotModifyContentLocationFromOriginOnCacheHit() throws Exception {
3031 final String url = "http://foo.example.com/other";
3032 testDoesNotModifyHeaderFromOriginOnCacheHit("Content-Location", url);
3033 }
3034
3035 @Test
3036 public void testDoesNotModifyContentMD5FromOriginOnCacheHit() throws Exception {
3037 testDoesNotModifyHeaderFromOriginOnCacheHit("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
3038 }
3039
3040 @Test
3041 public void testDoesNotModifyEtagFromOriginOnCacheHit() throws Exception {
3042 testDoesNotModifyHeaderFromOriginOnCacheHit("Etag", "\"the-etag\"");
3043 }
3044
3045 @Test
3046 public void testDoesNotModifyLastModifiedFromOriginOnCacheHit() throws Exception {
3047 final String lm = DateUtils.formatDate(new Date(System.currentTimeMillis() - 10 * 1000L));
3048 testDoesNotModifyHeaderFromOriginOnCacheHit("Last-Modified", lm);
3049 }
3050
3051 private void testDoesNotAddHeaderOnCacheHit(final String header) throws Exception {
3052
3053 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3054 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3055
3056 originResponse.addHeader("Cache-Control", "max-age=3600");
3057 originResponse.removeHeaders(header);
3058
3059 backendExpectsAnyRequest().andReturn(originResponse);
3060
3061 replayMocks();
3062 execute(req1);
3063 final ClassicHttpResponse result = execute(req2);
3064 verifyMocks();
3065
3066 Assert.assertNull(result.getFirstHeader(header));
3067 }
3068
3069 @Test
3070 public void testDoesNotAddContentLocationHeaderOnCacheHit() throws Exception {
3071 testDoesNotAddHeaderOnCacheHit("Content-Location");
3072 }
3073
3074 @Test
3075 public void testDoesNotAddContentMD5HeaderOnCacheHit() throws Exception {
3076 testDoesNotAddHeaderOnCacheHit("Content-MD5");
3077 }
3078
3079 @Test
3080 public void testDoesNotAddETagHeaderOnCacheHit() throws Exception {
3081 testDoesNotAddHeaderOnCacheHit("ETag");
3082 }
3083
3084 @Test
3085 public void testDoesNotAddLastModifiedHeaderOnCacheHit() throws Exception {
3086 testDoesNotAddHeaderOnCacheHit("Last-Modified");
3087 }
3088
3089 private void testDoesNotModifyHeaderOnRequest(final String header, final String value) throws Exception {
3090 final BasicClassicHttpRequest req = new BasicClassicHttpRequest("POST","/");
3091 req.setEntity(HttpTestUtils.makeBody(128));
3092 req.setHeader("Content-Length","128");
3093 req.setHeader(header,value);
3094
3095 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
3096
3097 EasyMock.expect(
3098 mockExecChain.proceed(
3099 EasyMock.capture(cap),
3100 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
3101
3102 replayMocks();
3103 execute(req);
3104 verifyMocks();
3105
3106 final ClassicHttpRequest captured = cap.getValue();
3107 Assert.assertEquals(value, captured.getFirstHeader(header).getValue());
3108 }
3109
3110 @Test
3111 public void testDoesNotModifyContentLocationHeaderOnRequest() throws Exception {
3112 final String url = "http://foo.example.com/other";
3113 testDoesNotModifyHeaderOnRequest("Content-Location",url);
3114 }
3115
3116 @Test
3117 public void testDoesNotModifyContentMD5HeaderOnRequest() throws Exception {
3118 testDoesNotModifyHeaderOnRequest("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
3119 }
3120
3121 @Test
3122 public void testDoesNotModifyETagHeaderOnRequest() throws Exception {
3123 testDoesNotModifyHeaderOnRequest("ETag","\"etag\"");
3124 }
3125
3126 @Test
3127 public void testDoesNotModifyLastModifiedHeaderOnRequest() throws Exception {
3128 final long tenSecondsAgo = System.currentTimeMillis() - 10 * 1000L;
3129 final String lm = DateUtils.formatDate(new Date(tenSecondsAgo));
3130 testDoesNotModifyHeaderOnRequest("Last-Modified", lm);
3131 }
3132
3133 private void testDoesNotAddHeaderToRequestIfNotPresent(final String header) throws Exception {
3134 final BasicClassicHttpRequest req = new BasicClassicHttpRequest("POST","/");
3135 req.setEntity(HttpTestUtils.makeBody(128));
3136 req.setHeader("Content-Length","128");
3137 req.removeHeaders(header);
3138
3139 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
3140
3141 EasyMock.expect(
3142 mockExecChain.proceed(
3143 EasyMock.capture(cap),
3144 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
3145
3146 replayMocks();
3147 execute(req);
3148 verifyMocks();
3149
3150 final ClassicHttpRequest captured = cap.getValue();
3151 Assert.assertNull(captured.getFirstHeader(header));
3152 }
3153
3154 @Test
3155 public void testDoesNotAddContentLocationToRequestIfNotPresent() throws Exception {
3156 testDoesNotAddHeaderToRequestIfNotPresent("Content-Location");
3157 }
3158
3159 @Test
3160 public void testDoesNotAddContentMD5ToRequestIfNotPresent() throws Exception {
3161 testDoesNotAddHeaderToRequestIfNotPresent("Content-MD5");
3162 }
3163
3164 @Test
3165 public void testDoesNotAddETagToRequestIfNotPresent() throws Exception {
3166 testDoesNotAddHeaderToRequestIfNotPresent("ETag");
3167 }
3168
3169 @Test
3170 public void testDoesNotAddLastModifiedToRequestIfNotPresent() throws Exception {
3171 testDoesNotAddHeaderToRequestIfNotPresent("Last-Modified");
3172 }
3173
3174
3175
3176
3177
3178
3179
3180 @Test
3181 public void testDoesNotModifyExpiresHeaderFromOrigin() throws Exception {
3182 final long inTenSeconds = System.currentTimeMillis() + 10 * 1000L;
3183 final String expires = DateUtils.formatDate(new Date(inTenSeconds));
3184 testDoesNotModifyHeaderFromOrigin("Expires", expires);
3185 }
3186
3187 @Test
3188 public void testDoesNotModifyExpiresHeaderFromOriginOnCacheHit() throws Exception {
3189 final long inTenSeconds = System.currentTimeMillis() + 10 * 1000L;
3190 final String expires = DateUtils.formatDate(new Date(inTenSeconds));
3191 testDoesNotModifyHeaderFromOriginOnCacheHit("Expires", expires);
3192 }
3193
3194 @Test
3195 public void testExpiresHeaderMatchesDateIfAddedToOriginResponse() throws Exception {
3196 originResponse.removeHeaders("Expires");
3197
3198 backendExpectsAnyRequest().andReturn(originResponse);
3199
3200 replayMocks();
3201 final ClassicHttpResponse result = execute(request);
3202 verifyMocks();
3203
3204 final Header expHdr = result.getFirstHeader("Expires");
3205 if (expHdr != null) {
3206 Assert.assertEquals(result.getFirstHeader("Date").getValue(),
3207 expHdr.getValue());
3208 }
3209 }
3210
3211 @Test
3212 public void testExpiresHeaderMatchesDateIfAddedToCacheHit() throws Exception {
3213 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3214 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3215
3216 originResponse.setHeader("Cache-Control","max-age=3600");
3217 originResponse.removeHeaders("Expires");
3218
3219 backendExpectsAnyRequest().andReturn(originResponse);
3220
3221 replayMocks();
3222 execute(req1);
3223 final ClassicHttpResponse result = execute(req2);
3224 verifyMocks();
3225
3226 final Header expHdr = result.getFirstHeader("Expires");
3227 if (expHdr != null) {
3228 Assert.assertEquals(result.getFirstHeader("Date").getValue(),
3229 expHdr.getValue());
3230 }
3231 }
3232
3233
3234
3235
3236
3237
3238
3239 private void testDoesNotModifyHeaderFromOriginResponseWithNoTransform(final String header, final String value) throws Exception {
3240 originResponse.addHeader("Cache-Control","no-transform");
3241 originResponse.setHeader(header, value);
3242
3243 backendExpectsAnyRequest().andReturn(originResponse);
3244
3245 replayMocks();
3246 final ClassicHttpResponse result = execute(request);
3247 verifyMocks();
3248
3249 Assert.assertEquals(value, result.getFirstHeader(header).getValue());
3250 }
3251
3252 @Test
3253 public void testDoesNotModifyContentEncodingHeaderFromOriginResponseWithNoTransform() throws Exception {
3254 testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Encoding","gzip");
3255 }
3256
3257 @Test
3258 public void testDoesNotModifyContentRangeHeaderFromOriginResponseWithNoTransform() throws Exception {
3259 request.setHeader("If-Range","\"etag\"");
3260 request.setHeader("Range","bytes=0-49");
3261
3262 originResponse = new BasicClassicHttpResponse(206, "Partial Content");
3263 originResponse.setEntity(HttpTestUtils.makeBody(50));
3264 testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Range","bytes 0-49/128");
3265 }
3266
3267 @Test
3268 public void testDoesNotModifyContentTypeHeaderFromOriginResponseWithNoTransform() throws Exception {
3269 testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Type","text/html;charset=utf-8");
3270 }
3271
3272 private void testDoesNotModifyHeaderOnCachedResponseWithNoTransform(final String header, final String value) throws Exception {
3273 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3274 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3275
3276 originResponse.addHeader("Cache-Control","max-age=3600, no-transform");
3277 originResponse.setHeader(header, value);
3278
3279 backendExpectsAnyRequest().andReturn(originResponse);
3280
3281 replayMocks();
3282 execute(req1);
3283 final ClassicHttpResponse result = execute(req2);
3284 verifyMocks();
3285
3286 Assert.assertEquals(value, result.getFirstHeader(header).getValue());
3287 }
3288
3289 @Test
3290 public void testDoesNotModifyContentEncodingHeaderOnCachedResponseWithNoTransform() throws Exception {
3291 testDoesNotModifyHeaderOnCachedResponseWithNoTransform("Content-Encoding","gzip");
3292 }
3293
3294 @Test
3295 public void testDoesNotModifyContentTypeHeaderOnCachedResponseWithNoTransform() throws Exception {
3296 testDoesNotModifyHeaderOnCachedResponseWithNoTransform("Content-Type","text/html;charset=utf-8");
3297 }
3298
3299 @Test
3300 public void testDoesNotModifyContentRangeHeaderOnCachedResponseWithNoTransform() throws Exception {
3301 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3302 req1.setHeader("If-Range","\"etag\"");
3303 req1.setHeader("Range","bytes=0-49");
3304 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3305 req2.setHeader("If-Range","\"etag\"");
3306 req2.setHeader("Range","bytes=0-49");
3307
3308 originResponse.addHeader("Cache-Control","max-age=3600, no-transform");
3309 originResponse.setHeader("Content-Range", "bytes 0-49/128");
3310
3311 backendExpectsAnyRequest().andReturn(originResponse).times(1,2);
3312
3313 replayMocks();
3314 execute(req1);
3315 final ClassicHttpResponse result = execute(req2);
3316 verifyMocks();
3317
3318 Assert.assertEquals("bytes 0-49/128",
3319 result.getFirstHeader("Content-Range").getValue());
3320 }
3321
3322 @Test
3323 public void testDoesNotAddContentEncodingHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception {
3324 originResponse.addHeader("Cache-Control","no-transform");
3325 testDoesNotAddHeaderToOriginResponse("Content-Encoding");
3326 }
3327
3328 @Test
3329 public void testDoesNotAddContentRangeHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception {
3330 originResponse.addHeader("Cache-Control","no-transform");
3331 testDoesNotAddHeaderToOriginResponse("Content-Range");
3332 }
3333
3334 @Test
3335 public void testDoesNotAddContentTypeHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception {
3336 originResponse.addHeader("Cache-Control","no-transform");
3337 testDoesNotAddHeaderToOriginResponse("Content-Type");
3338 }
3339
3340
3341 @Test
3342 public void testDoesNotAddContentEncodingHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception {
3343 originResponse.addHeader("Cache-Control","no-transform");
3344 testDoesNotAddHeaderOnCacheHit("Content-Encoding");
3345 }
3346
3347 @Test
3348 public void testDoesNotAddContentRangeHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception {
3349 originResponse.addHeader("Cache-Control","no-transform");
3350 testDoesNotAddHeaderOnCacheHit("Content-Range");
3351 }
3352
3353 @Test
3354 public void testDoesNotAddContentTypeHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception {
3355 originResponse.addHeader("Cache-Control","no-transform");
3356 testDoesNotAddHeaderOnCacheHit("Content-Type");
3357 }
3358
3359
3360 @Test
3361 public void testDoesNotAddContentEncodingToRequestIfNotPresent() throws Exception {
3362 testDoesNotAddHeaderToRequestIfNotPresent("Content-Encoding");
3363 }
3364
3365 @Test
3366 public void testDoesNotAddContentRangeToRequestIfNotPresent() throws Exception {
3367 testDoesNotAddHeaderToRequestIfNotPresent("Content-Range");
3368 }
3369
3370 @Test
3371 public void testDoesNotAddContentTypeToRequestIfNotPresent() throws Exception {
3372 testDoesNotAddHeaderToRequestIfNotPresent("Content-Type");
3373 }
3374
3375 @Test
3376 public void testDoesNotAddContentEncodingHeaderToRequestIfNotPresent() throws Exception {
3377 testDoesNotAddHeaderToRequestIfNotPresent("Content-Encoding");
3378 }
3379
3380 @Test
3381 public void testDoesNotAddContentRangeHeaderToRequestIfNotPresent() throws Exception {
3382 testDoesNotAddHeaderToRequestIfNotPresent("Content-Range");
3383 }
3384
3385 @Test
3386 public void testDoesNotAddContentTypeHeaderToRequestIfNotPresent() throws Exception {
3387 testDoesNotAddHeaderToRequestIfNotPresent("Content-Type");
3388 }
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401 public void testCachedEntityBodyIsUsedForResponseAfter304Validation() throws Exception {
3402 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3403 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
3404 resp1.setHeader("Cache-Control","max-age=3600");
3405 resp1.setHeader("ETag","\"etag\"");
3406
3407 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3408 req2.setHeader("Cache-Control","max-age=0, max-stale=0");
3409 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
3410
3411 backendExpectsAnyRequestAndReturn(resp1);
3412 backendExpectsAnyRequestAndReturn(resp2);
3413
3414 replayMocks();
3415 execute(req1);
3416 final ClassicHttpResponse result = execute(req2);
3417 verifyMocks();
3418
3419 final InputStream i1 = resp1.getEntity().getContent();
3420 final InputStream i2 = result.getEntity().getContent();
3421 int b1, b2;
3422 while((b1 = i1.read()) != -1) {
3423 b2 = i2.read();
3424 Assert.assertEquals(b1, b2);
3425 }
3426 b2 = i2.read();
3427 Assert.assertEquals(-1, b2);
3428 i1.close();
3429 i2.close();
3430 }
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443 private void decorateWithEndToEndHeaders(final ClassicHttpResponse r) {
3444 r.setHeader("Allow","GET");
3445 r.setHeader("Content-Encoding","gzip");
3446 r.setHeader("Content-Language","en");
3447 r.setHeader("Content-Length", "128");
3448 r.setHeader("Content-Location","http://foo.example.com/other");
3449 r.setHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
3450 r.setHeader("Content-Type", "text/html;charset=utf-8");
3451 r.setHeader("Expires", DateUtils.formatDate(new Date(System.currentTimeMillis() + 10 * 1000L)));
3452 r.setHeader("Last-Modified", DateUtils.formatDate(new Date(System.currentTimeMillis() - 10 * 1000L)));
3453 r.setHeader("Location", "http://foo.example.com/other2");
3454 r.setHeader("Pragma", "x-pragma");
3455 r.setHeader("Retry-After","180");
3456 }
3457
3458 @Test
3459 public void testResponseIncludesCacheEntryEndToEndHeadersForResponseAfter304Validation() throws Exception {
3460 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3461 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
3462 resp1.setHeader("Cache-Control","max-age=3600");
3463 resp1.setHeader("ETag","\"etag\"");
3464 decorateWithEndToEndHeaders(resp1);
3465
3466 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3467 req2.setHeader("Cache-Control", "max-age=0, max-stale=0");
3468 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
3469 resp2.setHeader("Date", DateUtils.formatDate(new Date()));
3470 resp2.setHeader("Server", "MockServer/1.0");
3471
3472 backendExpectsAnyRequestAndReturn(resp1);
3473 backendExpectsAnyRequestAndReturn(resp2);
3474
3475 replayMocks();
3476 execute(req1);
3477 final ClassicHttpResponse result = execute(req2);
3478 verifyMocks();
3479
3480 final String[] endToEndHeaders = {
3481 "Cache-Control", "ETag", "Allow", "Content-Encoding",
3482 "Content-Language", "Content-Length", "Content-Location",
3483 "Content-MD5", "Content-Type", "Expires", "Last-Modified",
3484 "Location", "Pragma", "Retry-After"
3485 };
3486 for(final String h : endToEndHeaders) {
3487 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp1, h),
3488 HttpTestUtils.getCanonicalHeaderValue(result, h));
3489 }
3490 }
3491
3492 @Test
3493 public void testUpdatedEndToEndHeadersFrom304ArePassedOnResponseAndUpdatedInCacheEntry() throws Exception {
3494
3495 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3496 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
3497 resp1.setHeader("Cache-Control","max-age=3600");
3498 resp1.setHeader("ETag","\"etag\"");
3499 decorateWithEndToEndHeaders(resp1);
3500
3501 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3502 req2.setHeader("Cache-Control", "max-age=0, max-stale=0");
3503 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
3504 resp2.setHeader("Cache-Control", "max-age=1800");
3505 resp2.setHeader("Date", DateUtils.formatDate(new Date()));
3506 resp2.setHeader("Server", "MockServer/1.0");
3507 resp2.setHeader("Allow", "GET,HEAD");
3508 resp2.setHeader("Content-Language", "en,en-us");
3509 resp2.setHeader("Content-Location", "http://foo.example.com/new");
3510 resp2.setHeader("Content-Type","text/html");
3511 resp2.setHeader("Expires", DateUtils.formatDate(new Date(System.currentTimeMillis() + 5 * 1000L)));
3512 resp2.setHeader("Location", "http://foo.example.com/new2");
3513 resp2.setHeader("Pragma","x-new-pragma");
3514 resp2.setHeader("Retry-After","120");
3515
3516 backendExpectsAnyRequestAndReturn(resp1);
3517 backendExpectsAnyRequestAndReturn(resp2);
3518
3519 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3520
3521 replayMocks();
3522 execute(req1);
3523 final ClassicHttpResponse result1 = execute(req2);
3524 final ClassicHttpResponse result2 = execute(req3);
3525 verifyMocks();
3526
3527 final String[] endToEndHeaders = {
3528 "Date", "Cache-Control", "Allow", "Content-Language",
3529 "Content-Location", "Content-Type", "Expires", "Location",
3530 "Pragma", "Retry-After"
3531 };
3532 for(final String h : endToEndHeaders) {
3533 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h),
3534 HttpTestUtils.getCanonicalHeaderValue(result1, h));
3535 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h),
3536 HttpTestUtils.getCanonicalHeaderValue(result2, h));
3537 }
3538 }
3539
3540
3541
3542
3543
3544 @Test
3545 public void testMultiHeadersAreSuccessfullyReplacedOn304Validation() throws Exception {
3546 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3547 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
3548 resp1.addHeader("Cache-Control","max-age=3600");
3549 resp1.addHeader("Cache-Control","public");
3550 resp1.setHeader("ETag","\"etag\"");
3551
3552 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3553 req2.setHeader("Cache-Control", "max-age=0, max-stale=0");
3554 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
3555 resp2.setHeader("Cache-Control", "max-age=1800");
3556
3557 backendExpectsAnyRequestAndReturn(resp1);
3558 backendExpectsAnyRequestAndReturn(resp2);
3559
3560 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3561
3562 replayMocks();
3563 execute(req1);
3564 final ClassicHttpResponse result1 = execute(req2);
3565 final ClassicHttpResponse result2 = execute(req3);
3566 verifyMocks();
3567
3568 final String h = "Cache-Control";
3569 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h),
3570 HttpTestUtils.getCanonicalHeaderValue(result1, h));
3571 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h),
3572 HttpTestUtils.getCanonicalHeaderValue(result2, h));
3573 }
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594 @Test
3595 public void testCannotCombinePartialResponseIfIncomingResponseDoesNotHaveACacheValidator() throws Exception {
3596
3597 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3598 req1.setHeader("Range","bytes=0-49");
3599
3600 final Date now = new Date();
3601 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3602 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3603
3604 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3605 resp1.setEntity(HttpTestUtils.makeBody(50));
3606 resp1.setHeader("Server","MockServer/1.0");
3607 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3608 resp1.setHeader("Cache-Control","max-age=3600");
3609 resp1.setHeader("Content-Range","bytes 0-49/128");
3610 resp1.setHeader("ETag","\"etag1\"");
3611
3612 backendExpectsAnyRequestAndReturn(resp1);
3613
3614 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3615 req2.setHeader("Range","bytes=50-127");
3616
3617 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3618 resp2.setEntity(HttpTestUtils.makeBody(78));
3619 resp2.setHeader("Cache-Control","max-age=3600");
3620 resp2.setHeader("Content-Range","bytes 50-127/128");
3621 resp2.setHeader("Server","MockServer/1.0");
3622 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3623
3624 backendExpectsAnyRequestAndReturn(resp2);
3625
3626 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3627
3628 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3629 resp3.setEntity(HttpTestUtils.makeBody(128));
3630 resp3.setHeader("Server","MockServer/1.0");
3631 resp3.setHeader("Date", DateUtils.formatDate(now));
3632
3633 backendExpectsAnyRequestAndReturn(resp3);
3634
3635 replayMocks();
3636 execute(req1);
3637 execute(req2);
3638 execute(req3);
3639 verifyMocks();
3640 }
3641
3642 @Test
3643 public void testCannotCombinePartialResponseIfCacheEntryDoesNotHaveACacheValidator() throws Exception {
3644
3645 final Date now = new Date();
3646 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3647 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3648
3649 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3650 req1.setHeader("Range","bytes=0-49");
3651
3652 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3653 resp1.setEntity(HttpTestUtils.makeBody(50));
3654 resp1.setHeader("Cache-Control","max-age=3600");
3655 resp1.setHeader("Content-Range","bytes 0-49/128");
3656 resp1.setHeader("Server","MockServer/1.0");
3657 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3658
3659 backendExpectsAnyRequestAndReturn(resp1);
3660
3661 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3662 req2.setHeader("Range","bytes=50-127");
3663
3664 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3665 resp2.setEntity(HttpTestUtils.makeBody(78));
3666 resp2.setHeader("Cache-Control","max-age=3600");
3667 resp2.setHeader("Content-Range","bytes 50-127/128");
3668 resp2.setHeader("ETag","\"etag1\"");
3669 resp2.setHeader("Server","MockServer/1.0");
3670 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3671
3672 backendExpectsAnyRequestAndReturn(resp2);
3673
3674 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3675
3676 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3677 resp3.setEntity(HttpTestUtils.makeBody(128));
3678 resp3.setHeader("Server","MockServer/1.0");
3679 resp3.setHeader("Date", DateUtils.formatDate(now));
3680
3681 backendExpectsAnyRequestAndReturn(resp3);
3682
3683 replayMocks();
3684 execute(req1);
3685 execute(req2);
3686 execute(req3);
3687 verifyMocks();
3688 }
3689
3690 @Test
3691 public void testCannotCombinePartialResponseIfCacheValidatorsDoNotStronglyMatch() throws Exception {
3692
3693 final Date now = new Date();
3694 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3695 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3696
3697 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3698 req1.setHeader("Range","bytes=0-49");
3699
3700 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3701 resp1.setEntity(HttpTestUtils.makeBody(50));
3702 resp1.setHeader("Cache-Control","max-age=3600");
3703 resp1.setHeader("Content-Range","bytes 0-49/128");
3704 resp1.setHeader("ETag","\"etag1\"");
3705 resp1.setHeader("Server","MockServer/1.0");
3706 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3707
3708 backendExpectsAnyRequestAndReturn(resp1);
3709
3710 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3711 req2.setHeader("Range","bytes=50-127");
3712
3713 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3714 resp2.setEntity(HttpTestUtils.makeBody(78));
3715 resp2.setHeader("Cache-Control","max-age=3600");
3716 resp2.setHeader("Content-Range","bytes 50-127/128");
3717 resp2.setHeader("ETag","\"etag2\"");
3718 resp2.setHeader("Server","MockServer/1.0");
3719 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3720
3721 backendExpectsAnyRequestAndReturn(resp2);
3722
3723 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3724
3725 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3726 resp3.setEntity(HttpTestUtils.makeBody(128));
3727 resp3.setHeader("Server","MockServer/1.0");
3728 resp3.setHeader("Date", DateUtils.formatDate(now));
3729
3730 backendExpectsAnyRequestAndReturn(resp3);
3731
3732 replayMocks();
3733 execute(req1);
3734 execute(req2);
3735 execute(req3);
3736 verifyMocks();
3737 }
3738
3739 @Test
3740 public void testMustDiscardLeastRecentPartialResponseIfIncomingRequestDoesNotHaveCacheValidator() throws Exception {
3741
3742 final Date now = new Date();
3743 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3744 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3745
3746 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3747 req1.setHeader("Range","bytes=0-49");
3748
3749 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3750 resp1.setEntity(HttpTestUtils.makeBody(50));
3751 resp1.setHeader("Cache-Control","max-age=3600");
3752 resp1.setHeader("Content-Range","bytes 0-49/128");
3753 resp1.setHeader("ETag","\"etag1\"");
3754 resp1.setHeader("Server","MockServer/1.0");
3755 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3756
3757 backendExpectsAnyRequestAndReturn(resp1);
3758
3759 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3760 req2.setHeader("Range","bytes=50-127");
3761
3762 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3763 resp2.setEntity(HttpTestUtils.makeBody(78));
3764 resp2.setHeader("Cache-Control","max-age=3600");
3765 resp2.setHeader("Content-Range","bytes 50-127/128");
3766 resp2.setHeader("Server","MockServer/1.0");
3767 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3768
3769 backendExpectsAnyRequestAndReturn(resp2);
3770
3771 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3772 req3.setHeader("Range","bytes=0-49");
3773
3774 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3775 resp3.setEntity(HttpTestUtils.makeBody(128));
3776 resp3.setHeader("Server","MockServer/1.0");
3777 resp3.setHeader("Date", DateUtils.formatDate(now));
3778
3779
3780 backendExpectsAnyRequestAndReturn(resp3);
3781
3782 replayMocks();
3783 execute(req1);
3784 execute(req2);
3785 execute(req3);
3786 verifyMocks();
3787 }
3788
3789 @Test
3790 public void testMustDiscardLeastRecentPartialResponseIfCachedResponseDoesNotHaveCacheValidator() throws Exception {
3791
3792 final Date now = new Date();
3793 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3794 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3795
3796 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3797 req1.setHeader("Range","bytes=0-49");
3798
3799 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3800 resp1.setEntity(HttpTestUtils.makeBody(50));
3801 resp1.setHeader("Cache-Control","max-age=3600");
3802 resp1.setHeader("Content-Range","bytes 0-49/128");
3803 resp1.setHeader("Server","MockServer/1.0");
3804 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3805
3806 backendExpectsAnyRequestAndReturn(resp1);
3807
3808 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3809 req2.setHeader("Range","bytes=50-127");
3810
3811 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3812 resp2.setEntity(HttpTestUtils.makeBody(78));
3813 resp2.setHeader("Cache-Control","max-age=3600");
3814 resp2.setHeader("Content-Range","bytes 50-127/128");
3815 resp2.setHeader("ETag","\"etag1\"");
3816 resp2.setHeader("Server","MockServer/1.0");
3817 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3818
3819 backendExpectsAnyRequestAndReturn(resp2);
3820
3821 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3822 req3.setHeader("Range","bytes=0-49");
3823
3824 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3825 resp3.setEntity(HttpTestUtils.makeBody(128));
3826 resp3.setHeader("Server","MockServer/1.0");
3827 resp3.setHeader("Date", DateUtils.formatDate(now));
3828
3829
3830 backendExpectsAnyRequestAndReturn(resp3);
3831
3832 replayMocks();
3833 execute(req1);
3834 execute(req2);
3835 execute(req3);
3836 verifyMocks();
3837 }
3838
3839 @Test
3840 public void testMustDiscardLeastRecentPartialResponseIfCacheValidatorsDoNotStronglyMatch() throws Exception {
3841
3842 final Date now = new Date();
3843 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3844 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3845
3846 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3847 req1.setHeader("Range","bytes=0-49");
3848
3849 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3850 resp1.setEntity(HttpTestUtils.makeBody(50));
3851 resp1.setHeader("Cache-Control","max-age=3600");
3852 resp1.setHeader("Content-Range","bytes 0-49/128");
3853 resp1.setHeader("Etag","\"etag1\"");
3854 resp1.setHeader("Server","MockServer/1.0");
3855 resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3856
3857 backendExpectsAnyRequestAndReturn(resp1);
3858
3859 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3860 req2.setHeader("Range","bytes=50-127");
3861
3862 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3863 resp2.setEntity(HttpTestUtils.makeBody(78));
3864 resp2.setHeader("Cache-Control","max-age=3600");
3865 resp2.setHeader("Content-Range","bytes 50-127/128");
3866 resp2.setHeader("ETag","\"etag2\"");
3867 resp2.setHeader("Server","MockServer/1.0");
3868 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3869
3870 backendExpectsAnyRequestAndReturn(resp2);
3871
3872 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3873 req3.setHeader("Range","bytes=0-49");
3874
3875 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3876 resp3.setEntity(HttpTestUtils.makeBody(128));
3877 resp3.setHeader("Server","MockServer/1.0");
3878 resp3.setHeader("Date", DateUtils.formatDate(now));
3879
3880
3881 backendExpectsAnyRequestAndReturn(resp3);
3882
3883 replayMocks();
3884 execute(req1);
3885 execute(req2);
3886 execute(req3);
3887 verifyMocks();
3888 }
3889
3890 @Test
3891 public void testMustDiscardLeastRecentPartialResponseIfCacheValidatorsDoNotStronglyMatchEvenIfResponsesOutOfOrder() throws Exception {
3892
3893 final Date now = new Date();
3894 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3895 final Date twoSecondsAgo = new Date(now.getTime() - 2 * 1000L);
3896
3897 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3898 req1.setHeader("Range","bytes=0-49");
3899
3900 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3901 resp1.setEntity(HttpTestUtils.makeBody(50));
3902 resp1.setHeader("Cache-Control","max-age=3600");
3903 resp1.setHeader("Content-Range","bytes 0-49/128");
3904 resp1.setHeader("Etag","\"etag1\"");
3905 resp1.setHeader("Server","MockServer/1.0");
3906 resp1.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3907
3908 backendExpectsAnyRequestAndReturn(resp1);
3909
3910 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3911 req2.setHeader("Range","bytes=50-127");
3912
3913 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3914 resp2.setEntity(HttpTestUtils.makeBody(78));
3915 resp2.setHeader("Cache-Control","max-age=3600");
3916 resp2.setHeader("Content-Range","bytes 50-127/128");
3917 resp2.setHeader("ETag","\"etag2\"");
3918 resp2.setHeader("Server","MockServer/1.0");
3919 resp2.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
3920
3921 backendExpectsAnyRequestAndReturn(resp2);
3922
3923 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3924 req3.setHeader("Range","bytes=50-127");
3925
3926 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3927 resp3.setEntity(HttpTestUtils.makeBody(128));
3928 resp3.setHeader("Server","MockServer/1.0");
3929 resp3.setHeader("Date", DateUtils.formatDate(now));
3930
3931
3932 backendExpectsAnyRequestAndReturn(resp3);
3933
3934 replayMocks();
3935 execute(req1);
3936 execute(req2);
3937 execute(req3);
3938 verifyMocks();
3939 }
3940
3941 @Test
3942 public void testMustDiscardCachedPartialResponseIfCacheValidatorsDoNotStronglyMatchAndDateHeadersAreEqual() throws Exception {
3943
3944 final Date now = new Date();
3945 final Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
3946
3947 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
3948 req1.setHeader("Range","bytes=0-49");
3949
3950 final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3951 resp1.setEntity(HttpTestUtils.makeBody(50));
3952 resp1.setHeader("Cache-Control","max-age=3600");
3953 resp1.setHeader("Content-Range","bytes 0-49/128");
3954 resp1.setHeader("Etag","\"etag1\"");
3955 resp1.setHeader("Server","MockServer/1.0");
3956 resp1.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3957
3958 backendExpectsAnyRequestAndReturn(resp1);
3959
3960 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
3961 req2.setHeader("Range","bytes=50-127");
3962
3963 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
3964 resp2.setEntity(HttpTestUtils.makeBody(78));
3965 resp2.setHeader("Cache-Control","max-age=3600");
3966 resp2.setHeader("Content-Range","bytes 50-127/128");
3967 resp2.setHeader("ETag","\"etag2\"");
3968 resp2.setHeader("Server","MockServer/1.0");
3969 resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
3970
3971 backendExpectsAnyRequestAndReturn(resp2);
3972
3973 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
3974 req3.setHeader("Range","bytes=0-49");
3975
3976 final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
3977 resp3.setEntity(HttpTestUtils.makeBody(128));
3978 resp3.setHeader("Server","MockServer/1.0");
3979 resp3.setHeader("Date", DateUtils.formatDate(now));
3980
3981
3982 backendExpectsAnyRequestAndReturn(resp3);
3983
3984 replayMocks();
3985 execute(req1);
3986 execute(req2);
3987 execute(req3);
3988 verifyMocks();
3989 }
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000 @Test
4001 public void testCannotUseVariantCacheEntryIfNotAllSelectingRequestHeadersMatch() throws Exception {
4002
4003 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4004 req1.setHeader("Accept-Encoding","gzip");
4005
4006 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4007 resp1.setHeader("ETag","\"etag1\"");
4008 resp1.setHeader("Cache-Control","max-age=3600");
4009 resp1.setHeader("Vary","Accept-Encoding");
4010
4011 backendExpectsAnyRequestAndReturn(resp1);
4012
4013 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4014 req2.removeHeaders("Accept-Encoding");
4015
4016 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4017 resp2.setHeader("ETag","\"etag1\"");
4018 resp2.setHeader("Cache-Control","max-age=3600");
4019
4020
4021 backendExpectsAnyRequestAndReturn(resp2);
4022
4023 replayMocks();
4024 execute(req1);
4025 execute(req2);
4026 verifyMocks();
4027 }
4028
4029
4030
4031
4032
4033
4034
4035 @Test
4036 public void testCannotServeFromCacheForVaryStar() throws Exception {
4037 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4038
4039 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4040 resp1.setHeader("ETag","\"etag1\"");
4041 resp1.setHeader("Cache-Control","max-age=3600");
4042 resp1.setHeader("Vary","*");
4043
4044 backendExpectsAnyRequestAndReturn(resp1);
4045
4046 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4047
4048 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4049 resp2.setHeader("ETag","\"etag1\"");
4050 resp2.setHeader("Cache-Control","max-age=3600");
4051
4052
4053 backendExpectsAnyRequestAndReturn(resp2);
4054
4055 replayMocks();
4056 execute(req1);
4057 execute(req2);
4058 verifyMocks();
4059 }
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083 @Test
4084 public void testNonmatchingVariantCannotBeServedFromCacheUnlessConditionallyValidated() throws Exception {
4085
4086 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4087 req1.setHeader("User-Agent","MyBrowser/1.0");
4088
4089 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4090 resp1.setHeader("ETag","\"etag1\"");
4091 resp1.setHeader("Cache-Control","max-age=3600");
4092 resp1.setHeader("Vary","User-Agent");
4093 resp1.setHeader("Content-Type","application/octet-stream");
4094
4095 backendExpectsAnyRequestAndReturn(resp1);
4096
4097 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4098 req2.setHeader("User-Agent","MyBrowser/1.5");
4099
4100 final ClassicHttpRequest conditional = new BasicClassicHttpRequest("GET", "/");
4101 conditional.setHeader("User-Agent","MyBrowser/1.5");
4102 conditional.setHeader("If-None-Match","\"etag1\"");
4103
4104 final ClassicHttpResponse resp200 = HttpTestUtils.make200Response();
4105 resp200.setHeader("ETag","\"etag1\"");
4106 resp200.setHeader("Vary","User-Agent");
4107
4108 final ClassicHttpResponse resp304 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
4109 resp304.setHeader("ETag","\"etag1\"");
4110 resp304.setHeader("Vary","User-Agent");
4111
4112 final Capture<ClassicHttpRequest> condCap = EasyMock.newCapture();
4113 final Capture<ClassicHttpRequest> uncondCap = EasyMock.newCapture();
4114
4115 EasyMock.expect(
4116 mockExecChain.proceed(
4117 EasyMock.and(eqRequest(conditional), EasyMock.capture(condCap)),
4118 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp304).times(0,1);
4119 EasyMock.expect(
4120 mockExecChain.proceed(
4121 EasyMock.and(eqRequest(req2), EasyMock.capture(uncondCap)),
4122 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp200).times(0,1);
4123
4124 replayMocks();
4125 execute(req1);
4126 final ClassicHttpResponse result = execute(req2);
4127 verifyMocks();
4128
4129 if (HttpStatus.SC_OK == result.getCode()) {
4130 Assert.assertTrue(condCap.hasCaptured()
4131 || uncondCap.hasCaptured());
4132 if (uncondCap.hasCaptured()) {
4133 Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp200, result));
4134 }
4135 }
4136 }
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148 protected void testUnsafeOperationInvalidatesCacheForThatUri(
4149 final ClassicHttpRequest unsafeReq) throws Exception {
4150 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4151 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4152 resp1.setHeader("Cache-Control","public, max-age=3600");
4153
4154 backendExpectsAnyRequestAndReturn(resp1);
4155
4156 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
4157
4158 backendExpectsAnyRequestAndReturn(resp2);
4159
4160 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
4161 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
4162 resp3.setHeader("Cache-Control","public, max-age=3600");
4163
4164
4165 backendExpectsAnyRequestAndReturn(resp3);
4166
4167 replayMocks();
4168 execute(req1);
4169 execute(unsafeReq);
4170 execute(req3);
4171 verifyMocks();
4172 }
4173
4174 @Test
4175 public void testPutToUriInvalidatesCacheForThatUri() throws Exception {
4176 final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
4177 testUnsafeOperationInvalidatesCacheForThatUri(req);
4178 }
4179
4180 @Test
4181 public void testDeleteToUriInvalidatesCacheForThatUri() throws Exception {
4182 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE","/");
4183 testUnsafeOperationInvalidatesCacheForThatUri(req);
4184 }
4185
4186 @Test
4187 public void testPostToUriInvalidatesCacheForThatUri() throws Exception {
4188 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4189 testUnsafeOperationInvalidatesCacheForThatUri(req);
4190 }
4191
4192 protected void testUnsafeMethodInvalidatesCacheForHeaderUri(
4193 final ClassicHttpRequest unsafeReq) throws Exception {
4194 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/content");
4195 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4196 resp1.setHeader("Cache-Control","public, max-age=3600");
4197
4198 backendExpectsAnyRequestAndReturn(resp1);
4199
4200 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
4201
4202 backendExpectsAnyRequestAndReturn(resp2);
4203
4204 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content");
4205 final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
4206 resp3.setHeader("Cache-Control","public, max-age=3600");
4207
4208
4209 backendExpectsAnyRequestAndReturn(resp3);
4210
4211 replayMocks();
4212 execute(req1);
4213 execute(unsafeReq);
4214 execute(req3);
4215 verifyMocks();
4216 }
4217
4218 protected void testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(
4219 final ClassicHttpRequest unsafeReq) throws Exception {
4220 unsafeReq.setHeader("Content-Location","http://foo.example.com/content");
4221 testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq);
4222 }
4223
4224 protected void testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(
4225 final ClassicHttpRequest unsafeReq) throws Exception {
4226 unsafeReq.setHeader("Content-Location","/content");
4227 testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq);
4228 }
4229
4230 protected void testUnsafeMethodInvalidatesCacheForUriInLocationHeader(
4231 final ClassicHttpRequest unsafeReq) throws Exception {
4232 unsafeReq.setHeader("Location","http://foo.example.com/content");
4233 testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq);
4234 }
4235
4236 @Test
4237 public void testPutInvalidatesCacheForThatUriInContentLocationHeader() throws Exception {
4238 final ClassicHttpRequest req2 = makeRequestWithBody("PUT","/");
4239 testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req2);
4240 }
4241
4242 @Test
4243 public void testPutInvalidatesCacheForThatUriInLocationHeader() throws Exception {
4244 final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
4245 testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req);
4246 }
4247
4248 @Test
4249 public void testPutInvalidatesCacheForThatUriInRelativeContentLocationHeader() throws Exception {
4250 final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
4251 testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req);
4252 }
4253
4254 @Test
4255 public void testDeleteInvalidatesCacheForThatUriInContentLocationHeader() throws Exception {
4256 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4257 testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req);
4258 }
4259
4260 @Test
4261 public void testDeleteInvalidatesCacheForThatUriInRelativeContentLocationHeader() throws Exception {
4262 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4263 testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req);
4264 }
4265
4266 @Test
4267 public void testDeleteInvalidatesCacheForThatUriInLocationHeader() throws Exception {
4268 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4269 testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req);
4270 }
4271
4272 @Test
4273 public void testPostInvalidatesCacheForThatUriInContentLocationHeader() throws Exception {
4274 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4275 testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req);
4276 }
4277
4278 @Test
4279 public void testPostInvalidatesCacheForThatUriInLocationHeader() throws Exception {
4280 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4281 testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req);
4282 }
4283
4284 @Test
4285 public void testPostInvalidatesCacheForRelativeUriInContentLocationHeader() throws Exception {
4286 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4287 testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req);
4288 }
4289
4290
4291
4292
4293
4294
4295
4296 protected void testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(
4297 final ClassicHttpRequest unsafeReq) throws Exception {
4298
4299 final HttpHost otherHost = new HttpHost("bar.example.com", 80);
4300 final HttpRoute otherRoute = new HttpRoute(otherHost);
4301 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/content");
4302 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4303 resp1.setHeader("Cache-Control","public, max-age=3600");
4304
4305 backendExpectsAnyRequestAndReturn(resp1);
4306
4307 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
4308
4309 backendExpectsAnyRequestAndReturn(resp2);
4310
4311 final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content");
4312
4313 replayMocks();
4314 execute(req1);
4315 execute(unsafeReq);
4316 execute(req3);
4317 verifyMocks();
4318 }
4319
4320 protected void testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(
4321 final ClassicHttpRequest unsafeReq) throws Exception {
4322 unsafeReq.setHeader("Content-Location","http://bar.example.com/content");
4323 testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(unsafeReq);
4324 }
4325
4326 protected void testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(
4327 final ClassicHttpRequest unsafeReq) throws Exception {
4328 unsafeReq.setHeader("Location","http://bar.example.com/content");
4329 testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(unsafeReq);
4330 }
4331
4332 protected ClassicHttpRequest makeRequestWithBody(final String method, final String requestUri) {
4333 final ClassicHttpRequest req =
4334 new BasicClassicHttpRequest(method, requestUri);
4335 final int nbytes = 128;
4336 req.setEntity(HttpTestUtils.makeBody(nbytes));
4337 req.setHeader("Content-Length",""+nbytes);
4338 return req;
4339 }
4340
4341 @Test
4342 public void testPutDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception {
4343 final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
4344 testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req);
4345 }
4346
4347 @Test
4348 public void testPutDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception {
4349 final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
4350 testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req);
4351 }
4352
4353 @Test
4354 public void testPostDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception {
4355 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4356 testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req);
4357 }
4358
4359 @Test
4360 public void testPostDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception {
4361 final ClassicHttpRequest req = makeRequestWithBody("POST","/");
4362 testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req);
4363 }
4364
4365 @Test
4366 public void testDeleteDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception {
4367 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4368 testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req);
4369 }
4370
4371 @Test
4372 public void testDeleteDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception {
4373 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4374 testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req);
4375 }
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386 private void testRequestIsWrittenThroughToOrigin(final ClassicHttpRequest req) throws Exception {
4387 final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
4388 final ClassicHttpRequest wrapper = req;
4389 EasyMock.expect(
4390 mockExecChain.proceed(
4391 eqRequest(wrapper),
4392 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp);
4393
4394 replayMocks();
4395 execute(wrapper);
4396 verifyMocks();
4397 }
4398
4399 @Test @Ignore
4400 public void testOPTIONSRequestsAreWrittenThroughToOrigin() throws Exception {
4401 final ClassicHttpRequest req = new BasicClassicHttpRequest("OPTIONS","*");
4402 testRequestIsWrittenThroughToOrigin(req);
4403 }
4404
4405 @Test
4406 public void testPOSTRequestsAreWrittenThroughToOrigin() throws Exception {
4407 final ClassicHttpRequest req = new BasicClassicHttpRequest("POST","/");
4408 req.setEntity(HttpTestUtils.makeBody(128));
4409 req.setHeader("Content-Length","128");
4410 testRequestIsWrittenThroughToOrigin(req);
4411 }
4412
4413 @Test
4414 public void testPUTRequestsAreWrittenThroughToOrigin() throws Exception {
4415 final ClassicHttpRequest req = new BasicClassicHttpRequest("PUT","/");
4416 req.setEntity(HttpTestUtils.makeBody(128));
4417 req.setHeader("Content-Length","128");
4418 testRequestIsWrittenThroughToOrigin(req);
4419 }
4420
4421 @Test
4422 public void testDELETERequestsAreWrittenThroughToOrigin() throws Exception {
4423 final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/");
4424 testRequestIsWrittenThroughToOrigin(req);
4425 }
4426
4427 @Test
4428 public void testTRACERequestsAreWrittenThroughToOrigin() throws Exception {
4429 final ClassicHttpRequest req = new BasicClassicHttpRequest("TRACE","/");
4430 testRequestIsWrittenThroughToOrigin(req);
4431 }
4432
4433 @Test
4434 public void testCONNECTRequestsAreWrittenThroughToOrigin() throws Exception {
4435 final ClassicHttpRequest req = new BasicClassicHttpRequest("CONNECT","/");
4436 testRequestIsWrittenThroughToOrigin(req);
4437 }
4438
4439 @Test
4440 public void testUnknownMethodRequestsAreWrittenThroughToOrigin() throws Exception {
4441 final ClassicHttpRequest req = new BasicClassicHttpRequest("UNKNOWN","/");
4442 testRequestIsWrittenThroughToOrigin(req);
4443 }
4444
4445
4446
4447
4448
4449
4450
4451
4452 @Test
4453 public void testTransmitsAgeHeaderIfIncomingAgeHeaderTooBig() throws Exception {
4454 final String reallyOldAge = "1" + Long.MAX_VALUE;
4455 originResponse.setHeader("Age",reallyOldAge);
4456
4457 backendExpectsAnyRequest().andReturn(originResponse);
4458
4459 replayMocks();
4460 final ClassicHttpResponse result = execute(request);
4461 verifyMocks();
4462
4463 Assert.assertEquals("2147483648",
4464 result.getFirstHeader("Age").getValue());
4465 }
4466
4467
4468
4469
4470
4471
4472
4473 @Test
4474 public void testDoesNotModifyAllowHeaderWithUnknownMethods() throws Exception {
4475 final String allowHeaderValue = "GET, HEAD, FOOBAR";
4476 originResponse.setHeader("Allow",allowHeaderValue);
4477 backendExpectsAnyRequest().andReturn(originResponse);
4478 replayMocks();
4479 final ClassicHttpResponse result = execute(request);
4480 verifyMocks();
4481 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse,"Allow"),
4482 HttpTestUtils.getCanonicalHeaderValue(result, "Allow"));
4483 }
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510 protected void testSharedCacheRevalidatesAuthorizedResponse(
4511 final ClassicHttpResponse authorizedResponse, final int minTimes, final int maxTimes) throws Exception {
4512 if (config.isSharedCache()) {
4513 final String authorization = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=";
4514 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4515 req1.setHeader("Authorization",authorization);
4516
4517 backendExpectsAnyRequestAndReturn(authorizedResponse);
4518
4519 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4520 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4521 resp2.setHeader("Cache-Control","max-age=3600");
4522
4523 if (maxTimes > 0) {
4524
4525 backendExpectsAnyRequest().andReturn(resp2).times(minTimes,maxTimes);
4526 }
4527
4528 replayMocks();
4529 execute(req1);
4530 execute(req2);
4531 verifyMocks();
4532 }
4533 }
4534
4535 @Test
4536 public void testSharedCacheMustNotNormallyCacheAuthorizedResponses() throws Exception {
4537 final ClassicHttpResponse resp = HttpTestUtils.make200Response();
4538 resp.setHeader("Cache-Control","max-age=3600");
4539 resp.setHeader("ETag","\"etag\"");
4540 testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1);
4541 }
4542
4543 @Test
4544 public void testSharedCacheMayCacheAuthorizedResponsesWithSMaxAgeHeader() throws Exception {
4545 final ClassicHttpResponse resp = HttpTestUtils.make200Response();
4546 resp.setHeader("Cache-Control","s-maxage=3600");
4547 resp.setHeader("ETag","\"etag\"");
4548 testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
4549 }
4550
4551 @Test
4552 public void testSharedCacheMustRevalidateAuthorizedResponsesWhenSMaxAgeIsZero() throws Exception {
4553 final ClassicHttpResponse resp = HttpTestUtils.make200Response();
4554 resp.setHeader("Cache-Control","s-maxage=0");
4555 resp.setHeader("ETag","\"etag\"");
4556 testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1);
4557 }
4558
4559 @Test
4560 public void testSharedCacheMayCacheAuthorizedResponsesWithMustRevalidate() throws Exception {
4561 final ClassicHttpResponse resp = HttpTestUtils.make200Response();
4562 resp.setHeader("Cache-Control","must-revalidate");
4563 resp.setHeader("ETag","\"etag\"");
4564 testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
4565 }
4566
4567 @Test
4568 public void testSharedCacheMayCacheAuthorizedResponsesWithCacheControlPublic() throws Exception {
4569 final ClassicHttpResponse resp = HttpTestUtils.make200Response();
4570 resp.setHeader("Cache-Control","public");
4571 testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
4572 }
4573
4574 protected void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(
4575 final ClassicHttpResponse authorizedResponse) throws Exception {
4576 if (config.isSharedCache()) {
4577 final String authorization1 = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=";
4578 final String authorization2 = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Qy";
4579
4580 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4581 req1.setHeader("Authorization",authorization1);
4582
4583 backendExpectsAnyRequestAndReturn(authorizedResponse);
4584
4585 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4586 req2.setHeader("Authorization",authorization2);
4587
4588 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4589
4590 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
4591 EasyMock.expect(
4592 mockExecChain.proceed(
4593 EasyMock.capture(cap),
4594 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
4595
4596 replayMocks();
4597 execute(req1);
4598 execute(req2);
4599 verifyMocks();
4600
4601 final ClassicHttpRequest captured = cap.getValue();
4602 Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(req2, "Authorization"),
4603 HttpTestUtils.getCanonicalHeaderValue(captured, "Authorization"));
4604 }
4605 }
4606
4607 @Test
4608 public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithSMaxAge() throws Exception {
4609 final Date now = new Date();
4610 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4611 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4612 resp1.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
4613 resp1.setHeader("ETag","\"etag\"");
4614 resp1.setHeader("Cache-Control","s-maxage=5");
4615
4616 testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1);
4617 }
4618
4619 @Test
4620 public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithMustRevalidate() throws Exception {
4621 final Date now = new Date();
4622 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4623 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4624 resp1.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
4625 resp1.setHeader("ETag","\"etag\"");
4626 resp1.setHeader("Cache-Control","maxage=5, must-revalidate");
4627
4628 testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1);
4629 }
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644 @Test
4645 public void testWarning110IsAddedToStaleResponses() throws Exception {
4646 final Date now = new Date();
4647 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4648 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4649 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4650 resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
4651 resp1.setHeader("Cache-Control","max-age=5");
4652 resp1.setHeader("Etag","\"etag\"");
4653
4654 backendExpectsAnyRequestAndReturn(resp1);
4655
4656 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4657 req2.setHeader("Cache-Control","max-stale=60");
4658 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4659
4660 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
4661 EasyMock.expect(
4662 mockExecChain.proceed(
4663 EasyMock.capture(cap),
4664 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
4665
4666 replayMocks();
4667 execute(req1);
4668 final ClassicHttpResponse result = execute(req2);
4669 verifyMocks();
4670
4671 if (!cap.hasCaptured()) {
4672 boolean found110Warning = false;
4673 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.WARNING);
4674 while (it.hasNext()) {
4675 final HeaderElement elt = it.next();
4676 final String[] parts = elt.getName().split("\\s");
4677 if ("110".equals(parts[0])) {
4678 found110Warning = true;
4679 break;
4680 }
4681 }
4682 Assert.assertTrue(found110Warning);
4683 }
4684 }
4685
4686
4687
4688
4689
4690
4691 @Test
4692 public void testDoesNotTransmitNoCacheDirectivesWithFieldsDownstream() throws Exception {
4693 request.setHeader("Cache-Control","no-cache=\"X-Field\"");
4694 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
4695 EasyMock.expect(mockExecChain.proceed(
4696 EasyMock.capture(cap),
4697 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(0,1);
4698
4699 replayMocks();
4700 try {
4701 execute(request);
4702 } catch (final ClientProtocolException acceptable) {
4703 }
4704 verifyMocks();
4705
4706 if (cap.hasCaptured()) {
4707 final ClassicHttpRequest captured = cap.getValue();
4708 final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
4709 while (it.hasNext()) {
4710 final HeaderElement elt = it.next();
4711 if ("no-cache".equals(elt.getName())) {
4712 Assert.assertNull(elt.getValue());
4713 }
4714 }
4715 }
4716 }
4717
4718
4719
4720
4721
4722
4723
4724 protected void testCacheIsNotUsedWhenRespondingToRequest(final ClassicHttpRequest req) throws Exception {
4725 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4726 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4727 resp1.setHeader("Etag","\"etag\"");
4728 resp1.setHeader("Cache-Control","max-age=3600");
4729
4730 backendExpectsAnyRequestAndReturn(resp1);
4731
4732 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4733 resp2.setHeader("Etag","\"etag2\"");
4734 resp2.setHeader("Cache-Control","max-age=1200");
4735
4736 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
4737 EasyMock.expect(mockExecChain.proceed(
4738 EasyMock.capture(cap),
4739 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
4740
4741 replayMocks();
4742 execute(req1);
4743 final ClassicHttpResponse result = execute(req);
4744 verifyMocks();
4745
4746 Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
4747 final ClassicHttpRequest captured = cap.getValue();
4748 Assert.assertTrue(HttpTestUtils.equivalent(req, captured));
4749 }
4750
4751 @Test
4752 public void testCacheIsNotUsedWhenRespondingToRequestWithCacheControlNoCache() throws Exception {
4753 final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/");
4754 req.setHeader("Cache-Control","no-cache");
4755 testCacheIsNotUsedWhenRespondingToRequest(req);
4756 }
4757
4758 @Test
4759 public void testCacheIsNotUsedWhenRespondingToRequestWithPragmaNoCache() throws Exception {
4760 final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/");
4761 req.setHeader("Pragma","no-cache");
4762 testCacheIsNotUsedWhenRespondingToRequest(req);
4763 }
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774 protected void testStaleCacheResponseMustBeRevalidatedWithOrigin(
4775 final ClassicHttpResponse staleResponse) throws Exception {
4776 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4777
4778 backendExpectsAnyRequestAndReturn(staleResponse);
4779
4780 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4781 req2.setHeader("Cache-Control","max-stale=3600");
4782 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4783 resp2.setHeader("ETag","\"etag2\"");
4784 resp2.setHeader("Cache-Control","max-age=5, must-revalidate");
4785
4786 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
4787
4788 EasyMock.expect(
4789 mockExecChain.proceed(
4790 EasyMock.capture(cap),
4791 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
4792
4793 replayMocks();
4794 execute(req1);
4795 execute(req2);
4796 verifyMocks();
4797
4798 final ClassicHttpRequest reval = cap.getValue();
4799 boolean foundMaxAge0 = false;
4800 final Iterator<HeaderElement> it = MessageSupport.iterate(reval, HttpHeaders.CACHE_CONTROL);
4801 while (it.hasNext()) {
4802 final HeaderElement elt = it.next();
4803 if ("max-age".equalsIgnoreCase(elt.getName())
4804 && "0".equals(elt.getValue())) {
4805 foundMaxAge0 = true;
4806 }
4807 }
4808 Assert.assertTrue(foundMaxAge0);
4809 }
4810
4811 @Test
4812 public void testStaleEntryWithMustRevalidateIsNotUsedWithoutRevalidatingWithOrigin() throws Exception {
4813 final ClassicHttpResponse response = HttpTestUtils.make200Response();
4814 final Date now = new Date();
4815 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4816 response.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
4817 response.setHeader("ETag","\"etag1\"");
4818 response.setHeader("Cache-Control","max-age=5, must-revalidate");
4819
4820 testStaleCacheResponseMustBeRevalidatedWithOrigin(response);
4821 }
4822
4823
4824
4825
4826
4827
4828 protected void testGenerates504IfCannotRevalidateStaleResponse(
4829 final ClassicHttpResponse staleResponse) throws Exception {
4830 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4831
4832 backendExpectsAnyRequestAndReturn(staleResponse);
4833
4834 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4835
4836 backendExpectsAnyRequest().andThrow(new SocketTimeoutException());
4837
4838 replayMocks();
4839 execute(req1);
4840 final ClassicHttpResponse result = execute(req2);
4841 verifyMocks();
4842
4843 Assert.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
4844 result.getCode());
4845 }
4846
4847 @Test
4848 public void testGenerates504IfCannotRevalidateAMustRevalidateEntry() throws Exception {
4849 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4850 final Date now = new Date();
4851 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4852 resp1.setHeader("ETag","\"etag\"");
4853 resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
4854 resp1.setHeader("Cache-Control","max-age=5,must-revalidate");
4855
4856 testGenerates504IfCannotRevalidateStaleResponse(resp1);
4857 }
4858
4859
4860
4861
4862
4863
4864
4865 @Test
4866 public void testStaleEntryWithProxyRevalidateOnSharedCacheIsNotUsedWithoutRevalidatingWithOrigin() throws Exception {
4867 if (config.isSharedCache()) {
4868 final ClassicHttpResponse response = HttpTestUtils.make200Response();
4869 final Date now = new Date();
4870 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4871 response.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
4872 response.setHeader("ETag","\"etag1\"");
4873 response.setHeader("Cache-Control","max-age=5, proxy-revalidate");
4874
4875 testStaleCacheResponseMustBeRevalidatedWithOrigin(response);
4876 }
4877 }
4878
4879 @Test
4880 public void testGenerates504IfSharedCacheCannotRevalidateAProxyRevalidateEntry() throws Exception {
4881 if (config.isSharedCache()) {
4882 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4883 final Date now = new Date();
4884 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
4885 resp1.setHeader("ETag","\"etag\"");
4886 resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
4887 resp1.setHeader("Cache-Control","max-age=5,proxy-revalidate");
4888
4889 testGenerates504IfCannotRevalidateStaleResponse(resp1);
4890 }
4891 }
4892
4893
4894
4895
4896
4897
4898
4899 @Test
4900 public void testCacheControlPrivateIsNotCacheableBySharedCache() throws Exception {
4901 if (config.isSharedCache()) {
4902 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4903 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4904 resp1.setHeader("Cache-Control","private,max-age=3600");
4905
4906 backendExpectsAnyRequestAndReturn(resp1);
4907
4908 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4909 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4910
4911 backendExpectsAnyRequestAndReturn(resp2);
4912
4913 replayMocks();
4914 execute(req1);
4915 execute(req2);
4916 verifyMocks();
4917 }
4918 }
4919
4920 @Test
4921 public void testCacheControlPrivateOnFieldIsNotReturnedBySharedCache() throws Exception {
4922 if (config.isSharedCache()) {
4923 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4924 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4925 resp1.setHeader("X-Personal","stuff");
4926 resp1.setHeader("Cache-Control","private=\"X-Personal\",s-maxage=3600");
4927
4928 backendExpectsAnyRequestAndReturn(resp1);
4929
4930 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4931 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4932
4933
4934 backendExpectsAnyRequestAndReturn(resp2).times(0,1);
4935
4936 replayMocks();
4937 execute(req1);
4938 final ClassicHttpResponse result = execute(req2);
4939 verifyMocks();
4940 Assert.assertNull(result.getFirstHeader("X-Personal"));
4941 }
4942 }
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952 @Test
4953 public void testNoCacheCannotSatisfyASubsequentRequestWithoutRevalidation() throws Exception {
4954 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4955 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4956 resp1.setHeader("ETag","\"etag\"");
4957 resp1.setHeader("Cache-Control","no-cache");
4958
4959 backendExpectsAnyRequestAndReturn(resp1);
4960
4961 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4962 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4963
4964
4965 backendExpectsAnyRequestAndReturn(resp2);
4966
4967 replayMocks();
4968 execute(req1);
4969 execute(req2);
4970 verifyMocks();
4971 }
4972
4973 @Test
4974 public void testNoCacheCannotSatisfyASubsequentRequestWithoutRevalidationEvenWithContraryIndications() throws Exception {
4975 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
4976 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
4977 resp1.setHeader("ETag","\"etag\"");
4978 resp1.setHeader("Cache-Control","no-cache,s-maxage=3600");
4979
4980 backendExpectsAnyRequestAndReturn(resp1);
4981
4982 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
4983 req2.setHeader("Cache-Control","max-stale=7200");
4984 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
4985
4986
4987 backendExpectsAnyRequestAndReturn(resp2);
4988
4989 replayMocks();
4990 execute(req1);
4991 execute(req2);
4992 verifyMocks();
4993 }
4994
4995
4996
4997
4998
4999
5000
5001 @Test
5002 public void testNoCacheOnFieldIsNotReturnedWithoutRevalidation() throws Exception {
5003 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
5004 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
5005 resp1.setHeader("ETag","\"etag\"");
5006 resp1.setHeader("X-Stuff","things");
5007 resp1.setHeader("Cache-Control","no-cache=\"X-Stuff\", max-age=3600");
5008
5009 backendExpectsAnyRequestAndReturn(resp1);
5010
5011 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
5012 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
5013 resp2.setHeader("ETag","\"etag\"");
5014 resp2.setHeader("X-Stuff","things");
5015 resp2.setHeader("Cache-Control","no-cache=\"X-Stuff\",max-age=3600");
5016
5017 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
5018 EasyMock.expect(
5019 mockExecChain.proceed(
5020 EasyMock.capture(cap),
5021 EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
5022
5023 replayMocks();
5024 execute(req1);
5025 final ClassicHttpResponse result = execute(req2);
5026 verifyMocks();
5027
5028 if (!cap.hasCaptured()) {
5029 Assert.assertNull(result.getFirstHeader("X-Stuff"));
5030 }
5031 }
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048 @Test
5049 public void testNoStoreOnRequestIsNotStoredInCache() throws Exception {
5050 emptyMockCacheExpectsNoPuts();
5051 request.setHeader("Cache-Control","no-store");
5052 backendExpectsAnyRequest().andReturn(originResponse);
5053
5054 replayMocks();
5055 execute(request);
5056 verifyMocks();
5057 }
5058
5059 @Test
5060 public void testNoStoreOnRequestIsNotStoredInCacheEvenIfResponseMarkedCacheable() throws Exception {
5061 emptyMockCacheExpectsNoPuts();
5062 request.setHeader("Cache-Control","no-store");
5063 originResponse.setHeader("Cache-Control","max-age=3600");
5064 backendExpectsAnyRequest().andReturn(originResponse);
5065
5066 replayMocks();
5067 execute(request);
5068 verifyMocks();
5069 }
5070
5071 @Test
5072 public void testNoStoreOnResponseIsNotStoredInCache() throws Exception {
5073 emptyMockCacheExpectsNoPuts();
5074 originResponse.setHeader("Cache-Control","no-store");
5075 backendExpectsAnyRequest().andReturn(originResponse);
5076
5077 replayMocks();
5078 execute(request);
5079 verifyMocks();
5080 }
5081
5082 @Test
5083 public void testNoStoreOnResponseIsNotStoredInCacheEvenWithContraryIndicators() throws Exception {
5084 emptyMockCacheExpectsNoPuts();
5085 originResponse.setHeader("Cache-Control","no-store,max-age=3600");
5086 backendExpectsAnyRequest().andReturn(originResponse);
5087
5088 replayMocks();
5089 execute(request);
5090 verifyMocks();
5091 }
5092
5093
5094
5095
5096
5097
5098 @Test
5099 public void testOrderOfMultipleContentEncodingHeaderValuesIsPreserved() throws Exception {
5100 originResponse.addHeader("Content-Encoding","gzip");
5101 originResponse.addHeader("Content-Encoding","deflate");
5102 backendExpectsAnyRequest().andReturn(originResponse);
5103
5104 replayMocks();
5105 final ClassicHttpResponse result = execute(request);
5106 verifyMocks();
5107 int total_encodings = 0;
5108 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
5109 while (it.hasNext()) {
5110 final HeaderElement elt = it.next();
5111 switch(total_encodings) {
5112 case 0:
5113 Assert.assertEquals("gzip", elt.getName());
5114 break;
5115 case 1:
5116 Assert.assertEquals("deflate", elt.getName());
5117 break;
5118 default:
5119 Assert.fail("too many encodings");
5120 }
5121 total_encodings++;
5122 }
5123 Assert.assertEquals(2, total_encodings);
5124 }
5125
5126 @Test
5127 public void testOrderOfMultipleParametersInContentEncodingHeaderIsPreserved() throws Exception {
5128 originResponse.addHeader("Content-Encoding","gzip,deflate");
5129 backendExpectsAnyRequest().andReturn(originResponse);
5130
5131 replayMocks();
5132 final ClassicHttpResponse result = execute(request);
5133 verifyMocks();
5134 int total_encodings = 0;
5135 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
5136 while (it.hasNext()) {
5137 final HeaderElement elt = it.next();
5138 switch(total_encodings) {
5139 case 0:
5140 Assert.assertEquals("gzip", elt.getName());
5141 break;
5142 case 1:
5143 Assert.assertEquals("deflate", elt.getName());
5144 break;
5145 default:
5146 Assert.fail("too many encodings");
5147 }
5148 total_encodings++;
5149 }
5150 Assert.assertEquals(2, total_encodings);
5151 }
5152
5153
5154
5155
5156
5157
5158
5159 @Test
5160 public void testCacheDoesNotAssumeContentLocationHeaderIndicatesAnotherCacheableResource() throws Exception {
5161 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/foo");
5162 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
5163 resp1.setHeader("Cache-Control","public,max-age=3600");
5164 resp1.setHeader("Etag","\"etag\"");
5165 resp1.setHeader("Content-Location","http://foo.example.com/bar");
5166
5167 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/bar");
5168 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
5169 resp2.setHeader("Cache-Control","public,max-age=3600");
5170 resp2.setHeader("Etag","\"etag\"");
5171
5172 backendExpectsAnyRequestAndReturn(resp1);
5173 backendExpectsAnyRequestAndReturn(resp2);
5174
5175 replayMocks();
5176 execute(req1);
5177 execute(req2);
5178 verifyMocks();
5179 }
5180
5181
5182
5183
5184
5185
5186
5187 @Test
5188 public void testCachedResponsesWithMissingDateHeadersShouldBeAssignedOne() throws Exception {
5189 originResponse.removeHeaders("Date");
5190 originResponse.setHeader("Cache-Control","public");
5191 originResponse.setHeader("ETag","\"etag\"");
5192
5193 backendExpectsAnyRequest().andReturn(originResponse);
5194
5195 replayMocks();
5196 final ClassicHttpResponse result = execute(request);
5197 verifyMocks();
5198 Assert.assertNotNull(result.getFirstHeader("Date"));
5199 }
5200
5201
5202
5203
5204
5205
5206
5207
5208 private void testInvalidExpiresHeaderIsTreatedAsStale(
5209 final String expiresHeader) throws Exception {
5210 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
5211 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
5212 resp1.setHeader("Cache-Control","public");
5213 resp1.setHeader("ETag","\"etag\"");
5214 resp1.setHeader("Expires", expiresHeader);
5215
5216 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
5217 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
5218
5219 backendExpectsAnyRequestAndReturn(resp1);
5220
5221 backendExpectsAnyRequestAndReturn(resp2);
5222
5223 replayMocks();
5224 execute(req1);
5225 execute(req2);
5226 verifyMocks();
5227 }
5228
5229 @Test
5230 public void testMalformedExpiresHeaderIsTreatedAsStale() throws Exception {
5231 testInvalidExpiresHeaderIsTreatedAsStale("garbage");
5232 }
5233
5234 @Test
5235 public void testExpiresZeroHeaderIsTreatedAsStale() throws Exception {
5236 testInvalidExpiresHeaderIsTreatedAsStale("0");
5237 }
5238
5239
5240
5241
5242
5243
5244 @Test
5245 public void testExpiresHeaderEqualToDateHeaderIsTreatedAsStale() throws Exception {
5246 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
5247 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
5248 resp1.setHeader("Cache-Control","public");
5249 resp1.setHeader("ETag","\"etag\"");
5250 resp1.setHeader("Expires", resp1.getFirstHeader("Date").getValue());
5251
5252 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
5253 final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
5254
5255 backendExpectsAnyRequestAndReturn(resp1);
5256
5257 backendExpectsAnyRequestAndReturn(resp2);
5258
5259 replayMocks();
5260 execute(req1);
5261 execute(req2);
5262 verifyMocks();
5263 }
5264
5265
5266
5267
5268
5269
5270 @Test
5271 public void testDoesNotModifyServerResponseHeader() throws Exception {
5272 final String server = "MockServer/1.0";
5273 originResponse.setHeader("Server", server);
5274
5275 backendExpectsAnyRequest().andReturn(originResponse);
5276
5277 replayMocks();
5278 final ClassicHttpResponse result = execute(request);
5279 verifyMocks();
5280 Assert.assertEquals(server, result.getFirstHeader("Server").getValue());
5281 }
5282
5283
5284
5285
5286
5287
5288 @Test
5289 public void testOrderOfMultipleTransferEncodingHeadersIsPreserved() throws Exception {
5290 originResponse.addHeader("Transfer-Encoding","chunked");
5291 originResponse.addHeader("Transfer-Encoding","x-transfer");
5292
5293 backendExpectsAnyRequest().andReturn(originResponse);
5294
5295 replayMocks();
5296 final ClassicHttpResponse result = execute(request);
5297 verifyMocks();
5298 int transfer_encodings = 0;
5299 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING);
5300 while (it.hasNext()) {
5301 final HeaderElement elt = it.next();
5302 switch(transfer_encodings) {
5303 case 0:
5304 Assert.assertEquals("chunked",elt.getName());
5305 break;
5306 case 1:
5307 Assert.assertEquals("x-transfer",elt.getName());
5308 break;
5309 default:
5310 Assert.fail("too many transfer encodings");
5311 }
5312 transfer_encodings++;
5313 }
5314 Assert.assertEquals(2, transfer_encodings);
5315 }
5316
5317 @Test
5318 public void testOrderOfMultipleTransferEncodingsInSingleHeadersIsPreserved() throws Exception {
5319 originResponse.addHeader("Transfer-Encoding","chunked, x-transfer");
5320
5321 backendExpectsAnyRequest().andReturn(originResponse);
5322
5323 replayMocks();
5324 final ClassicHttpResponse result = execute(request);
5325 verifyMocks();
5326 int transfer_encodings = 0;
5327 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING);
5328 while (it.hasNext()) {
5329 final HeaderElement elt = it.next();
5330 switch(transfer_encodings) {
5331 case 0:
5332 Assert.assertEquals("chunked",elt.getName());
5333 break;
5334 case 1:
5335 Assert.assertEquals("x-transfer",elt.getName());
5336 break;
5337 default:
5338 Assert.fail("too many transfer encodings");
5339 }
5340 transfer_encodings++;
5341 }
5342 Assert.assertEquals(2, transfer_encodings);
5343 }
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353 @Test
5354 public void testVaryStarIsNotGeneratedByProxy() throws Exception {
5355 request.setHeader("User-Agent","my-agent/1.0");
5356 originResponse.setHeader("Cache-Control","public, max-age=3600");
5357 originResponse.setHeader("Vary","User-Agent");
5358 originResponse.setHeader("ETag","\"etag\"");
5359
5360 backendExpectsAnyRequest().andReturn(originResponse);
5361
5362 replayMocks();
5363 final ClassicHttpResponse result = execute(request);
5364 verifyMocks();
5365 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.VARY);
5366 while (it.hasNext()) {
5367 final HeaderElement elt = it.next();
5368 Assert.assertFalse("*".equals(elt.getName()));
5369 }
5370 }
5371
5372
5373
5374
5375
5376
5377
5378
5379 @Test
5380 public void testProperlyFormattedViaHeaderIsAddedToRequests() throws Exception {
5381 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
5382 request.removeHeaders("Via");
5383 EasyMock.expect(
5384 mockExecChain.proceed(
5385 EasyMock.capture(cap),
5386 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
5387
5388 replayMocks();
5389 execute(request);
5390 verifyMocks();
5391
5392 final ClassicHttpRequest captured = cap.getValue();
5393 final String via = captured.getFirstHeader("Via").getValue();
5394 assertValidViaHeader(via);
5395 }
5396
5397 @Test
5398 public void testProperlyFormattedViaHeaderIsAddedToResponses() throws Exception {
5399 originResponse.removeHeaders("Via");
5400 backendExpectsAnyRequest().andReturn(originResponse);
5401 replayMocks();
5402 final ClassicHttpResponse result = execute(request);
5403 verifyMocks();
5404 assertValidViaHeader(result.getFirstHeader("Via").getValue());
5405 }
5406
5407
5408 private void assertValidViaHeader(final String via) {
5409
5410
5411
5412
5413
5414
5415
5416 final String[] parts = via.split("\\s+");
5417 Assert.assertTrue(parts.length >= 2);
5418
5419
5420 final String receivedProtocol = parts[0];
5421 final String[] protocolParts = receivedProtocol.split("/");
5422 Assert.assertTrue(protocolParts.length >= 1);
5423 Assert.assertTrue(protocolParts.length <= 2);
5424
5425 final String tokenRegexp = "[^\\p{Cntrl}()<>@,;:\\\\\"/\\[\\]?={} \\t]+";
5426 for(final String protocolPart : protocolParts) {
5427 Assert.assertTrue(Pattern.matches(tokenRegexp, protocolPart));
5428 }
5429
5430
5431 if (!Pattern.matches(tokenRegexp, parts[1])) {
5432
5433 new HttpHost(parts[1]);
5434 }
5435
5436
5437 if (parts.length > 2) {
5438 final StringBuilder buf = new StringBuilder(parts[2]);
5439 for(int i=3; i<parts.length; i++) {
5440 buf.append(" "); buf.append(parts[i]);
5441 }
5442 Assert.assertTrue(isValidComment(buf.toString()));
5443 }
5444 }
5445
5446 private boolean isValidComment(final String s) {
5447 final String leafComment = "^\\(([^\\p{Cntrl}()]|\\\\\\p{ASCII})*\\)$";
5448 final String nestedPrefix = "^\\(([^\\p{Cntrl}()]|\\\\\\p{ASCII})*\\(";
5449 final String nestedSuffix = "\\)([^\\p{Cntrl}()]|\\\\\\p{ASCII})*\\)$";
5450
5451 if (Pattern.matches(leafComment,s)) {
5452 return true;
5453 }
5454 final Matcher pref = Pattern.compile(nestedPrefix).matcher(s);
5455 final Matcher suff = Pattern.compile(nestedSuffix).matcher(s);
5456 if (!pref.find()) {
5457 return false;
5458 }
5459 if (!suff.find()) {
5460 return false;
5461 }
5462 return isValidComment(s.substring(pref.end() - 1, suff.start() + 1));
5463 }
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476 @Test
5477 public void testViaHeaderOnRequestProperlyRecordsClientProtocol() throws Exception {
5478 final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/");
5479 originalRequest.setVersion(HttpVersion.HTTP_1_0);
5480 request = originalRequest;
5481 request.removeHeaders("Via");
5482 final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
5483 EasyMock.expect(
5484 mockExecChain.proceed(
5485 EasyMock.capture(cap),
5486 EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
5487
5488 replayMocks();
5489 execute(request);
5490 verifyMocks();
5491
5492 final ClassicHttpRequest captured = cap.getValue();
5493 final String via = captured.getFirstHeader("Via").getValue();
5494 final String protocol = via.split("\\s+")[0];
5495 final String[] protoParts = protocol.split("/");
5496 if (protoParts.length > 1) {
5497 Assert.assertTrue("http".equalsIgnoreCase(protoParts[0]));
5498 }
5499 Assert.assertEquals("1.0",protoParts[protoParts.length-1]);
5500 }
5501
5502 @Test
5503 public void testViaHeaderOnResponseProperlyRecordsOriginProtocol() throws Exception {
5504
5505 originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
5506 originResponse.setVersion(HttpVersion.HTTP_1_0);
5507
5508 backendExpectsAnyRequest().andReturn(originResponse);
5509
5510 replayMocks();
5511 final ClassicHttpResponse result = execute(request);
5512 verifyMocks();
5513
5514 final String via = result.getFirstHeader("Via").getValue();
5515 final String protocol = via.split("\\s+")[0];
5516 final String[] protoParts = protocol.split("/");
5517 Assert.assertTrue(protoParts.length >= 1);
5518 Assert.assertTrue(protoParts.length <= 2);
5519 if (protoParts.length > 1) {
5520 Assert.assertTrue("http".equalsIgnoreCase(protoParts[0]));
5521 }
5522 Assert.assertEquals("1.0", protoParts[protoParts.length - 1]);
5523 }
5524
5525
5526
5527
5528
5529
5530 @Test
5531 public void testRetainsWarningHeadersReceivedFromUpstream() throws Exception {
5532 originResponse.removeHeaders("Warning");
5533 final String warning = "199 fred \"misc\"";
5534 originResponse.addHeader("Warning", warning);
5535 backendExpectsAnyRequest().andReturn(originResponse);
5536
5537 replayMocks();
5538 final ClassicHttpResponse result = execute(request);
5539 verifyMocks();
5540 Assert.assertEquals(warning,
5541 result.getFirstHeader("Warning").getValue());
5542 }
5543
5544
5545
5546
5547
5548
5549
5550
5551 @Test
5552 public void testUpdatesWarningHeadersOnValidation() throws Exception {
5553 final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
5554 final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
5555
5556 final Date now = new Date();
5557 final Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L);
5558 final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
5559 resp1.setHeader("Date", DateUtils.formatDate(twentySecondsAgo));
5560 resp1.setHeader("Cache-Control","public,max-age=5");
5561 resp1.setHeader("ETag", "\"etag1\"");
5562 final String oldWarning = "113 wilma \"stale\"";
5563 resp1.setHeader("Warning", oldWarning);
5564
5565 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
5566 final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
5567 resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
5568 resp2.setHeader("ETag", "\"etag1\"");
5569 final String newWarning = "113 betty \"stale too\"";
5570 resp2.setHeader("Warning", newWarning);
5571
5572 backendExpectsAnyRequestAndReturn(resp1);
5573 backendExpectsAnyRequestAndReturn(resp2);
5574
5575 replayMocks();
5576 execute(req1);
5577 final ClassicHttpResponse result = execute(req2);
5578 verifyMocks();
5579
5580 boolean oldWarningFound = false;
5581 boolean newWarningFound = false;
5582 for(final Header h : result.getHeaders("Warning")) {
5583 for(final String warnValue : h.getValue().split("\\s*,\\s*")) {
5584 if (oldWarning.equals(warnValue)) {
5585 oldWarningFound = true;
5586 } else if (newWarning.equals(warnValue)) {
5587 newWarningFound = true;
5588 }
5589 }
5590 }
5591 Assert.assertFalse(oldWarningFound);
5592 Assert.assertTrue(newWarningFound);
5593 }
5594
5595
5596
5597
5598
5599
5600
5601
5602 @Test
5603 public void testWarnDatesAreAddedToWarningsOnLowerProtocolVersions() throws Exception {
5604 final String dateHdr = DateUtils.formatDate(new Date());
5605 final String origWarning = "110 fred \"stale\"";
5606 originResponse.setCode(HttpStatus.SC_OK);
5607 originResponse.setVersion(HttpVersion.HTTP_1_0);
5608 originResponse.addHeader("Warning", origWarning);
5609 originResponse.setHeader("Date", dateHdr);
5610 backendExpectsAnyRequest().andReturn(originResponse);
5611 replayMocks();
5612 final ClassicHttpResponse result = execute(request);
5613 verifyMocks();
5614
5615
5616
5617
5618 if (HttpVersion.HTTP_1_0.greaterEquals(result.getVersion())) {
5619 Assert.assertEquals(dateHdr, result.getFirstHeader("Date").getValue());
5620 boolean warningFound = false;
5621 final String targetWarning = origWarning + " \"" + dateHdr + "\"";
5622 for(final Header h : result.getHeaders("Warning")) {
5623 for(final String warning : h.getValue().split("\\s*,\\s*")) {
5624 if (targetWarning.equals(warning)) {
5625 warningFound = true;
5626 break;
5627 }
5628 }
5629 }
5630 Assert.assertTrue(warningFound);
5631 }
5632 }
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644 @Test
5645 public void testStripsBadlyDatedWarningsFromForwardedResponses() throws Exception {
5646 final Date now = new Date();
5647 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
5648 originResponse.setHeader("Date", DateUtils.formatDate(now));
5649 originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \""
5650 + DateUtils.formatDate(tenSecondsAgo) + "\"");
5651 originResponse.setHeader("Cache-Control","no-cache,no-store");
5652 backendExpectsAnyRequest().andReturn(originResponse);
5653
5654 replayMocks();
5655 final ClassicHttpResponse result = execute(request);
5656 verifyMocks();
5657
5658 for(final Header h : result.getHeaders("Warning")) {
5659 Assert.assertFalse(h.getValue().contains("wilma"));
5660 }
5661 }
5662
5663 @Test
5664 public void testStripsBadlyDatedWarningsFromStoredResponses() throws Exception {
5665 final Date now = new Date();
5666 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
5667 originResponse.setHeader("Date", DateUtils.formatDate(now));
5668 originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \""
5669 + DateUtils.formatDate(tenSecondsAgo) + "\"");
5670 originResponse.setHeader("Cache-Control","public,max-age=3600");
5671 backendExpectsAnyRequest().andReturn(originResponse);
5672
5673 replayMocks();
5674 final ClassicHttpResponse result = execute(request);
5675 verifyMocks();
5676
5677 for(final Header h : result.getHeaders("Warning")) {
5678 Assert.assertFalse(h.getValue().contains("wilma"));
5679 }
5680 }
5681
5682 @Test
5683 public void testRemovesWarningHeaderIfAllWarnValuesAreBadlyDated() throws Exception {
5684 final Date now = new Date();
5685 final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
5686 originResponse.setHeader("Date", DateUtils.formatDate(now));
5687 originResponse.addHeader("Warning", "110 wilma \"stale\" \""
5688 + DateUtils.formatDate(tenSecondsAgo) + "\"");
5689 backendExpectsAnyRequest().andReturn(originResponse);
5690
5691 replayMocks();
5692 final ClassicHttpResponse result = execute(request);
5693 verifyMocks();
5694
5695 final Header[] warningHeaders = result.getHeaders("Warning");
5696 Assert.assertTrue(warningHeaders == null || warningHeaders.length == 0);
5697 }
5698
5699 }