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