Remove the list of removed caches from CacheStorageEngine
[WebKit-https.git] / Source / WebKit / NetworkProcess / cache / CacheStorageEngine.cpp
1 /*
2  * Copyright (C) 2017 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CacheStorageEngine.h"
28
29 #include "NetworkCacheIOChannel.h"
30 #include "NetworkProcess.h"
31 #include <WebCore/CacheQueryOptions.h>
32 #include <pal/SessionID.h>
33 #include <wtf/MainThread.h>
34 #include <wtf/NeverDestroyed.h>
35 #include <wtf/text/StringHash.h>
36
37 using namespace WebCore::DOMCache;
38 using namespace WebKit::NetworkCache;
39
40 namespace WebKit {
41
42 namespace CacheStorage {
43
44 static HashMap<PAL::SessionID, RefPtr<Engine>>& globalEngineMap()
45 {
46     static NeverDestroyed<HashMap<PAL::SessionID, RefPtr<Engine>>> map;
47
48     return map;
49 }
50
51 Engine& Engine::from(PAL::SessionID sessionID)
52 {
53     auto addResult = globalEngineMap().add(sessionID, nullptr);
54     if (addResult.isNewEntry)
55         addResult.iterator->value = Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID));
56     return *addResult.iterator->value;
57 }
58
59 void Engine::destroyEngine(PAL::SessionID sessionID)
60 {
61     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
62     globalEngineMap().remove(sessionID);
63 }
64
65 Engine& Engine::defaultEngine()
66 {
67     auto sessionID = PAL::SessionID::defaultSessionID();
68     static NeverDestroyed<Ref<Engine>> defaultEngine = { Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID)) };
69     return defaultEngine.get();
70 }
71
72 Engine::Engine(String&& rootPath)
73     : m_rootPath(WTFMove(rootPath))
74 {
75     if (!m_rootPath.isNull())
76         m_ioQueue = WorkQueue::create("com.apple.WebKit.CacheStorageEngine.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background);
77 }
78
79 void Engine::open(const String& origin, const String& cacheName, CacheIdentifierCallback&& callback)
80 {
81     readCachesFromDisk(origin, [this, cacheName, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
82         if (!cachesOrError.hasValue()) {
83             callback(makeUnexpected(cachesOrError.error()));
84             return;
85         }
86
87         Caches& caches = cachesOrError.value();
88
89         if (auto* cache = caches.find(cacheName)) {
90             callback(cache->identifier);
91             return;
92         }
93
94         caches.open(String { cacheName }, [callback = WTFMove(callback)](const CacheIdentifierOrError& result) mutable {
95             callback(result);
96         });
97     });
98 }
99
100 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
101 {
102     Caches* cachesToModify = nullptr;
103     for (auto& caches : m_caches.values()) {
104         auto* cacheToRemove = caches->find(cacheIdentifier);
105         if (cacheToRemove) {
106             cachesToModify = caches.ptr();
107             break;
108         }
109     }
110     if (!cachesToModify) {
111         callback(makeUnexpected(Error::Internal));
112         return;
113     }
114
115     cachesToModify->remove(cacheIdentifier, [cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
116         if (error) {
117             callback(makeUnexpected(error.value()));
118             return;
119         }
120         callback(cacheIdentifier);
121     });
122 }
123
124 void Engine::retrieveCaches(const String& origin, CacheInfosCallback&& callback)
125 {
126     readCachesFromDisk(origin, [callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
127         if (!cachesOrError.hasValue()) {
128             callback(makeUnexpected(cachesOrError.error()));
129             return;
130         }
131
132         callback(cachesOrError.value().get().cacheInfos());
133     });
134 }
135
136 void Engine::retrieveRecords(uint64_t cacheIdentifier, RecordsCallback&& callback)
137 {
138     readCache(cacheIdentifier, [callback = WTFMove(callback)](CacheOrError&& result) mutable {
139         if (!result.hasValue()) {
140             callback(makeUnexpected(result.error()));
141             return;
142         }
143         // FIXME: Pass records by reference.
144         auto& records = result.value().get().records;
145
146         Vector<Record> copy;
147         copy.reserveInitialCapacity(records.size());
148         for (auto& record : records)
149             copy.uncheckedAppend(record.copy());
150
151         callback(WTFMove(copy));
152     });
153 }
154
155 void Engine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
156 {
157     readCache(cacheIdentifier, [this, cacheIdentifier, records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
158         if (!result.hasValue()) {
159             callback(makeUnexpected(result.error()));
160             return;
161         }
162
163         Cache& cache = result.value();
164
165         WebCore::CacheQueryOptions options;
166         Vector<uint64_t> recordIdentifiers;
167         recordIdentifiers.reserveInitialCapacity(records.size());
168         for (auto& record : records) {
169             auto matchingRecords = Engine::queryCache(cache.records, record.request, options);
170             if (matchingRecords.isEmpty()) {
171                 record.identifier = ++cache.nextRecordIdentifier;
172                 recordIdentifiers.uncheckedAppend(record.identifier);
173                 cache.records.append(WTFMove(record));
174             } else {
175                 auto identifier = matchingRecords[0];
176                 auto position = cache.records.findMatching([&](const auto& item) { return item.identifier == identifier; });
177                 ASSERT(position != notFound);
178                 if (position != notFound) {
179                     auto& existingRecord = cache.records[position];
180                     recordIdentifiers.uncheckedAppend(identifier);
181                     existingRecord.responseHeadersGuard = record.responseHeadersGuard;
182                     existingRecord.response = WTFMove(record.response);
183                     existingRecord.responseBody = WTFMove(record.responseBody);
184                     ++existingRecord.updateResponseCounter;
185                 }
186             }
187         }
188         writeCacheRecords(cacheIdentifier, WTFMove(recordIdentifiers), [callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
189             callback(WTFMove(result));
190         });
191     });
192 }
193
194 void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
195 {
196     readCache(cacheIdentifier, [this, cacheIdentifier, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
197         if (!result.hasValue()) {
198             callback(makeUnexpected(result.error()));
199             return;
200         }
201
202         auto& currentRecords = result.value().get().records;
203
204         auto recordsToRemove = queryCache(currentRecords, request, options);
205         if (recordsToRemove.isEmpty()) {
206             callback({ });
207             return;
208         }
209
210         Vector<Record> recordsToKeep;
211         for (auto& record : currentRecords) {
212             if (recordsToRemove.findMatching([&](auto item) { return item == record.identifier; }) == notFound)
213                 recordsToKeep.append(record.copy());
214         }
215         removeCacheRecords(cacheIdentifier, WTFMove(recordsToRemove), [this, cacheIdentifier, recordsToKeep = WTFMove(recordsToKeep), callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
216             if (!result.hasValue()) {
217                 callback(makeUnexpected(result.error()));
218                 return;
219             }
220
221             auto* writtenCache = cache(cacheIdentifier);
222             if (!writtenCache) {
223                 callback(makeUnexpected(Error::Internal));
224                 return;
225             }
226             writtenCache->records = WTFMove(recordsToKeep);
227
228             callback(WTFMove(result.value()));
229         });
230     });
231 }
232
233 void Engine::initialize(Function<void(std::optional<Error>&&)>&& callback)
234 {
235     if (m_salt) {
236         callback(std::nullopt);
237         return;
238     }
239
240     if (!shouldPersist()) {
241         m_salt = makeSalt();
242         callback(std::nullopt);
243         return;
244     }
245
246     String saltPath = WebCore::pathByAppendingComponent(m_rootPath, ASCIILiteral("salt"));
247     m_ioQueue->dispatch([protectedThis = makeRef(*this), this, callback = WTFMove(callback), saltPath = WTFMove(saltPath)] () mutable {
248         WebCore::makeAllDirectories(m_rootPath);
249         RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), this, salt = readOrMakeSalt(saltPath), callback = WTFMove(callback)]() mutable {
250             if (!salt) {
251                 callback(Error::WriteDisk);
252                 return;
253             }
254             m_salt = WTFMove(salt);
255             callback(std::nullopt);
256         });
257     });
258 }
259
260 void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
261 {
262     auto& caches = m_caches.ensure(origin, [&origin, this] {
263         return Caches::create(*this, origin);
264     }).iterator->value;
265
266     if (caches->isInitialized()) {
267         callback(std::reference_wrapper<Caches> { caches.get() });
268         return;
269     }
270
271     initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
272         if (error) {
273             callback(makeUnexpected(error.value()));
274             return;
275         }
276
277         auto caches = m_caches.get(origin);
278         ASSERT(caches);
279
280         caches->initialize([callback = WTFMove(callback), caches](std::optional<Error>&& error) mutable {
281             if (error) {
282                 callback(makeUnexpected(error.value()));
283                 return;
284             }
285
286             callback(std::reference_wrapper<Caches> { *caches });
287         });
288     });
289 }
290
291 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
292 {
293     // FIXME: Implement reading.
294     auto* cache = this->cache(cacheIdentifier);
295     if (!cache) {
296         callback(makeUnexpected(Error::Internal));
297         return;
298     }
299     callback(std::reference_wrapper<Cache> { *cache });
300 }
301
302 void Engine::writeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
303 {
304     // FIXME: Implement writing.
305     callback(WTFMove(recordsIdentifiers));
306 }
307
308 void Engine::removeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
309 {
310     // FIXME: Implement writing.
311     callback(WTFMove(recordsIdentifiers));
312 }
313
314 Cache* Engine::cache(uint64_t cacheIdentifier)
315 {
316     Cache* result = nullptr;
317     for (auto& caches : m_caches.values()) {
318         if ((result = caches->find(cacheIdentifier)))
319             break;
320     }
321     return result;
322 }
323
324 Vector<uint64_t> Engine::queryCache(const Vector<Record>& records, const WebCore::ResourceRequest& request, const WebCore::CacheQueryOptions& options)
325 {
326     if (!options.ignoreMethod && request.httpMethod() != "GET")
327         return { };
328
329     Vector<uint64_t> results;
330     for (const auto& record : records) {
331         if (WebCore::DOMCache::queryCacheMatch(request, record.request, record.response, options))
332             results.append(record.identifier);
333     }
334     return results;
335 }
336
337 void Engine::writeFile(const String& filename, NetworkCache::Data&& data, WebCore::DOMCache::CompletionCallback&& callback)
338 {
339     if (!shouldPersist()) {
340         callback(std::nullopt);
341         return;
342     }
343
344     m_ioQueue->dispatch([callback = WTFMove(callback), data = WTFMove(data), filename = filename.isolatedCopy()] () mutable {
345         auto channel = IOChannel::open(filename, IOChannel::Type::Create);
346         channel->write(0, data, nullptr, [callback = WTFMove(callback)](int error) mutable {
347             ASSERT(RunLoop::isMain());
348             if (error) {
349                 callback(Error::WriteDisk);
350                 return;
351             }
352             callback(std::nullopt);
353         });
354     });
355 }
356
357 void Engine::readFile(const String& filename, WTF::Function<void(const NetworkCache::Data&, int error)>&& callback)
358 {
359     if (!shouldPersist()) {
360         callback(Data { }, 0);
361         return;
362     }
363
364     m_ioQueue->dispatch([callback = WTFMove(callback), filename = filename.isolatedCopy()]() mutable {
365         auto channel = IOChannel::open(filename, IOChannel::Type::Read);
366         if (channel->fileDescriptor() < 0) {
367             RunLoop::main().dispatch([callback = WTFMove(callback)]() mutable {
368                 callback(Data { }, 0);
369             });
370             return;
371         }
372
373         channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [callback = WTFMove(callback)](const Data& data, int error) mutable {
374             // FIXME: We should do the decoding in the background thread.
375             ASSERT(RunLoop::isMain());
376             callback(data, error);
377         });
378     });
379 }
380
381 void Engine::removeFile(const String& filename)
382 {
383     if (!shouldPersist())
384         return;
385
386     m_ioQueue->dispatch([filename = filename.isolatedCopy()]() mutable {
387         WebCore::deleteFile(filename);
388     });
389 }
390
391 void Engine::clearMemoryRepresentation(const String& origin)
392 {
393     readCachesFromDisk(origin, [](CachesOrError&& result) {
394         if (!result.hasValue())
395             return;
396         result.value().get().clearMemoryRepresentation();
397     });
398 }
399
400 } // namespace CacheStorage
401
402 } // namespace WebKit