[CacheAPI] Introduce a WebKit::CacheStorage namespace
[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 <WebCore/CacheQueryOptions.h>
30 #include <pal/SessionID.h>
31 #include <wtf/MainThread.h>
32 #include <wtf/NeverDestroyed.h>
33 #include <wtf/text/StringHash.h>
34
35 namespace WebKit {
36
37 namespace CacheStorage {
38
39 static HashMap<PAL::SessionID, std::unique_ptr<Engine>>& globalEngineMap()
40 {
41     static NeverDestroyed<HashMap<PAL::SessionID, std::unique_ptr<Engine>>> map;
42     return map;
43 }
44
45 Engine& Engine::from(PAL::SessionID sessionID)
46 {
47     if (sessionID == PAL::SessionID::defaultSessionID())
48         return defaultEngine();
49
50     return *globalEngineMap().ensure(sessionID, [] {
51         return std::make_unique<Engine>();
52     }).iterator->value;
53 }
54
55 void Engine::destroyEngine(PAL::SessionID sessionID)
56 {
57     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
58     globalEngineMap().remove(sessionID);
59 }
60
61 Engine& Engine::defaultEngine()
62 {
63     static NeverDestroyed<std::unique_ptr<Engine>> defaultEngine = { std::make_unique<Engine>() };
64     return *defaultEngine.get();
65 }
66
67 void Engine::open(const String& origin, const String& cacheName, CacheIdentifierCallback&& callback)
68 {
69     readCachesFromDisk(origin, [this, cacheName, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
70         if (!cachesOrError.hasValue()) {
71             callback(makeUnexpected(cachesOrError.error()));
72             return;
73         }
74
75         auto& caches = cachesOrError.value().get();
76
77         auto position = caches.findMatching([&](const auto& item) { return item.name == cacheName; });
78         if (position == notFound) {
79             uint64_t cacheIdentifier = ++m_nextCacheIdentifier;
80             caches.append(Cache { cacheIdentifier, cacheName, Vector<Record>(), 0 });
81             writeCachesToDisk([cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
82                 if (error) {
83                     callback(makeUnexpected(error.value()));
84                     return;
85                 }
86                 callback(cacheIdentifier);
87             });
88         } else
89             callback(caches[position].identifier);
90
91     });
92 }
93
94 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
95 {
96     std::optional<Cache> removedCache;
97     for (auto& caches : m_caches.values()) {
98         auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
99         if (position != notFound) {
100             removedCache = WTFMove(caches[position]);
101             caches.remove(position);
102             break;
103         }
104     }
105     if (!removedCache) {
106         callback(makeUnexpected(Error::Internal));
107         return;
108     }
109     m_removedCaches.append(WTFMove(removedCache.value()));
110     writeCachesToDisk([cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
111         if (error) {
112             callback(makeUnexpected(error.value()));
113             return;
114         }
115         callback(cacheIdentifier);
116     });
117 }
118
119 void Engine::retrieveCaches(const String& origin, CacheInfosCallback&& callback)
120 {
121     readCachesFromDisk(origin, [this, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
122         if (!cachesOrError.hasValue()) {
123             callback(makeUnexpected(cachesOrError.error()));
124             return;
125         }
126
127         auto& caches = cachesOrError.value().get();
128
129         Vector<WebCore::CacheStorageConnection::CacheInfo> cachesInfo;
130         cachesInfo.reserveInitialCapacity(caches.size());
131         for (auto& cache : caches)
132             cachesInfo.uncheckedAppend(WebCore::CacheStorageConnection::CacheInfo { cache.identifier, cache.name});
133
134         callback(WTFMove(cachesInfo));
135     });
136 }
137
138 void Engine::retrieveRecords(uint64_t cacheIdentifier, RecordsCallback&& callback)
139 {
140     readCache(cacheIdentifier, [callback = WTFMove(callback)](CacheOrError&& result) mutable {
141         if (!result.hasValue()) {
142             callback(makeUnexpected(result.error()));
143             return;
144         }
145         // FIXME: Pass records by reference.
146         auto& records = result.value().get().records;
147
148         Vector<Record> copy;
149         copy.reserveInitialCapacity(records.size());
150         for (auto& record : result.value().get().records)
151             copy.uncheckedAppend(record.copy());
152
153         callback(WTFMove(copy));
154     });
155 }
156
157 void Engine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
158 {
159     readCache(cacheIdentifier, [this, cacheIdentifier, records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
160         if (!result.hasValue()) {
161             callback(makeUnexpected(result.error()));
162             return;
163         }
164
165         Cache& cache = result.value();
166
167         WebCore::CacheQueryOptions options;
168         Vector<uint64_t> recordIdentifiers;
169         recordIdentifiers.reserveInitialCapacity(records.size());
170         for (auto& record : records) {
171             auto matchingRecords = Engine::queryCache(cache.records, record.request, options);
172             if (matchingRecords.isEmpty()) {
173                 record.identifier = ++cache.nextRecordIdentifier;
174                 recordIdentifiers.uncheckedAppend(record.identifier);
175                 cache.records.append(WTFMove(record));
176             } else {
177                 auto identifier = matchingRecords[0];
178                 auto position = cache.records.findMatching([&](const auto& item) { return item.identifier == identifier; });
179                 ASSERT(position != notFound);
180                 if (position != notFound) {
181                     auto& existingRecord = cache.records[position];
182                     recordIdentifiers.uncheckedAppend(identifier);
183                     existingRecord.responseHeadersGuard = record.responseHeadersGuard;
184                     existingRecord.response = WTFMove(record.response);
185                     existingRecord.responseBody = WTFMove(record.responseBody);
186                     ++existingRecord.updateResponseCounter;
187                 }
188             }
189         }
190         writeCacheRecords(cacheIdentifier, WTFMove(recordIdentifiers), [cacheIdentifier, callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
191             callback(WTFMove(result));
192         });
193     });
194 }
195
196 void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
197 {
198     readCache(cacheIdentifier, [this, cacheIdentifier, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
199         if (!result.hasValue()) {
200             callback(makeUnexpected(result.error()));
201             return;
202         }
203
204         auto& currentRecords = result.value().get().records;
205
206         auto recordsToRemove = queryCache(currentRecords, request, options);
207         if (recordsToRemove.isEmpty()) {
208             callback({ });
209             return;
210         }
211
212         Vector<Record> recordsToKeep;
213         for (auto& record : currentRecords) {
214             if (recordsToRemove.findMatching([&](auto item) { return item == record.identifier; }) == notFound)
215                 recordsToKeep.append(record.copy());
216         }
217         removeCacheRecords(cacheIdentifier, WTFMove(recordsToRemove), [this, cacheIdentifier, recordsToKeep = WTFMove(recordsToKeep), callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
218             if (!result.hasValue()) {
219                 callback(makeUnexpected(result.error()));
220                 return;
221             }
222
223             auto* writtenCache = cache(cacheIdentifier);
224             if (!writtenCache) {
225                 callback(makeUnexpected(Error::Internal));
226                 return;
227             }
228             writtenCache->records = WTFMove(recordsToKeep);
229
230             callback(WTFMove(result.value()));
231         });
232     });
233 }
234
235 void Engine::writeCachesToDisk(Function<void(std::optional<Error>&&)>&& callback)
236 {
237     // FIXME: Implement writing.
238     callback(std::nullopt);
239 }
240
241 void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
242 {
243     // FIXME: Implement reading.
244
245     auto& caches = m_caches.ensure(origin, [] {
246         return Vector<Cache>();
247     }).iterator->value;
248
249     callback(std::reference_wrapper<Vector<Cache>> { caches });
250 }
251
252 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
253 {
254     // FIXME: Implement reading.
255     auto* cache = this->cache(cacheIdentifier);
256     if (!cache) {
257         callback(makeUnexpected(Error::Internal));
258         return;
259     }
260     callback(std::reference_wrapper<Cache> { *cache });
261 }
262
263 void Engine::writeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
264 {
265     // FIXME: Implement writing.
266     callback(WTFMove(recordsIdentifiers));
267 }
268
269 void Engine::removeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
270 {
271     // FIXME: Implement writing.
272     callback(WTFMove(recordsIdentifiers));
273 }
274
275 Cache* Engine::cache(uint64_t cacheIdentifier)
276 {
277     Cache* result = nullptr;
278     for (auto& caches : m_caches.values()) {
279         auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
280         if (position != notFound) {
281             result = &caches[position];
282             break;
283         }
284     }
285     if (!result) {
286         auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
287         if (position != notFound)
288             result = &m_removedCaches[position];
289     }
290     return result;
291 }
292
293 Vector<uint64_t> Engine::queryCache(const Vector<Record>& records, const WebCore::ResourceRequest& request, const WebCore::CacheQueryOptions& options)
294 {
295     if (!options.ignoreMethod && request.httpMethod() != "GET")
296         return { };
297
298     Vector<uint64_t> results;
299     for (const auto& record : records) {
300         if (WebCore::CacheStorageConnection::queryCacheMatch(request, record.request, record.response, options))
301             results.append(record.identifier);
302     }
303     return results;
304 }
305
306 } // namespace CacheStorage
307
308 } // namespace WebKit
309