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 "CacheStorageEngineCaches.h" |
30 | #include "NetworkCacheCoders.h" |
31 | #include "NetworkCacheIOChannel.h" |
32 | #include "NetworkCacheKey.h" |
33 | #include "NetworkProcess.h" |
34 | #include "WebCoreArgumentCoders.h" |
35 | #include <WebCore/CacheQueryOptions.h> |
36 | #include <WebCore/HTTPParsers.h> |
37 | #include <pal/SessionID.h> |
38 | #include <wtf/MainThread.h> |
39 | #include <wtf/NeverDestroyed.h> |
40 | #include <wtf/UUID.h> |
41 | #include <wtf/persistence/PersistentCoders.h> |
42 | #include <wtf/persistence/PersistentDecoder.h> |
43 | #include <wtf/persistence/PersistentEncoder.h> |
44 | #include <wtf/text/StringBuilder.h> |
45 | #include <wtf/text/StringHash.h> |
46 | |
47 | namespace WebKit { |
48 | |
49 | namespace CacheStorage { |
50 | |
51 | using namespace WebCore; |
52 | using namespace WebCore::DOMCacheEngine; |
53 | using namespace NetworkCache; |
54 | |
55 | static inline String computeKeyURL(const URL& url) |
56 | { |
57 | URL keyURL { url }; |
58 | keyURL.removeQueryAndFragmentIdentifier(); |
59 | return keyURL.string(); |
60 | } |
61 | |
62 | static inline Vector<uint64_t> queryCache(const Vector<RecordInformation>* records, const ResourceRequest& request, const CacheQueryOptions& options) |
63 | { |
64 | if (!records) |
65 | return { }; |
66 | |
67 | if (!options.ignoreMethod && request.httpMethod() != "GET" ) |
68 | return { }; |
69 | |
70 | Vector<uint64_t> results; |
71 | for (const auto& record : *records) { |
72 | if (WebCore::DOMCacheEngine::queryCacheMatch(request, record.url, record.hasVaryStar, record.varyHeaders, options)) |
73 | results.append(record.identifier); |
74 | } |
75 | return results; |
76 | } |
77 | |
78 | static inline void updateVaryInformation(RecordInformation& recordInformation, const ResourceRequest& request, const ResourceResponse& response) |
79 | { |
80 | auto varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary); |
81 | if (varyValue.isNull()) { |
82 | recordInformation.hasVaryStar = false; |
83 | recordInformation.varyHeaders = { }; |
84 | return; |
85 | } |
86 | |
87 | varyValue.split(',', [&](StringView view) { |
88 | if (!recordInformation.hasVaryStar && stripLeadingAndTrailingHTTPSpaces(view) == "*" ) |
89 | recordInformation.hasVaryStar = true; |
90 | String = view.toString(); |
91 | recordInformation.varyHeaders.add(headerName, request.httpHeaderField(headerName)); |
92 | }); |
93 | |
94 | if (recordInformation.hasVaryStar) |
95 | recordInformation.varyHeaders = { }; |
96 | } |
97 | |
98 | RecordInformation Cache::toRecordInformation(const Record& record) |
99 | { |
100 | Key key { "record"_s , m_uniqueName, { }, createCanonicalUUIDString(), m_caches.salt() }; |
101 | RecordInformation recordInformation { WTFMove(key), MonotonicTime::now().secondsSinceEpoch().milliseconds(), record.identifier, 0 , record.responseBodySize, record.request.url(), false, { } }; |
102 | |
103 | updateVaryInformation(recordInformation, record.request, record.response); |
104 | |
105 | return recordInformation; |
106 | } |
107 | |
108 | Cache::Cache(Caches& caches, uint64_t identifier, State state, String&& name, String&& uniqueName) |
109 | : m_caches(caches) |
110 | , m_state(state) |
111 | , m_identifier(identifier) |
112 | , m_name(WTFMove(name)) |
113 | , m_uniqueName(WTFMove(uniqueName)) |
114 | { |
115 | } |
116 | |
117 | void Cache::dispose() |
118 | { |
119 | m_caches.dispose(*this); |
120 | } |
121 | |
122 | void Cache::clearMemoryRepresentation() |
123 | { |
124 | m_records = { }; |
125 | m_nextRecordIdentifier = 0; |
126 | m_state = State::Uninitialized; |
127 | } |
128 | |
129 | static RecordInformation isolatedCopy(const RecordInformation& information) |
130 | { |
131 | auto result = RecordInformation { information.key, information.insertionTime, information.identifier, information.updateResponseCounter, information.size, information.url.isolatedCopy(), information.hasVaryStar, { } }; |
132 | HashMap<String, String> ; |
133 | for (const auto& keyValue : information.varyHeaders) |
134 | varyHeaders.set(keyValue.key.isolatedCopy(), keyValue.value.isolatedCopy()); |
135 | result.varyHeaders = WTFMove(varyHeaders); |
136 | return result; |
137 | } |
138 | |
139 | struct TraversalResult { |
140 | uint64_t cacheIdentifier; |
141 | HashMap<String, Vector<RecordInformation>> records; |
142 | Vector<Key> failedRecords; |
143 | }; |
144 | |
145 | static TraversalResult isolatedCopy(TraversalResult&& result) |
146 | { |
147 | HashMap<String, Vector<RecordInformation>> isolatedRecords; |
148 | for (auto& keyValue : result.records) { |
149 | auto& recordVector = keyValue.value; |
150 | for (size_t cptr = 0; cptr < recordVector.size(); cptr++) |
151 | recordVector[cptr] = isolatedCopy(recordVector[cptr]); |
152 | |
153 | isolatedRecords.set(keyValue.key.isolatedCopy(), WTFMove(recordVector)); |
154 | } |
155 | |
156 | // No need to isolate keys since they are isolated through the copy constructor |
157 | return TraversalResult { result.cacheIdentifier, WTFMove(isolatedRecords), WTFMove(result.failedRecords) }; |
158 | } |
159 | |
160 | void Cache::open(CompletionCallback&& callback) |
161 | { |
162 | if (m_state == State::Open) { |
163 | callback(WTF::nullopt); |
164 | return; |
165 | } |
166 | if (m_state == State::Opening) { |
167 | m_pendingOpeningCallbacks.append(WTFMove(callback)); |
168 | return; |
169 | } |
170 | m_state = State::Opening; |
171 | TraversalResult traversalResult { m_identifier, { }, { } }; |
172 | m_caches.readRecordsList(*this, [caches = makeRef(m_caches), callback = WTFMove(callback), traversalResult = WTFMove(traversalResult)](const auto* storageRecord, const auto&) mutable { |
173 | if (!storageRecord) { |
174 | RunLoop::main().dispatch([caches = WTFMove(caches), callback = WTFMove(callback), traversalResult = isolatedCopy(WTFMove(traversalResult)) ]() mutable { |
175 | for (auto& key : traversalResult.failedRecords) |
176 | caches->removeCacheEntry(key); |
177 | |
178 | auto* cache = caches->find(traversalResult.cacheIdentifier); |
179 | if (!cache) { |
180 | callback(Error::Internal); |
181 | return; |
182 | } |
183 | cache->m_records = WTFMove(traversalResult.records); |
184 | cache->finishOpening(WTFMove(callback), WTF::nullopt); |
185 | }); |
186 | return; |
187 | } |
188 | |
189 | auto decoded = decodeRecordHeader(*storageRecord); |
190 | if (!decoded) { |
191 | traversalResult.failedRecords.append(storageRecord->key); |
192 | return; |
193 | } |
194 | |
195 | auto& record = decoded->record; |
196 | auto insertionTime = decoded->insertionTime; |
197 | |
198 | RecordInformation recordInformation { storageRecord->key, insertionTime, 0, 0, record.responseBodySize, record.request.url(), false, { } }; |
199 | updateVaryInformation(recordInformation, record.request, record.response); |
200 | |
201 | auto& sameURLRecords = traversalResult.records.ensure(computeKeyURL(recordInformation.url), [] { return Vector<RecordInformation> { }; }).iterator->value; |
202 | sameURLRecords.append(WTFMove(recordInformation)); |
203 | }); |
204 | } |
205 | |
206 | void Cache::finishOpening(CompletionCallback&& callback, Optional<Error>&& error) |
207 | { |
208 | Vector<std::reference_wrapper<RecordInformation>> records; |
209 | for (auto& value : m_records.values()) { |
210 | for (auto& record : value) |
211 | records.append(record); |
212 | } |
213 | std::sort(records.begin(), records.end(), [&](const auto& a, const auto& b) { |
214 | return a.get().insertionTime < b.get().insertionTime; |
215 | }); |
216 | for (auto& record : records) |
217 | record.get().identifier = ++m_nextRecordIdentifier; |
218 | |
219 | if (error) { |
220 | m_state = State::Uninitialized; |
221 | callback(error.value()); |
222 | auto callbacks = WTFMove(m_pendingOpeningCallbacks); |
223 | for (auto& callback : callbacks) |
224 | callback(error.value()); |
225 | return; |
226 | } |
227 | m_state = State::Open; |
228 | |
229 | callback(WTF::nullopt); |
230 | auto callbacks = WTFMove(m_pendingOpeningCallbacks); |
231 | for (auto& callback : callbacks) |
232 | callback(WTF::nullopt); |
233 | } |
234 | |
235 | class ReadRecordTaskCounter : public RefCounted<ReadRecordTaskCounter> { |
236 | public: |
237 | using ReadRecordsCallback = WTF::Function<void(Vector<Record>&&, Vector<uint64_t>&&)>; |
238 | static Ref<ReadRecordTaskCounter> create(ReadRecordsCallback&& callback) { return adoptRef(*new ReadRecordTaskCounter(WTFMove(callback))); } |
239 | |
240 | ~ReadRecordTaskCounter() |
241 | { |
242 | ASSERT(RunLoop::isMain()); |
243 | if (!m_callback) |
244 | return; |
245 | std::sort(m_records.begin(), m_records.end(), [&] (const auto& a, const auto& b) { |
246 | return a.identifier < b.identifier; |
247 | }); |
248 | m_callback(WTFMove(m_records), WTFMove(m_failedRecords)); |
249 | } |
250 | |
251 | void appendRecord(Expected<Record, Error>&& result, uint64_t recordIdentifier, uint64_t updateCounter) |
252 | { |
253 | ASSERT(RunLoop::isMain()); |
254 | if (!result.has_value()) { |
255 | m_failedRecords.append(recordIdentifier); |
256 | return; |
257 | } |
258 | result.value().identifier = recordIdentifier; |
259 | result.value().updateResponseCounter = updateCounter; |
260 | m_records.append(WTFMove(result.value())); |
261 | } |
262 | |
263 | private: |
264 | explicit ReadRecordTaskCounter(ReadRecordsCallback&& callback) |
265 | : m_callback(WTFMove(callback)) |
266 | { |
267 | } |
268 | |
269 | ReadRecordsCallback m_callback; |
270 | Vector<Record> m_records; |
271 | Vector<uint64_t> m_failedRecords; |
272 | }; |
273 | |
274 | void Cache::retrieveRecord(const RecordInformation& record, Ref<ReadRecordTaskCounter>&& taskCounter) |
275 | { |
276 | readRecordFromDisk(record, [caches = makeRef(m_caches), identifier = m_identifier, recordIdentifier = record.identifier, updateCounter = record.updateResponseCounter, taskCounter = WTFMove(taskCounter)](Expected<Record, Error>&& result) mutable { |
277 | auto* cache = caches->find(identifier); |
278 | if (!cache) |
279 | return; |
280 | taskCounter->appendRecord(WTFMove(result), recordIdentifier, updateCounter); |
281 | }); |
282 | } |
283 | |
284 | void Cache::retrieveRecords(const URL& url, RecordsCallback&& callback) |
285 | { |
286 | ASSERT(m_state == State::Open); |
287 | |
288 | auto taskCounter = ReadRecordTaskCounter::create([caches = makeRef(m_caches), identifier = m_identifier, callback = WTFMove(callback)](Vector<Record>&& records, Vector<uint64_t>&& failedRecordIdentifiers) mutable { |
289 | auto* cache = caches->find(identifier); |
290 | if (cache) |
291 | cache->removeFromRecordList(failedRecordIdentifiers); |
292 | callback(WTFMove(records)); |
293 | }); |
294 | |
295 | if (url.isNull()) { |
296 | for (auto& records : m_records.values()) { |
297 | for (auto& record : records) |
298 | retrieveRecord(record, taskCounter.copyRef()); |
299 | } |
300 | return; |
301 | } |
302 | |
303 | auto* records = recordsFromURL(url); |
304 | if (!records) |
305 | return; |
306 | |
307 | for (auto& record : *records) |
308 | retrieveRecord(record, taskCounter.copyRef()); |
309 | } |
310 | |
311 | RecordInformation& Cache::addRecord(Vector<RecordInformation>* records, const Record& record) |
312 | { |
313 | if (!records) { |
314 | auto key = computeKeyURL(record.request.url()); |
315 | ASSERT(!m_records.contains(key)); |
316 | records = &m_records.set(key, Vector<RecordInformation> { }).iterator->value; |
317 | } |
318 | records->append(toRecordInformation(record)); |
319 | return records->last(); |
320 | } |
321 | |
322 | Vector<RecordInformation>* Cache::recordsFromURL(const URL& url) |
323 | { |
324 | auto iterator = m_records.find(computeKeyURL(url)); |
325 | if (iterator == m_records.end()) |
326 | return nullptr; |
327 | return &iterator->value; |
328 | } |
329 | |
330 | const Vector<RecordInformation>* Cache::recordsFromURL(const URL& url) const |
331 | { |
332 | auto iterator = m_records.find(computeKeyURL(url)); |
333 | if (iterator == m_records.end()) |
334 | return nullptr; |
335 | return &iterator->value; |
336 | } |
337 | |
338 | class AsynchronousPutTaskCounter : public RefCounted<AsynchronousPutTaskCounter> { |
339 | public: |
340 | static Ref<AsynchronousPutTaskCounter> create(RecordIdentifiersCallback&& callback) { return adoptRef(*new AsynchronousPutTaskCounter(WTFMove(callback))); } |
341 | ~AsynchronousPutTaskCounter() |
342 | { |
343 | ASSERT(RunLoop::isMain()); |
344 | if (!m_callback) |
345 | return; |
346 | if (m_error) { |
347 | m_callback(makeUnexpected(m_error.value())); |
348 | return; |
349 | } |
350 | m_callback(WTFMove(m_recordIdentifiers)); |
351 | } |
352 | |
353 | void setError(Error error) |
354 | { |
355 | ASSERT(RunLoop::isMain()); |
356 | if (m_error) |
357 | return; |
358 | |
359 | m_error = error; |
360 | } |
361 | |
362 | void addRecordIdentifier(uint64_t identifier) |
363 | { |
364 | m_recordIdentifiers.append(identifier); |
365 | } |
366 | |
367 | private: |
368 | explicit AsynchronousPutTaskCounter(RecordIdentifiersCallback&& callback) |
369 | : m_callback(WTFMove(callback)) |
370 | { |
371 | } |
372 | |
373 | Optional<Error> m_error; |
374 | RecordIdentifiersCallback m_callback; |
375 | Vector<uint64_t> m_recordIdentifiers; |
376 | }; |
377 | |
378 | void Cache::storeRecords(Vector<Record>&& records, RecordIdentifiersCallback&& callback) |
379 | { |
380 | auto taskCounter = AsynchronousPutTaskCounter::create(WTFMove(callback)); |
381 | |
382 | WebCore::CacheQueryOptions options; |
383 | for (auto& record : records) { |
384 | auto* sameURLRecords = recordsFromURL(record.request.url()); |
385 | auto matchingRecords = queryCache(sameURLRecords, record.request, options); |
386 | |
387 | auto position = !matchingRecords.isEmpty() ? sameURLRecords->findMatching([&](const auto& item) { return item.identifier == matchingRecords[0]; }) : notFound; |
388 | |
389 | if (position == notFound) { |
390 | record.identifier = ++m_nextRecordIdentifier; |
391 | taskCounter->addRecordIdentifier(record.identifier); |
392 | |
393 | auto& recordToWrite = addRecord(sameURLRecords, record); |
394 | writeRecordToDisk(recordToWrite, WTFMove(record), taskCounter.copyRef(), 0); |
395 | } else { |
396 | auto& existingRecord = sameURLRecords->at(position); |
397 | taskCounter->addRecordIdentifier(existingRecord.identifier); |
398 | updateRecordToDisk(existingRecord, WTFMove(record), taskCounter.copyRef()); |
399 | } |
400 | } |
401 | } |
402 | |
403 | void Cache::put(Vector<Record>&& records, RecordIdentifiersCallback&& callback) |
404 | { |
405 | ASSERT(m_state == State::Open); |
406 | |
407 | WebCore::CacheQueryOptions options; |
408 | uint64_t spaceRequired = 0; |
409 | |
410 | for (auto& record : records) { |
411 | auto* sameURLRecords = recordsFromURL(record.request.url()); |
412 | auto matchingRecords = queryCache(sameURLRecords, record.request, options); |
413 | |
414 | auto position = (sameURLRecords && !matchingRecords.isEmpty()) ? sameURLRecords->findMatching([&](const auto& item) { return item.identifier == matchingRecords[0]; }) : notFound; |
415 | |
416 | spaceRequired += record.responseBodySize; |
417 | if (position != notFound) |
418 | spaceRequired -= sameURLRecords->at(position).size; |
419 | } |
420 | |
421 | m_caches.requestSpace(spaceRequired, [caches = makeRef(m_caches), identifier = m_identifier, records = WTFMove(records), callback = WTFMove(callback)](Optional<DOMCacheEngine::Error>&& error) mutable { |
422 | if (error) { |
423 | callback(makeUnexpected(error.value())); |
424 | return; |
425 | } |
426 | auto* cache = caches->find(identifier); |
427 | if (!cache) { |
428 | callback(makeUnexpected(DOMCacheEngine::Error::Internal)); |
429 | return; |
430 | } |
431 | cache->storeRecords(WTFMove(records), WTFMove(callback)); |
432 | }); |
433 | } |
434 | |
435 | void Cache::remove(WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback) |
436 | { |
437 | ASSERT(m_state == State::Open); |
438 | |
439 | auto* records = recordsFromURL(request.url()); |
440 | auto recordIdentifiers = queryCache(records, request, options); |
441 | if (recordIdentifiers.isEmpty()) { |
442 | callback({ }); |
443 | return; |
444 | } |
445 | |
446 | records->removeAllMatching([this, &recordIdentifiers](auto& item) { |
447 | bool shouldRemove = recordIdentifiers.findMatching([&item](auto identifier) { return identifier == item.identifier; }) != notFound; |
448 | if (shouldRemove) |
449 | this->removeRecordFromDisk(item); |
450 | return shouldRemove; |
451 | }); |
452 | |
453 | callback(WTFMove(recordIdentifiers)); |
454 | } |
455 | |
456 | void Cache::removeFromRecordList(const Vector<uint64_t>& recordIdentifiers) |
457 | { |
458 | if (recordIdentifiers.isEmpty()) |
459 | return; |
460 | |
461 | for (auto& records : m_records.values()) { |
462 | auto* cache = this; |
463 | records.removeAllMatching([cache, &recordIdentifiers](const auto& item) { |
464 | return notFound != recordIdentifiers.findMatching([cache, &item](const auto& identifier) { |
465 | if (item.identifier != identifier) |
466 | return false; |
467 | cache->removeRecordFromDisk(item); |
468 | return true; |
469 | }); |
470 | }); |
471 | } |
472 | } |
473 | |
474 | void Cache::writeRecordToDisk(const RecordInformation& recordInformation, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter, uint64_t previousRecordSize) |
475 | { |
476 | m_caches.writeRecord(*this, recordInformation, WTFMove(record), previousRecordSize, [taskCounter = WTFMove(taskCounter)](Optional<Error>&& error) { |
477 | if (error) |
478 | taskCounter->setError(error.value()); |
479 | }); |
480 | } |
481 | |
482 | void Cache::updateRecordToDisk(RecordInformation& existingRecord, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter) |
483 | { |
484 | ++existingRecord.updateResponseCounter; |
485 | readRecordFromDisk(existingRecord, [caches = makeRef(m_caches), identifier = m_identifier, recordIdentifier = existingRecord.identifier, record = WTFMove(record), taskCounter = WTFMove(taskCounter)](Expected<Record, Error>&& result) mutable { |
486 | if (!result.has_value()) |
487 | return; |
488 | |
489 | auto* cache = caches->find(identifier); |
490 | if (!cache) |
491 | return; |
492 | |
493 | auto* sameURLRecords = cache->recordsFromURL(result.value().request.url()); |
494 | if (!sameURLRecords) |
495 | return; |
496 | |
497 | auto position = sameURLRecords->findMatching([&] (const auto& item) { return item.identifier == recordIdentifier; }); |
498 | if (position == notFound) |
499 | return; |
500 | auto& recordInfo = sameURLRecords->at(position); |
501 | auto previousSize = recordInfo.size; |
502 | recordInfo.size = record.responseBodySize; |
503 | |
504 | auto& recordFromDisk = result.value(); |
505 | record.requestHeadersGuard = recordFromDisk.requestHeadersGuard; |
506 | record.request = WTFMove(recordFromDisk.request); |
507 | record.options = WTFMove(recordFromDisk.options); |
508 | record.referrer = WTFMove(recordFromDisk.referrer); |
509 | |
510 | updateVaryInformation(recordInfo, record.request, record.response); |
511 | |
512 | cache->writeRecordToDisk(recordInfo, WTFMove(record), WTFMove(taskCounter), previousSize); |
513 | }); |
514 | } |
515 | |
516 | void Cache::readRecordFromDisk(const RecordInformation& record, WTF::Function<void(Expected<Record, Error>&&)>&& callback) |
517 | { |
518 | m_caches.readRecord(record.key, WTFMove(callback)); |
519 | } |
520 | |
521 | void Cache::removeRecordFromDisk(const RecordInformation& record) |
522 | { |
523 | m_caches.removeRecord(record); |
524 | } |
525 | |
526 | Storage::Record Cache::encode(const RecordInformation& recordInformation, const Record& record) |
527 | { |
528 | WTF::Persistence::Encoder encoder; |
529 | encoder << recordInformation.insertionTime; |
530 | encoder << recordInformation.size; |
531 | encoder << record.requestHeadersGuard; |
532 | record.request.encodeWithoutPlatformData(encoder); |
533 | record.options.encodePersistent(encoder); |
534 | encoder << record.referrer; |
535 | |
536 | encoder << record.responseHeadersGuard; |
537 | encoder << record.response; |
538 | encoder << record.responseBodySize; |
539 | |
540 | encoder.encodeChecksum(); |
541 | |
542 | Data (encoder.buffer(), encoder.bufferSize()); |
543 | Data body; |
544 | WTF::switchOn(record.responseBody, [](const Ref<WebCore::FormData>& formData) { |
545 | // FIXME: Store form data body. |
546 | }, [&](const Ref<WebCore::SharedBuffer>& buffer) { |
547 | body = { reinterpret_cast<const uint8_t*>(buffer->data()), buffer->size() }; |
548 | }, [](const std::nullptr_t&) { |
549 | }); |
550 | |
551 | return { recordInformation.key, { }, header, body, { } }; |
552 | } |
553 | |
554 | Optional<Cache::DecodedRecord> Cache::(const Storage::Record& storage) |
555 | { |
556 | WTF::Persistence::Decoder decoder(storage.header.data(), storage.header.size()); |
557 | |
558 | Record record; |
559 | |
560 | double insertionTime; |
561 | if (!decoder.decode(insertionTime)) |
562 | return WTF::nullopt; |
563 | |
564 | uint64_t size; |
565 | if (!decoder.decode(size)) |
566 | return WTF::nullopt; |
567 | |
568 | if (!decoder.decode(record.requestHeadersGuard)) |
569 | return WTF::nullopt; |
570 | |
571 | if (!record.request.decodeWithoutPlatformData(decoder)) |
572 | return WTF::nullopt; |
573 | |
574 | if (!FetchOptions::decodePersistent(decoder, record.options)) |
575 | return WTF::nullopt; |
576 | |
577 | if (!decoder.decode(record.referrer)) |
578 | return WTF::nullopt; |
579 | |
580 | if (!decoder.decode(record.responseHeadersGuard)) |
581 | return WTF::nullopt; |
582 | |
583 | if (!decoder.decode(record.response)) |
584 | return WTF::nullopt; |
585 | |
586 | if (!decoder.decode(record.responseBodySize)) |
587 | return WTF::nullopt; |
588 | |
589 | if (!decoder.verifyChecksum()) |
590 | return WTF::nullopt; |
591 | |
592 | return DecodedRecord { insertionTime, size, WTFMove(record) }; |
593 | } |
594 | |
595 | Optional<Record> Cache::decode(const Storage::Record& storage) |
596 | { |
597 | auto result = decodeRecordHeader(storage); |
598 | |
599 | if (!result) |
600 | return WTF::nullopt; |
601 | |
602 | auto record = WTFMove(result->record); |
603 | record.responseBody = WebCore::SharedBuffer::create(storage.body.data(), storage.body.size()); |
604 | |
605 | return record; |
606 | } |
607 | |
608 | Vector<Key> Cache::keys() const |
609 | { |
610 | Vector<Key> keys; |
611 | for (auto& records : m_records.values()) { |
612 | for (auto& record : records) |
613 | keys.append(record.key); |
614 | } |
615 | return keys; |
616 | } |
617 | |
618 | } // namespace CacheStorage |
619 | |
620 | } // namespace WebKit |
621 | |