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