[Cache API] Add support for overwriting responses with put on an existing record
[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         // FIXME: Pass records by reference.
138         auto& records = result.value().get().records;
139
140         Vector<Record> copy;
141         copy.reserveInitialCapacity(records.size());
142         for (auto& record : result.value().get().records)
143             copy.uncheckedAppend(record.copy());
144
145         callback(WTFMove(copy));
146     });
147 }
148
149 void CacheStorageEngine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
150 {
151     readCache(cacheIdentifier, [this, cacheIdentifier, records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
152         if (!result.hasValue()) {
153             callback(makeUnexpected(result.error()));
154             return;
155         }
156
157         Cache& cache = result.value();
158
159         WebCore::CacheQueryOptions options;
160         Vector<uint64_t> recordIdentifiers;
161         recordIdentifiers.reserveInitialCapacity(records.size());
162         for (auto& record : records) {
163             auto matchingRecords = CacheStorageEngine::queryCache(cache.records, record.request, options);
164             if (matchingRecords.isEmpty()) {
165                 record.identifier = ++cache.nextRecordIdentifier;
166                 recordIdentifiers.uncheckedAppend(record.identifier);
167                 cache.records.append(WTFMove(record));
168             } else {
169                 auto identifier = matchingRecords[0];
170                 auto position = cache.records.findMatching([&](const auto& item) { return item.identifier == identifier; });
171                 ASSERT(position != notFound);
172                 if (position != notFound) {
173                     auto& existingRecord = cache.records[position];
174                     recordIdentifiers.uncheckedAppend(identifier);
175                     existingRecord.responseHeadersGuard = record.responseHeadersGuard;
176                     existingRecord.response = WTFMove(record.response);
177                     existingRecord.responseBody = WTFMove(record.responseBody);
178                     ++existingRecord.updateResponseCounter;
179                 }
180             }
181         }
182         writeCacheRecords(cacheIdentifier, WTFMove(recordIdentifiers), [cacheIdentifier, callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
183             callback(WTFMove(result));
184         });
185     });
186 }
187
188 void CacheStorageEngine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
189 {
190     readCache(cacheIdentifier, [this, cacheIdentifier, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
191         if (!result.hasValue()) {
192             callback(makeUnexpected(result.error()));
193             return;
194         }
195
196         auto& currentRecords = result.value().get().records;
197
198         auto recordsToRemove = queryCache(currentRecords, request, options);
199         if (recordsToRemove.isEmpty()) {
200             callback({ });
201             return;
202         }
203
204         Vector<Record> recordsToKeep;
205         for (auto& record : currentRecords) {
206             if (recordsToRemove.findMatching([&](auto item) { return item == record.identifier; }) == notFound)
207                 recordsToKeep.append(record.copy());
208         }
209         removeCacheRecords(cacheIdentifier, WTFMove(recordsToRemove), [this, cacheIdentifier, recordsToKeep = WTFMove(recordsToKeep), callback = WTFMove(callback)](RecordIdentifiersOrError&& result) mutable {
210             if (!result.hasValue()) {
211                 callback(makeUnexpected(result.error()));
212                 return;
213             }
214
215             auto* writtenCache = cache(cacheIdentifier);
216             if (!writtenCache) {
217                 callback(makeUnexpected(Error::Internal));
218                 return;
219             }
220             writtenCache->records = WTFMove(recordsToKeep);
221
222             callback(WTFMove(result.value()));
223         });
224     });
225 }
226
227 void CacheStorageEngine::writeCachesToDisk(Function<void(std::optional<Error>&&)>&& callback)
228 {
229     // FIXME: Implement writing.
230     callback(std::nullopt);
231 }
232
233 void CacheStorageEngine::readCachesFromDisk(CompletionCallback&& callback)
234 {
235     // FIXME: Implement reading.
236     callback(std::nullopt);
237 }
238
239 void CacheStorageEngine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
240 {
241     // FIXME: Implement reading.
242     auto* cache = this->cache(cacheIdentifier);
243     if (!cache) {
244         callback(makeUnexpected(Error::Internal));
245         return;
246     }
247     callback(std::reference_wrapper<Cache> { *cache });
248 }
249
250 void CacheStorageEngine::writeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
251 {
252     // FIXME: Implement writing.
253     callback(WTFMove(recordsIdentifiers));
254 }
255
256 void CacheStorageEngine::removeCacheRecords(uint64_t cacheIdentifier, Vector<uint64_t>&& recordsIdentifiers, RecordIdentifiersCallback&& callback)
257 {
258     // FIXME: Implement writing.
259     callback(WTFMove(recordsIdentifiers));
260 }
261
262 CacheStorageEngine::Cache* CacheStorageEngine::cache(uint64_t cacheIdentifier)
263 {
264     Cache* result = nullptr;
265     for (auto& caches : m_caches.values()) {
266         auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
267         if (position != notFound) {
268             result = &caches[position];
269             break;
270         }
271     }
272     if (!result) {
273         auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
274         if (position != notFound)
275             result = &m_removedCaches[position];
276     }
277     return result;
278 }
279
280 Vector<WebCore::CacheStorageConnection::CacheInfo> CacheStorageEngine::caches(const String& origin) const
281 {
282     auto iterator = m_caches.find(origin);
283     if (iterator == m_caches.end())
284         return { };
285
286     Vector<WebCore::CacheStorageConnection::CacheInfo> cachesInfo;
287     cachesInfo.reserveInitialCapacity(iterator->value.size());
288     for (auto& cache : iterator->value)
289         cachesInfo.uncheckedAppend(WebCore::CacheStorageConnection::CacheInfo { cache.identifier, cache.name});
290
291     return cachesInfo;
292 }
293
294 Vector<uint64_t> CacheStorageEngine::queryCache(const Vector<Record>& records, const WebCore::ResourceRequest& request, const WebCore::CacheQueryOptions& options)
295 {
296     if (!options.ignoreMethod && request.httpMethod() != "GET")
297         return { };
298
299     Vector<uint64_t> results;
300     for (const auto& record : records) {
301         if (WebCore::CacheStorageConnection::queryCacheMatch(request, record.request, record.response, options))
302             results.append(record.identifier);
303     }
304     return results;
305 }
306
307 }