7e4e6c48c0e4941196defafd8e377d109e626bb7
[WebKit-https.git] / Source / WebKit2 / NetworkProcess / cache / NetworkCache.cpp
1 /*
2  * Copyright (C) 2014 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkCache.h"
28
29 #if ENABLE(NETWORK_CACHE)
30
31 #include "Logging.h"
32 #include "NetworkCacheCoders.h"
33 #include "NetworkCacheStorage.h"
34 #include "NetworkResourceLoader.h"
35 #include "WebCoreArgumentCoders.h"
36 #include <WebCore/CacheValidation.h>
37 #include <WebCore/HTTPHeaderNames.h>
38 #include <WebCore/ResourceResponse.h>
39 #include <WebCore/SharedBuffer.h>
40 #include <wtf/NeverDestroyed.h>
41 #include <wtf/StringHasher.h>
42 #include <wtf/text/StringBuilder.h>
43
44 namespace WebKit {
45
46 NetworkCache& NetworkCache::singleton()
47 {
48     static NeverDestroyed<NetworkCache> instance;
49     return instance;
50 }
51
52 bool NetworkCache::initialize(const String& cachePath)
53 {
54     m_storage = NetworkCacheStorage::open(cachePath);
55
56     LOG(NetworkCache, "(NetworkProcess) opened cache storage, success %d", !!m_storage);
57     return !!m_storage;
58 }
59
60 void NetworkCache::setMaximumSize(size_t maximumSize)
61 {
62     if (!m_storage)
63         return;
64     m_storage->setMaximumSize(maximumSize);
65 }
66
67 static NetworkCacheStorage::Entry encodeStorageEntry(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, PassRefPtr<WebCore::SharedBuffer> responseData)
68 {
69     NetworkCacheEncoder encoder;
70     encoder << response;
71
72     String varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary);
73     bool hasVaryingRequestHeaders = !varyValue.isEmpty();
74
75     encoder << hasVaryingRequestHeaders;
76
77     if (hasVaryingRequestHeaders) {
78         Vector<String> varyingHeaderNames;
79         varyValue.split(',', false, varyingHeaderNames);
80
81         Vector<std::pair<String, String>> varyingRequestHeaders;
82         for (auto& varyHeaderName : varyingHeaderNames) {
83             String headerName = varyHeaderName.stripWhiteSpace();
84             varyingRequestHeaders.append(std::make_pair(headerName, request.httpHeaderField(headerName)));
85         }
86         encoder << varyingRequestHeaders;
87     }
88     encoder.encodeChecksum();
89
90     auto timeStamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
91     NetworkCacheStorage::Data header(encoder.buffer(), encoder.bufferSize());
92     NetworkCacheStorage::Data body;
93     if (responseData)
94         body = NetworkCacheStorage::Data(reinterpret_cast<const uint8_t*>(responseData->data()), responseData->size());
95
96     return NetworkCacheStorage::Entry { timeStamp, header, body };
97 }
98
99 static bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const WebCore::ResourceRequest& request)
100 {
101     for (auto& varyingRequestHeader : varyingRequestHeaders) {
102         // FIXME: Vary: * in response would ideally trigger a cache delete instead of a store.
103         if (varyingRequestHeader.first == "*")
104             return false;
105         String requestValue = request.httpHeaderField(varyingRequestHeader.first);
106         if (requestValue != varyingRequestHeader.second)
107             return false;
108     }
109     return true;
110 }
111
112 static bool cachePolicyAllowsExpired(WebCore::ResourceRequestCachePolicy policy)
113 {
114     switch (policy) {
115     case WebCore::ReturnCacheDataElseLoad:
116     case WebCore::ReturnCacheDataDontLoad:
117         return true;
118     case WebCore::UseProtocolCachePolicy:
119     case WebCore::ReloadIgnoringCacheData:
120         return false;
121     }
122     ASSERT_NOT_REACHED();
123     return false;
124 }
125
126 static std::unique_ptr<NetworkCache::Entry> decodeStorageEntry(const NetworkCacheStorage::Entry& storageEntry, const WebCore::ResourceRequest& request)
127 {
128     NetworkCacheDecoder decoder(storageEntry.header.data(), storageEntry.header.size());
129
130     WebCore::ResourceResponse cachedResponse;
131     if (!decoder.decode(cachedResponse)) {
132         LOG(NetworkCache, "(NetworkProcess) response decoding failed\n");
133         return nullptr;
134     }
135
136     bool hasVaryingRequestHeaders;
137     if (!decoder.decode(hasVaryingRequestHeaders))
138         return nullptr;
139
140     if (hasVaryingRequestHeaders) {
141         Vector<std::pair<String, String>> varyingRequestHeaders;
142         if (!decoder.decode(varyingRequestHeaders))
143             return nullptr;
144
145         if (!verifyVaryingRequestHeaders(varyingRequestHeaders, request)) {
146             LOG(NetworkCache, "(NetworkProcess) varying header mismatch\n");
147             return nullptr;
148         }
149     }
150     if (!decoder.verifyChecksum()) {
151         LOG(NetworkCache, "(NetworkProcess) checksum verification failure\n");
152         return nullptr;
153     }
154
155     bool allowExpired = cachePolicyAllowsExpired(request.cachePolicy()) && !cachedResponse.cacheControlContainsMustRevalidate();
156     auto timeStamp = std::chrono::duration_cast<std::chrono::duration<double>>(storageEntry.timeStamp);
157     double age = WebCore::computeCurrentAge(cachedResponse, timeStamp.count());
158     double lifetime = WebCore::computeFreshnessLifetimeForHTTPFamily(cachedResponse, timeStamp.count());
159     bool isExpired = age > lifetime;
160     bool needsRevalidation = (isExpired && !allowExpired) || cachedResponse.cacheControlContainsNoCache();
161
162     if (needsRevalidation) {
163         bool hasValidatorFields = cachedResponse.hasCacheValidatorFields();
164         LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasValidatorFields=%d isExpired=%d age=%f lifetime=%f", isExpired, hasValidatorFields, age, lifetime);
165         if (!hasValidatorFields)
166             return nullptr;
167     }
168
169     auto entry = std::make_unique<NetworkCache::Entry>();
170     entry->storageEntry = storageEntry;
171     entry->needsRevalidation = needsRevalidation;
172
173     cachedResponse.setSource(needsRevalidation ? WebCore::ResourceResponse::Source::DiskCacheAfterValidation : WebCore::ResourceResponse::Source::DiskCache);
174     entry->response = cachedResponse;
175
176 #if ENABLE(SHAREABLE_RESOURCE)
177     RefPtr<SharedMemory> sharedMemory = storageEntry.body.isMap() ? SharedMemory::createFromVMBuffer(const_cast<uint8_t*>(storageEntry.body.data()), storageEntry.body.size()) : nullptr;
178     RefPtr<ShareableResource> shareableResource = sharedMemory ? ShareableResource::create(sharedMemory.release(), 0, storageEntry.body.size()) : nullptr;
179
180     if (shareableResource && shareableResource->createHandle(entry->shareableResourceHandle))
181         entry->buffer = entry->shareableResourceHandle.tryWrapInSharedBuffer();
182     else
183 #endif
184         entry->buffer = WebCore::SharedBuffer::create(storageEntry.body.data(), storageEntry.body.size());
185
186     return entry;
187 }
188
189 static bool canRetrieve(const WebCore::ResourceRequest& request)
190 {
191     if (!request.url().protocolIsInHTTPFamily())
192         return false;
193     // FIXME: Support HEAD and OPTIONS requests.
194     if (request.httpMethod() != "GET")
195         return false;
196     // FIXME: We should be able to validate conditional requests using cache.
197     if (request.isConditional())
198         return false;
199     if (request.cachePolicy() == WebCore::ReloadIgnoringCacheData)
200         return false;
201
202     return true;
203 }
204
205 static NetworkCacheKey makeCacheKey(const WebCore::ResourceRequest& request)
206 {
207 #if ENABLE(CACHE_PARTITIONING)
208     String partition = request.cachePartition();
209 #else
210     String partition;
211 #endif
212     if (partition.isEmpty())
213         partition = ASCIILiteral("No partition");
214     return NetworkCacheKey(request.httpMethod(), partition, request.url().string());
215 }
216
217 void NetworkCache::retrieve(const WebCore::ResourceRequest& originalRequest, std::function<void (std::unique_ptr<Entry>)> completionHandler)
218 {
219     ASSERT(isEnabled());
220
221     LOG(NetworkCache, "(NetworkProcess) retrieving %s priority %d", originalRequest.url().string().ascii().data(), originalRequest.priority());
222
223     if (!canRetrieve(originalRequest)) {
224         completionHandler(nullptr);
225         return;
226     }
227
228     auto startTime = std::chrono::system_clock::now();
229
230     NetworkCacheKey storageKey = makeCacheKey(originalRequest);
231     unsigned priority = originalRequest.priority();
232
233     // Captured data is going to be shuffled around threads. Avoid unsafe copying.
234     struct Capture {
235         WebCore::ResourceRequest originalRequest;
236         std::function<void (std::unique_ptr<Entry>)> completionHandler;
237     };
238     // FIXME: With C++14 this could use unique_ptr and initialized lambda capture
239     auto capture = std::make_shared<Capture>(Capture { originalRequest, completionHandler });
240
241     m_storage->retrieve(storageKey, priority, [this, capture, startTime](std::unique_ptr<NetworkCacheStorage::Entry> entry) {
242         if (!entry) {
243             LOG(NetworkCache, "(NetworkProcess) not found in storage");
244             capture->completionHandler(nullptr);
245             return false;
246         }
247         auto decodedEntry = decodeStorageEntry(*entry, capture->originalRequest);
248         bool success = !!decodedEntry;
249 #if !LOG_DISABLED
250         auto elapsedMS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count();
251 #endif
252         LOG(NetworkCache, "(NetworkProcess) retrieve complete success=%d priority=%d time=%lldms", success, capture->originalRequest.priority(), elapsedMS);
253         capture->completionHandler(WTF::move(decodedEntry));
254         return success;
255     });
256 }
257
258 static bool canStore(const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceResponse& response)
259 {
260     if (!originalRequest.url().protocolIsInHTTPFamily()) {
261         LOG(NetworkCache, "(NetworkProcess) not HTTP");
262         return false;
263     }
264     if (originalRequest.httpMethod() != "GET") {
265         LOG(NetworkCache, "(NetworkProcess) method %s", originalRequest.httpMethod().utf8().data());
266         return false;
267     }
268     if (response.isAttachment()) {
269         LOG(NetworkCache, "(NetworkProcess) attachment");
270         return false;
271     }
272
273     switch (response.httpStatusCode()) {
274     case 200: // OK
275     case 203: // Non-Authoritative Information
276     case 300: // Multiple Choices
277     case 301: // Moved Permanently
278     case 302: // Found
279     case 307: // Temporary Redirect
280     case 410: // Gone
281         if (response.cacheControlContainsNoStore()) {
282             LOG(NetworkCache, "(NetworkProcess) Cache-control:no-store");
283             return false;
284         }
285         return true;
286     default:
287         LOG(NetworkCache, "(NetworkProcess) status code %d", response.httpStatusCode());
288     }
289
290     return false;
291 }
292
293 void NetworkCache::store(const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceResponse& response, PassRefPtr<WebCore::SharedBuffer> responseData)
294 {
295     ASSERT(isEnabled());
296     ASSERT(responseData);
297
298     LOG(NetworkCache, "(NetworkProcess) storing %s, partition %s", originalRequest.url().string().latin1().data(), originalRequest.cachePartition().latin1().data());
299
300     if (!canStore(originalRequest, response)) {
301         LOG(NetworkCache, "(NetworkProcess) didn't store");
302         return;
303     }
304
305     auto key = makeCacheKey(originalRequest);
306     auto storageEntry = encodeStorageEntry(originalRequest, response, responseData);
307
308     m_storage->store(key, storageEntry, [](bool success) {
309         LOG(NetworkCache, "(NetworkProcess) store success=%d", success);
310     });
311 }
312
313 void NetworkCache::update(const WebCore::ResourceRequest& originalRequest, const Entry& entry, const WebCore::ResourceResponse& validatingResponse)
314 {
315     LOG(NetworkCache, "(NetworkProcess) updating %s", originalRequest.url().string().latin1().data());
316
317     WebCore::ResourceResponse response = entry.response;
318     WebCore::updateResponseHeadersAfterRevalidation(response, validatingResponse);
319
320     auto key = makeCacheKey(originalRequest);
321     auto updateEntry = encodeStorageEntry(originalRequest, response, entry.buffer);
322
323     m_storage->update(key, updateEntry, entry.storageEntry, [](bool success) {
324         LOG(NetworkCache, "(NetworkProcess) updated, success=%d", success);
325     });
326 }
327
328 void NetworkCache::clear()
329 {
330     LOG(NetworkCache, "(NetworkProcess) clearing cache");
331     if (m_storage)
332         m_storage->clear();
333 }
334
335 }
336
337 #endif