1/*
2 * Copyright (c) 2014, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "core/fetch/ImageResource.h"
34#include "core/fetch/MemoryCache.h"
35#include "core/fetch/Resource.h"
36#include "core/fetch/ResourceFetcher.h"
37#include "core/fetch/ResourcePtr.h"
38#include "core/html/HTMLDocument.h"
39#include "core/loader/DocumentLoader.h"
40#include "platform/network/ResourceRequest.h"
41#include "public/platform/Platform.h"
42#include "wtf/OwnPtr.h"
43#include "wtf/RefPtr.h"
44
45#include <gtest/gtest.h>
46
47using namespace WebCore;
48
49namespace {
50
51// An URL for the original request.
52const char kResourceURL[] = "http://resource.com/";
53
54// The origin time of our first request.
55const char kOriginalRequestDateAsString[] = "Thu, 25 May 1977 18:30:00 GMT";
56const double kOriginalRequestDateAsDouble = 233433000.;
57
58const char kOneDayBeforeOriginalRequest[] = "Wed, 24 May 1977 18:30:00 GMT";
59const char kOneDayAfterOriginalRequest[] = "Fri, 26 May 1977 18:30:00 GMT";
60
61const unsigned char kAConstUnsignedCharZero = 0;
62
63class CachingCorrectnessTest : public ::testing::Test {
64protected:
65    void advanceClock(double seconds)
66    {
67        m_proxyPlatform.advanceClock(seconds);
68    }
69
70    ResourcePtr<Resource> resourceFromResourceResponse(ResourceResponse response, Resource::Type type = Resource::Raw)
71    {
72        if (response.url().isNull())
73            response.setURL(KURL(ParsedURLString, kResourceURL));
74        ResourcePtr<Resource> resource =
75            new Resource(ResourceRequest(response.url()), type);
76        resource->setResponse(response);
77        memoryCache()->add(resource.get());
78
79        return resource;
80    }
81
82    ResourcePtr<Resource> resourceFromResourceRequest(ResourceRequest request, Resource::Type type = Resource::Raw)
83    {
84        if (request.url().isNull())
85            request.setURL(KURL(ParsedURLString, kResourceURL));
86        ResourcePtr<Resource> resource =
87            new Resource(request, type);
88        resource->setResponse(ResourceResponse(KURL(ParsedURLString, kResourceURL), "text/html", 0, nullAtom, String()));
89        memoryCache()->add(resource.get());
90
91        return resource;
92    }
93
94    ResourcePtr<Resource> fetch()
95    {
96        FetchRequest fetchRequest(ResourceRequest(KURL(ParsedURLString, kResourceURL)), FetchInitiatorInfo());
97        return m_fetcher->fetchSynchronously(fetchRequest);
98    }
99
100    ResourcePtr<Resource> fetchImage()
101    {
102        FetchRequest fetchRequest(ResourceRequest(KURL(ParsedURLString, kResourceURL)), FetchInitiatorInfo());
103        return m_fetcher->fetchImage(fetchRequest);
104    }
105
106    ResourceFetcher* fetcher() const { return m_fetcher.get(); }
107
108private:
109    // A simple platform that mocks out the clock, for cache freshness testing.
110    class ProxyPlatform : public blink::Platform {
111    public:
112        ProxyPlatform() : m_elapsedSeconds(0.) { }
113
114        void advanceClock(double seconds)
115        {
116            m_elapsedSeconds += seconds;
117        }
118
119    private:
120        // From blink::Platform:
121        virtual double currentTime()
122        {
123            return kOriginalRequestDateAsDouble + m_elapsedSeconds;
124        }
125
126        // These blink::Platform methods must be overriden to make a usable object.
127        virtual void cryptographicallyRandomValues(unsigned char* buffer, size_t length) { ASSERT_NOT_REACHED(); }
128        virtual const unsigned char* getTraceCategoryEnabledFlag(const char* categoryName)
129        {
130            return &kAConstUnsignedCharZero;
131        }
132
133        double m_elapsedSeconds;
134    };
135
136    virtual void SetUp()
137    {
138        m_savedPlatform = blink::Platform::current();
139        blink::Platform::initialize(&m_proxyPlatform);
140
141        // Save the global memory cache to restore it upon teardown.
142        m_globalMemoryCache = adoptPtr(memoryCache());
143        // Create the test memory cache instance and hook it in.
144        m_testingMemoryCache = adoptPtr(new MemoryCache());
145        setMemoryCacheForTesting(m_testingMemoryCache.leakPtr());
146
147        // Create a ResourceFetcher that has a real DocumentLoader and Document, but is not attached to a LocalFrame.
148        const KURL kDocumentURL(ParsedURLString, "http://document.com/");
149        m_documentLoader = DocumentLoader::create(0, ResourceRequest(kDocumentURL), SubstituteData());
150        m_document = HTMLDocument::create();
151        m_fetcher = ResourceFetcher::create(m_documentLoader.get());
152        m_fetcher->setDocument(m_document.get());
153    }
154
155    virtual void TearDown()
156    {
157        memoryCache()->evictResources();
158
159        // Regain the ownership of testing memory cache, so that it will be
160        // destroyed.
161        m_testingMemoryCache = adoptPtr(memoryCache());
162
163        // Yield the ownership of the global memory cache back.
164        setMemoryCacheForTesting(m_globalMemoryCache.leakPtr());
165
166        blink::Platform::initialize(m_savedPlatform);
167    }
168
169    blink::Platform* m_savedPlatform;
170    ProxyPlatform m_proxyPlatform;
171
172    OwnPtr<MemoryCache> m_testingMemoryCache;
173    OwnPtr<MemoryCache> m_globalMemoryCache;
174
175    RefPtr<DocumentLoader> m_documentLoader;
176
177    RefPtrWillBePersistent<HTMLDocument> m_document;
178    RefPtrWillBePersistent<ResourceFetcher> m_fetcher;
179};
180
181TEST_F(CachingCorrectnessTest, FreshFromLastModified)
182{
183    ResourceResponse fresh200Response;
184    fresh200Response.setHTTPStatusCode(200);
185    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
186    fresh200Response.setHTTPHeaderField("Last-Modified", kOneDayBeforeOriginalRequest);
187
188    ResourcePtr<Resource> fresh200 = resourceFromResourceResponse(fresh200Response);
189
190    // Advance the clock within the implicit freshness period of this resource before we make a request.
191    advanceClock(600.);
192
193    ResourcePtr<Resource> fetched = fetch();
194    EXPECT_EQ(fresh200, fetched);
195}
196
197TEST_F(CachingCorrectnessTest, FreshFromExpires)
198{
199    ResourceResponse fresh200Response;
200    fresh200Response.setHTTPStatusCode(200);
201    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
202    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
203
204    ResourcePtr<Resource> fresh200 = resourceFromResourceResponse(fresh200Response);
205
206    // Advance the clock within the freshness period of this resource before we make a request.
207    advanceClock(24. * 60. * 60. - 15.);
208
209    ResourcePtr<Resource> fetched = fetch();
210    EXPECT_EQ(fresh200, fetched);
211}
212
213TEST_F(CachingCorrectnessTest, FreshFromMaxAge)
214{
215    ResourceResponse fresh200Response;
216    fresh200Response.setHTTPStatusCode(200);
217    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
218    fresh200Response.setHTTPHeaderField("Cache-Control", "max-age=600");
219
220    ResourcePtr<Resource> fresh200 = resourceFromResourceResponse(fresh200Response);
221
222    // Advance the clock within the freshness period of this resource before we make a request.
223    advanceClock(500.);
224
225    ResourcePtr<Resource> fetched = fetch();
226    EXPECT_EQ(fresh200, fetched);
227}
228
229// The strong validator causes a revalidation to be launched, and the proxy and original resources leak because of their reference loop.
230TEST_F(CachingCorrectnessTest, DISABLED_ExpiredFromLastModified)
231{
232    ResourceResponse expired200Response;
233    expired200Response.setHTTPStatusCode(200);
234    expired200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
235    expired200Response.setHTTPHeaderField("Last-Modified", kOneDayBeforeOriginalRequest);
236
237    ResourcePtr<Resource> expired200 = resourceFromResourceResponse(expired200Response);
238
239    // Advance the clock beyond the implicit freshness period.
240    advanceClock(24. * 60. * 60. * 0.2);
241
242    ResourcePtr<Resource> fetched = fetch();
243    EXPECT_NE(expired200, fetched);
244}
245
246TEST_F(CachingCorrectnessTest, ExpiredFromExpires)
247{
248    ResourceResponse expired200Response;
249    expired200Response.setHTTPStatusCode(200);
250    expired200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
251    expired200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
252
253    ResourcePtr<Resource> expired200 = resourceFromResourceResponse(expired200Response);
254
255    // Advance the clock within the expiredness period of this resource before we make a request.
256    advanceClock(24. * 60. * 60. + 15.);
257
258    ResourcePtr<Resource> fetched = fetch();
259    EXPECT_NE(expired200, fetched);
260}
261
262// If the image hasn't been loaded in this "document" before, then it shouldn't have list of available images logic.
263TEST_F(CachingCorrectnessTest, NewImageExpiredFromExpires)
264{
265    ResourceResponse expired200Response;
266    expired200Response.setHTTPStatusCode(200);
267    expired200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
268    expired200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
269
270    ResourcePtr<Resource> expired200 = resourceFromResourceResponse(expired200Response, Resource::Image);
271
272    // Advance the clock within the expiredness period of this resource before we make a request.
273    advanceClock(24. * 60. * 60. + 15.);
274
275    ResourcePtr<Resource> fetched = fetchImage();
276    EXPECT_NE(expired200, fetched);
277}
278
279// If the image has been loaded in this "document" before, then it should have list of available images logic, and so
280// normal cache testing should be bypassed.
281TEST_F(CachingCorrectnessTest, ReuseImageExpiredFromExpires)
282{
283    ResourceResponse expired200Response;
284    expired200Response.setHTTPStatusCode(200);
285    expired200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
286    expired200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
287
288    ResourcePtr<Resource> expired200 = resourceFromResourceResponse(expired200Response, Resource::Image);
289
290    // Advance the clock within the freshness period, and make a request to add this image to the document resources.
291    advanceClock(15.);
292    ResourcePtr<Resource> firstFetched = fetchImage();
293    EXPECT_EQ(expired200, firstFetched);
294
295    // Advance the clock within the expiredness period of this resource before we make a request.
296    advanceClock(24. * 60. * 60. + 15.);
297
298    ResourcePtr<Resource> fetched = fetchImage();
299    EXPECT_EQ(expired200, fetched);
300}
301
302TEST_F(CachingCorrectnessTest, ExpiredFromMaxAge)
303{
304    ResourceResponse expired200Response;
305    expired200Response.setHTTPStatusCode(200);
306    expired200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
307    expired200Response.setHTTPHeaderField("Cache-Control", "max-age=600");
308
309    ResourcePtr<Resource> expired200 = resourceFromResourceResponse(expired200Response);
310
311    // Advance the clock within the expiredness period of this resource before we make a request.
312    advanceClock(700.);
313
314    ResourcePtr<Resource> fetched = fetch();
315    EXPECT_NE(expired200, fetched);
316}
317
318TEST_F(CachingCorrectnessTest, FreshButNoCache)
319{
320    ResourceResponse fresh200NocacheResponse;
321    fresh200NocacheResponse.setHTTPStatusCode(200);
322    fresh200NocacheResponse.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
323    fresh200NocacheResponse.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
324    fresh200NocacheResponse.setHTTPHeaderField("Cache-Control", "no-cache");
325
326    ResourcePtr<Resource> fresh200Nocache = resourceFromResourceResponse(fresh200NocacheResponse);
327
328    // Advance the clock within the freshness period of this resource before we make a request.
329    advanceClock(24. * 60. * 60. - 15.);
330
331    ResourcePtr<Resource> fetched = fetch();
332    EXPECT_NE(fresh200Nocache, fetched);
333}
334
335TEST_F(CachingCorrectnessTest, RequestWithNoCahe)
336{
337    ResourceRequest noCacheRequest;
338    noCacheRequest.setHTTPHeaderField("Cache-Control", "no-cache");
339    ResourcePtr<Resource> noCacheResource = resourceFromResourceRequest(noCacheRequest);
340    ResourcePtr<Resource> fetched = fetch();
341    EXPECT_NE(noCacheResource, fetched);
342}
343
344TEST_F(CachingCorrectnessTest, FreshButNoStore)
345{
346    ResourceResponse fresh200NostoreResponse;
347    fresh200NostoreResponse.setHTTPStatusCode(200);
348    fresh200NostoreResponse.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
349    fresh200NostoreResponse.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
350    fresh200NostoreResponse.setHTTPHeaderField("Cache-Control", "no-store");
351
352    ResourcePtr<Resource> fresh200Nostore = resourceFromResourceResponse(fresh200NostoreResponse);
353
354    // Advance the clock within the freshness period of this resource before we make a request.
355    advanceClock(24. * 60. * 60. - 15.);
356
357    ResourcePtr<Resource> fetched = fetch();
358    EXPECT_NE(fresh200Nostore, fetched);
359}
360
361TEST_F(CachingCorrectnessTest, RequestWithNoStore)
362{
363    ResourceRequest noStoreRequest;
364    noStoreRequest.setHTTPHeaderField("Cache-Control", "no-store");
365    ResourcePtr<Resource> noStoreResource = resourceFromResourceRequest(noStoreRequest);
366    ResourcePtr<Resource> fetched = fetch();
367    EXPECT_NE(noStoreResource, fetched);
368}
369
370// FIXME: Determine if ignoring must-revalidate for blink is correct behaviour.
371// See crbug.com/340088 .
372TEST_F(CachingCorrectnessTest, DISABLED_FreshButMustRevalidate)
373{
374    ResourceResponse fresh200MustRevalidateResponse;
375    fresh200MustRevalidateResponse.setHTTPStatusCode(200);
376    fresh200MustRevalidateResponse.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
377    fresh200MustRevalidateResponse.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
378    fresh200MustRevalidateResponse.setHTTPHeaderField("Cache-Control", "must-revalidate");
379
380    ResourcePtr<Resource> fresh200MustRevalidate = resourceFromResourceResponse(fresh200MustRevalidateResponse);
381
382    // Advance the clock within the freshness period of this resource before we make a request.
383    advanceClock(24. * 60. * 60. - 15.);
384
385    ResourcePtr<Resource> fetched = fetch();
386    EXPECT_NE(fresh200MustRevalidate, fetched);
387}
388
389TEST_F(CachingCorrectnessTest, FreshWithFreshRedirect)
390{
391    KURL redirectUrl(ParsedURLString, kResourceURL);
392    const char redirectTargetUrlString[] = "http://redirect-target.com";
393    KURL redirectTargetUrl(ParsedURLString, redirectTargetUrlString);
394
395    ResourcePtr<Resource> firstResource = new Resource(ResourceRequest(redirectUrl), Resource::Raw);
396
397    ResourceResponse fresh301Response;
398    fresh301Response.setURL(redirectUrl);
399    fresh301Response.setHTTPStatusCode(301);
400    fresh301Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
401    fresh301Response.setHTTPHeaderField("Location", redirectTargetUrlString);
402    fresh301Response.setHTTPHeaderField("Cache-Control", "max-age=600");
403
404    // Add the redirect to our request.
405    ResourceRequest redirectRequest = ResourceRequest(redirectTargetUrl);
406    firstResource->willSendRequest(redirectRequest, fresh301Response);
407
408    // Add the final response to our request.
409    ResourceResponse fresh200Response;
410    fresh200Response.setURL(redirectTargetUrl);
411    fresh200Response.setHTTPStatusCode(200);
412    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
413    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
414
415    firstResource->setResponse(fresh200Response);
416    memoryCache()->add(firstResource.get());
417
418    advanceClock(500.);
419
420    ResourcePtr<Resource> fetched = fetch();
421    EXPECT_EQ(firstResource, fetched);
422}
423
424TEST_F(CachingCorrectnessTest, FreshWithStaleRedirect)
425{
426    KURL redirectUrl(ParsedURLString, kResourceURL);
427    const char redirectTargetUrlString[] = "http://redirect-target.com";
428    KURL redirectTargetUrl(ParsedURLString, redirectTargetUrlString);
429
430    ResourcePtr<Resource> firstResource = new Resource(ResourceRequest(redirectUrl), Resource::Raw);
431
432    ResourceResponse stale301Response;
433    stale301Response.setURL(redirectUrl);
434    stale301Response.setHTTPStatusCode(301);
435    stale301Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
436    stale301Response.setHTTPHeaderField("Location", redirectTargetUrlString);
437
438    // Add the redirect to our request.
439    ResourceRequest redirectRequest = ResourceRequest(redirectTargetUrl);
440    firstResource->willSendRequest(redirectRequest, stale301Response);
441
442    // Add the final response to our request.
443    ResourceResponse fresh200Response;
444    fresh200Response.setURL(redirectTargetUrl);
445    fresh200Response.setHTTPStatusCode(200);
446    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
447    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
448
449    firstResource->setResponse(fresh200Response);
450    memoryCache()->add(firstResource.get());
451
452    advanceClock(500.);
453
454    ResourcePtr<Resource> fetched = fetch();
455    EXPECT_NE(firstResource, fetched);
456}
457
458TEST_F(CachingCorrectnessTest, PostToSameURLTwice)
459{
460    ResourceRequest request1(KURL(ParsedURLString, kResourceURL));
461    request1.setHTTPMethod("POST");
462    ResourcePtr<Resource> resource1 = new Resource(ResourceRequest(request1.url()), Resource::Raw);
463    resource1->setLoading(true);
464    memoryCache()->add(resource1.get());
465
466    ResourceRequest request2(KURL(ParsedURLString, kResourceURL));
467    request2.setHTTPMethod("POST");
468    FetchRequest fetch2(request2, FetchInitiatorInfo());
469    ResourcePtr<Resource> resource2 = fetcher()->fetchSynchronously(fetch2);
470
471    EXPECT_EQ(resource2, memoryCache()->resourceForURL(request2.url()));
472    EXPECT_NE(resource1, resource2);
473}
474
475TEST_F(CachingCorrectnessTest, 302RedirectNotImplicitlyFresh)
476{
477    KURL redirectUrl(ParsedURLString, kResourceURL);
478    const char redirectTargetUrlString[] = "http://redirect-target.com";
479    KURL redirectTargetUrl(ParsedURLString, redirectTargetUrlString);
480
481    ResourcePtr<Resource> firstResource = new Resource(ResourceRequest(redirectUrl), Resource::Raw);
482
483    ResourceResponse fresh302Response;
484    fresh302Response.setURL(redirectUrl);
485    fresh302Response.setHTTPStatusCode(302);
486    fresh302Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
487    fresh302Response.setHTTPHeaderField("Last-Modified", kOneDayBeforeOriginalRequest);
488    fresh302Response.setHTTPHeaderField("Location", redirectTargetUrlString);
489
490    // Add the redirect to our request.
491    ResourceRequest redirectRequest = ResourceRequest(redirectTargetUrl);
492    firstResource->willSendRequest(redirectRequest, fresh302Response);
493
494    // Add the final response to our request.
495    ResourceResponse fresh200Response;
496    fresh200Response.setURL(redirectTargetUrl);
497    fresh200Response.setHTTPStatusCode(200);
498    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
499    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
500
501    firstResource->setResponse(fresh200Response);
502    memoryCache()->add(firstResource.get());
503
504    advanceClock(500.);
505
506    ResourcePtr<Resource> fetched = fetch();
507    EXPECT_NE(firstResource, fetched);
508}
509
510TEST_F(CachingCorrectnessTest, 302RedirectExplicitlyFreshMaxAge)
511{
512    KURL redirectUrl(ParsedURLString, kResourceURL);
513    const char redirectTargetUrlString[] = "http://redirect-target.com";
514    KURL redirectTargetUrl(ParsedURLString, redirectTargetUrlString);
515
516    ResourcePtr<Resource> firstResource = new Resource(ResourceRequest(redirectUrl), Resource::Raw);
517
518    ResourceResponse fresh302Response;
519    fresh302Response.setURL(redirectUrl);
520    fresh302Response.setHTTPStatusCode(302);
521    fresh302Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
522    fresh302Response.setHTTPHeaderField("Cache-Control", "max-age=600");
523    fresh302Response.setHTTPHeaderField("Location", redirectTargetUrlString);
524
525    // Add the redirect to our request.
526    ResourceRequest redirectRequest = ResourceRequest(redirectTargetUrl);
527    firstResource->willSendRequest(redirectRequest, fresh302Response);
528
529    // Add the final response to our request.
530    ResourceResponse fresh200Response;
531    fresh200Response.setURL(redirectTargetUrl);
532    fresh200Response.setHTTPStatusCode(200);
533    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
534    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
535
536    firstResource->setResponse(fresh200Response);
537    memoryCache()->add(firstResource.get());
538
539    advanceClock(500.);
540
541    ResourcePtr<Resource> fetched = fetch();
542    EXPECT_EQ(firstResource, fetched);
543}
544
545TEST_F(CachingCorrectnessTest, 302RedirectExplicitlyFreshExpires)
546{
547    KURL redirectUrl(ParsedURLString, kResourceURL);
548    const char redirectTargetUrlString[] = "http://redirect-target.com";
549    KURL redirectTargetUrl(ParsedURLString, redirectTargetUrlString);
550
551    ResourcePtr<Resource> firstResource = new Resource(ResourceRequest(redirectUrl), Resource::Raw);
552
553    ResourceResponse fresh302Response;
554    fresh302Response.setURL(redirectUrl);
555    fresh302Response.setHTTPStatusCode(302);
556    fresh302Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
557    fresh302Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
558    fresh302Response.setHTTPHeaderField("Location", redirectTargetUrlString);
559
560    // Add the redirect to our request.
561    ResourceRequest redirectRequest = ResourceRequest(redirectTargetUrl);
562    firstResource->willSendRequest(redirectRequest, fresh302Response);
563
564    // Add the final response to our request.
565    ResourceResponse fresh200Response;
566    fresh200Response.setURL(redirectTargetUrl);
567    fresh200Response.setHTTPStatusCode(200);
568    fresh200Response.setHTTPHeaderField("Date", kOriginalRequestDateAsString);
569    fresh200Response.setHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
570
571    firstResource->setResponse(fresh200Response);
572    memoryCache()->add(firstResource.get());
573
574    advanceClock(500.);
575
576    ResourcePtr<Resource> fetched = fetch();
577    EXPECT_EQ(firstResource, fetched);
578}
579
580} // namespace
581