Switch to file backed buffer when resource is cached to disk
[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 %u", 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     NetworkCacheKey storageKey = makeCacheKey(originalRequest);
230     unsigned priority = originalRequest.priority();
231
232     m_storage->retrieve(storageKey, priority, [this, originalRequest, completionHandler, startTime](std::unique_ptr<NetworkCacheStorage::Entry> entry) {
233         if (!entry) {
234             LOG(NetworkCache, "(NetworkProcess) not found in storage");
235             completionHandler(nullptr);
236             return false;
237         }
238         auto decodedEntry = decodeStorageEntry(*entry, originalRequest);
239         bool success = !!decodedEntry;
240 #if !LOG_DISABLED
241         auto elapsedMS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count();
242 #endif
243         LOG(NetworkCache, "(NetworkProcess) retrieve complete success=%d priority=%u time=%lldms", success, originalRequest.priority(), elapsedMS);
244         completionHandler(WTF::move(decodedEntry));
245         return success;
246     });
247 }
248
249 static bool canStore(const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceResponse& response)
250 {
251     if (!originalRequest.url().protocolIsInHTTPFamily()) {
252         LOG(NetworkCache, "(NetworkProcess) not HTTP");
253         return false;
254     }
255     if (originalRequest.httpMethod() != "GET") {
256         LOG(NetworkCache, "(NetworkProcess) method %s", originalRequest.httpMethod().utf8().data());
257         return false;
258     }
259     if (response.isAttachment()) {
260         LOG(NetworkCache, "(NetworkProcess) attachment");
261         return false;
262     }
263
264     switch (response.httpStatusCode()) {
265     case 200: // OK
266     case 203: // Non-Authoritative Information
267     case 300: // Multiple Choices
268     case 301: // Moved Permanently
269     case 302: // Found
270     case 307: // Temporary Redirect
271     case 410: // Gone
272         if (response.cacheControlContainsNoStore()) {
273             LOG(NetworkCache, "(NetworkProcess) Cache-control:no-store");
274             return false;
275         }
276         return true;
277     default:
278         LOG(NetworkCache, "(NetworkProcess) status code %d", response.httpStatusCode());
279     }
280
281     return false;
282 }
283
284 void NetworkCache::store(const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceResponse& response, RefPtr<WebCore::SharedBuffer>&& responseData, std::function<void (MappedBody&)> completionHandler)
285 {
286     ASSERT(isEnabled());
287     ASSERT(responseData);
288
289     LOG(NetworkCache, "(NetworkProcess) storing %s, partition %s", originalRequest.url().string().latin1().data(), originalRequest.cachePartition().latin1().data());
290
291     if (!canStore(originalRequest, response)) {
292         LOG(NetworkCache, "(NetworkProcess) didn't store");
293         return;
294     }
295
296     auto key = makeCacheKey(originalRequest);
297     auto storageEntry = encodeStorageEntry(originalRequest, response, WTF::move(responseData));
298
299     m_storage->store(key, storageEntry, [completionHandler](bool success, const NetworkCacheStorage::Data& bodyData) {
300         MappedBody mappedBody;
301 #if ENABLE(SHAREABLE_RESOURCE)
302         if (bodyData.isMap()) {
303             RefPtr<SharedMemory> sharedMemory = SharedMemory::createFromVMBuffer(const_cast<uint8_t*>(bodyData.data()), bodyData.size());
304             mappedBody.shareableResource = sharedMemory ? ShareableResource::create(WTF::move(sharedMemory), 0, bodyData.size()) : nullptr;
305             if (mappedBody.shareableResource)
306                 mappedBody.shareableResource->createHandle(mappedBody.shareableResourceHandle);
307         }
308 #endif
309         completionHandler(mappedBody);
310         LOG(NetworkCache, "(NetworkProcess) store success=%d", success);
311     });
312 }
313
314 void NetworkCache::update(const WebCore::ResourceRequest& originalRequest, const Entry& entry, const WebCore::ResourceResponse& validatingResponse)
315 {
316     LOG(NetworkCache, "(NetworkProcess) updating %s", originalRequest.url().string().latin1().data());
317
318     WebCore::ResourceResponse response = entry.response;
319     WebCore::updateResponseHeadersAfterRevalidation(response, validatingResponse);
320
321     auto key = makeCacheKey(originalRequest);
322     auto updateEntry = encodeStorageEntry(originalRequest, response, entry.buffer);
323
324     m_storage->update(key, updateEntry, entry.storageEntry, [](bool success, const NetworkCacheStorage::Data&) {
325         LOG(NetworkCache, "(NetworkProcess) updated, success=%d", success);
326     });
327 }
328
329 void NetworkCache::clear()
330 {
331     LOG(NetworkCache, "(NetworkProcess) clearing cache");
332     if (m_storage)
333         m_storage->clear();
334 }
335
336 }
337
338 #endif