505637174534588ed10d27a1c745f0f10b856c5c
[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 static HashMap<PAL::SessionID, std::unique_ptr<CacheStorageEngine>>& globalEngineMap()
38 {
39     static NeverDestroyed<HashMap<PAL::SessionID, std::unique_ptr<CacheStorageEngine>>> map;
40     return map;
41 }
42
43 CacheStorageEngine& CacheStorageEngine::from(PAL::SessionID sessionID)
44 {
45     if (sessionID == PAL::SessionID::defaultSessionID())
46         return defaultEngine();
47
48     return *globalEngineMap().ensure(sessionID, [] {
49         return std::make_unique<CacheStorageEngine>();
50     }).iterator->value;
51 }
52
53 void CacheStorageEngine::destroyEngine(PAL::SessionID sessionID)
54 {
55     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
56     globalEngineMap().remove(sessionID);
57 }
58
59 CacheStorageEngine& CacheStorageEngine::defaultEngine()
60 {
61     static NeverDestroyed<std::unique_ptr<CacheStorageEngine>> defaultEngine = { std::make_unique<CacheStorageEngine>() };
62     return *defaultEngine.get();
63 }
64
65 void CacheStorageEngine::open(const String& origin, const String& cacheName, CacheIdentifierCallback&& callback)
66 {
67     readCachesFromDisk([this, origin, cacheName, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
68         if (error) {
69             callback(makeUnexpected(error.value()));
70             return;
71         }
72
73         auto& caches = m_caches.ensure(origin, [] {
74             return Vector<Cache>();
75         }).iterator->value;
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 CacheStorageEngine::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 CacheStorageEngine::retrieveCaches(const String& origin, CacheInfosCallback&& callback)
120 {
121     readCachesFromDisk([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
122         if (error) {
123             callback(makeUnexpected(error.value()));
124             return;
125         }
126         callback(caches(origin));
127     });
128 }
129
130 void CacheStorageEngine::retrieveRecords(uint64_t cacheIdentifier, RecordsCallback&& callback)
131 {
132     readCache(cacheIdentifier, [callback = WTFMove(callback)](CacheOrError&& result) mutable {
133         if (!result.hasValue()) {
134             callback(makeUnexpected(result.error()));
135             return;
136         }
137         callback(result.value().get().records);
138     });
139 }
140
141 void CacheStorageEngine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
142 {
143     readCache(cacheIdentifier, [this, cacheIdentifier, records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
144         if (!result.hasValue()) {
145             callback(makeUnexpected(result.error()));
146             return;
147         }
148
149         Cache& cache = result.value();
150
151         WebCore::CacheQueryOptions options;
152         Vector<uint64_t> recordIdentifiers;
153         recordIdentifiers.reserveInitialCapacity(records.size());
154         for (auto& record : records) {
155             auto matchingRecords = CacheStorageEngine::queryCache(cache.records, record.request, options);
156             if (matchingRecords.isEmpty()) {
157                 record.identifier = ++cache.nextRecordIdentifier;
158                 recordIdentifiers.uncheckedAppend(record.identifier);
159                 cache.records.append(WTFMove(record));
160             } else {
161                 auto identifier = matchingRecords[0];
162                 auto position = cache.records.findMatching([&](const auto& item) { return item.identifier == identifier; });
163                 ASSERT(position != notFound);
164                 if (position != notFound) {
165                     auto& existingRecord = cache.records[position];
166                     recordIdentifiers.uncheckedAppend(identifier);
167                     existingRecord.responseHeadersGuard = record.responseHeadersGuard;
168                     existingRecord.response = WTFMove(record.response);
169                 }
170             }
171         }
172         writeCacheRecords(cacheIdentifier, WTFMove(recordIdentifiers), [cacheIdentifier, callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
173             callback(WTFMove(result));
174         });
175     });
176 }
177
178 void CacheStorageEngine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
179 {
180     readCache(cacheIdentifier, [this, cacheIdentifier, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
181         if (!result.hasValue()) {
182             callback(makeUnexpected(result.error()));
183             return;
184         }
185
186         auto& currentRecords = result.value().get().records;
187
188         auto recordsToRemove = queryCache(currentRecords, request, options);
189         if (recordsToRemove.isEmpty()) {
190             callback({ });
191             return;
192         }
193
194         Vector<Record> recordsToKeep;
195         for (auto& record : currentRecords) {
196             if (recordsToRemove.findMatching([&](auto item) { return item == record.identifier; }) == notFound)
197                 recordsToKeep.append(record);
198         }
199         removeCacheRecords(cacheIdentifier, WTFMove(recordsToRemove), [this, cacheIdentifier, recordsToKeep = WTFMove(recordsToKeep), callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
200             if (!result.hasValue()) {
201                 callback(makeUnexpected(result.error()));
202                 return;
203             }
204
205             auto* writtenCache = cache(cacheIdentifier);
206             if (!writtenCache) {
207                 callback(makeUnexpected(Error::Internal));
208                 return;
209             }
210             writtenCache->records = WTFMove(recordsToKeep);
211
212             callback(WTFMove(result.value()));
213         });
214     });
215 }
216
217 void CacheStorageEngine::writeCachesToDisk(Function<void(std::optional<Error>&&)>&& callback)
218 {
219     // FIXME: Implement writing.
220     callback(std::nullopt);
221 }
222
223 void CacheStorageEngine::readCachesFromDisk(CompletionCallback&& callback)
224 {
225     // FIXME: Implement reading.
226     callback(std::nullopt);
227 }
228
229 void CacheStorageEngine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
230 {
231     // FIXME: Implement reading.
232     auto* cache = this->cache(cacheIdentifier);
233     if (!cache) {
234         callback(makeUnexpected(Error::Internal));
235         return;
236     }
237     callback(std::reference_wrapper<Cache> { *cache });
238 }
239
240 void CacheStorageEngine::writeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
241 {
242     // FIXME: Implement writing.
243     callback(WTFMove(recordsIdentifiers));
244 }
245
246 void CacheStorageEngine::removeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
247 {
248     // FIXME: Implement writing.
249     callback(WTFMove(recordsIdentifiers));
250 }
251
252 CacheStorageEngine::Cache* CacheStorageEngine::cache(uint64_t cacheIdentifier)
253 {
254     Cache* result = nullptr;
255     for (auto& caches : m_caches.values()) {
256         auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
257         if (position != notFound) {
258             result = &caches[position];
259             break;
260         }
261     }
262     if (!result) {
263         auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
264         if (position != notFound)
265             result = &m_removedCaches[position];
266     }
267     return result;
268 }
269
270 Vector<WebCore::CacheStorageConnection::CacheInfo> CacheStorageEngine::caches(const String& origin) const
271 {
272     auto iterator = m_caches.find(origin);
273     if (iterator == m_caches.end())
274         return { };
275
276     Vector<WebCore::CacheStorageConnection::CacheInfo> cachesInfo;
277     cachesInfo.reserveInitialCapacity(iterator->value.size());
278     for (auto& cache : iterator->value)
279         cachesInfo.uncheckedAppend(WebCore::CacheStorageConnection::CacheInfo { cache.identifier, cache.name});
280
281     return cachesInfo;
282 }
283
284 Vector<uint64_t> CacheStorageEngine::queryCache(const Vector<Record>& records, const WebCore::ResourceRequest& request, const WebCore::CacheQueryOptions& options)
285 {
286     if (!options.ignoreMethod && request.httpMethod() != "GET")
287         return { };
288
289     Vector<uint64_t> results;
290     for (const auto& record : records) {
291         if (WebCore::CacheStorageConnection::queryCacheMatch(request, record.request, record.response, options))
292             results.append(record.identifier);
293     }
294     return results;
295 }
296
297 }