1/*
2 * Copyright (C) 2014-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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "NetworkCacheBlobStorage.h"
29#include "NetworkCacheData.h"
30#include "NetworkCacheKey.h"
31#include <WebCore/Timer.h>
32#include <wtf/BloomFilter.h>
33#include <wtf/CompletionHandler.h>
34#include <wtf/Deque.h>
35#include <wtf/Function.h>
36#include <wtf/HashSet.h>
37#include <wtf/MonotonicTime.h>
38#include <wtf/Optional.h>
39#include <wtf/WallTime.h>
40#include <wtf/WorkQueue.h>
41#include <wtf/text/WTFString.h>
42
43namespace WebKit {
44namespace NetworkCache {
45
46class IOChannel;
47
48class Storage : public ThreadSafeRefCounted<Storage, WTF::DestructionThread::Main> {
49public:
50 enum class Mode { Normal, AvoidRandomness };
51 static RefPtr<Storage> open(const String& cachePath, Mode);
52
53 struct Record {
54 Key key;
55 WallTime timeStamp;
56 Data header;
57 Data body;
58 Optional<SHA1::Digest> bodyHash;
59
60 WTF_MAKE_FAST_ALLOCATED;
61 };
62
63 struct Timings {
64 MonotonicTime startTime;
65 MonotonicTime dispatchTime;
66 MonotonicTime recordIOStartTime;
67 MonotonicTime recordIOEndTime;
68 MonotonicTime blobIOStartTime;
69 MonotonicTime blobIOEndTime;
70 MonotonicTime completionTime;
71 size_t dispatchCountAtStart { 0 };
72 size_t dispatchCountAtDispatch { 0 };
73 bool synchronizationInProgressAtDispatch { false };
74 bool shrinkInProgressAtDispatch { false };
75 bool wasCanceled { false };
76
77 WTF_MAKE_FAST_ALLOCATED;
78 };
79
80 // This may call completion handler synchronously on failure.
81 using RetrieveCompletionHandler = CompletionHandler<bool(std::unique_ptr<Record>, const Timings&)>;
82 void retrieve(const Key&, unsigned priority, RetrieveCompletionHandler&&);
83
84 using MappedBodyHandler = Function<void (const Data& mappedBody)>;
85 void store(const Record&, MappedBodyHandler&&, CompletionHandler<void(int)>&& = { });
86
87 void remove(const Key&);
88 void remove(const Vector<Key>&, CompletionHandler<void()>&&);
89 void clear(const String& type, WallTime modifiedSinceTime, CompletionHandler<void()>&&);
90
91 struct RecordInfo {
92 size_t bodySize;
93 double worth; // 0-1 where 1 is the most valuable.
94 unsigned bodyShareCount;
95 String bodyHash;
96 };
97 enum TraverseFlag {
98 ComputeWorth = 1 << 0,
99 ShareCount = 1 << 1,
100 };
101 using TraverseHandler = Function<void (const Record*, const RecordInfo&)>;
102 // Null record signals end.
103 void traverse(const String& type, OptionSet<TraverseFlag>, TraverseHandler&&);
104
105 void setCapacity(size_t);
106 size_t capacity() const { return m_capacity; }
107 size_t approximateSize() const;
108
109 // Incrementing this number will delete all existing cache content for everyone. Do you really need to do it?
110 static const unsigned version = 14;
111#if PLATFORM(MAC)
112 /// Allow the last stable version of the cache to co-exist with the latest development one.
113 static const unsigned lastStableVersion = 13;
114#endif
115
116 String basePath() const;
117 String versionPath() const;
118 String recordsPath() const;
119
120 const Salt& salt() const { return m_salt; }
121
122 ~Storage();
123
124 void writeWithoutWaiting() { m_initialWriteDelay = 0_s; };
125
126private:
127 Storage(const String& directoryPath, Mode, Salt);
128
129 String recordDirectoryPathForKey(const Key&) const;
130 String recordPathForKey(const Key&) const;
131 String blobPathForKey(const Key&) const;
132
133 void synchronize();
134 void deleteOldVersions();
135 void shrinkIfNeeded();
136 void shrink();
137
138 struct ReadOperation;
139 void dispatchReadOperation(std::unique_ptr<ReadOperation>);
140 void dispatchPendingReadOperations();
141 void finishReadOperation(ReadOperation&);
142 void cancelAllReadOperations();
143
144 struct WriteOperation;
145 void dispatchWriteOperation(std::unique_ptr<WriteOperation>);
146 void dispatchPendingWriteOperations();
147 void finishWriteOperation(WriteOperation&, int error = 0);
148
149 bool shouldStoreBodyAsBlob(const Data& bodyData);
150 Optional<BlobStorage::Blob> storeBodyAsBlob(WriteOperation&);
151 Data encodeRecord(const Record&, Optional<BlobStorage::Blob>);
152 void readRecord(ReadOperation&, const Data&);
153
154 void updateFileModificationTime(const String& path);
155 void removeFromPendingWriteOperations(const Key&);
156
157 WorkQueue& ioQueue() { return m_ioQueue.get(); }
158 WorkQueue& backgroundIOQueue() { return m_backgroundIOQueue.get(); }
159 WorkQueue& serialBackgroundIOQueue() { return m_serialBackgroundIOQueue.get(); }
160
161 bool mayContain(const Key&) const;
162 bool mayContainBlob(const Key&) const;
163
164 void addToRecordFilter(const Key&);
165 void deleteFiles(const Key&);
166
167 const String m_basePath;
168 const String m_recordsPath;
169
170 const Mode m_mode;
171 const Salt m_salt;
172
173 size_t m_capacity { std::numeric_limits<size_t>::max() };
174 size_t m_approximateRecordsSize { 0 };
175
176 // 2^18 bit filter can support up to 26000 entries with false positive rate < 1%.
177 using ContentsFilter = BloomFilter<18>;
178 std::unique_ptr<ContentsFilter> m_recordFilter;
179 std::unique_ptr<ContentsFilter> m_blobFilter;
180
181 bool m_synchronizationInProgress { false };
182 bool m_shrinkInProgress { false };
183 size_t m_readOperationDispatchCount { 0 };
184
185 Vector<Key::HashType> m_recordFilterHashesAddedDuringSynchronization;
186 Vector<Key::HashType> m_blobFilterHashesAddedDuringSynchronization;
187
188 static const int maximumRetrievePriority = 4;
189 Deque<std::unique_ptr<ReadOperation>> m_pendingReadOperationsByPriority[maximumRetrievePriority + 1];
190 HashSet<std::unique_ptr<ReadOperation>> m_activeReadOperations;
191 WebCore::Timer m_readOperationTimeoutTimer;
192
193 Deque<std::unique_ptr<WriteOperation>> m_pendingWriteOperations;
194 HashSet<std::unique_ptr<WriteOperation>> m_activeWriteOperations;
195 WebCore::Timer m_writeOperationDispatchTimer;
196
197 struct TraverseOperation;
198 HashSet<std::unique_ptr<TraverseOperation>> m_activeTraverseOperations;
199
200 Ref<WorkQueue> m_ioQueue;
201 Ref<WorkQueue> m_backgroundIOQueue;
202 Ref<WorkQueue> m_serialBackgroundIOQueue;
203
204 BlobStorage m_blobStorage;
205
206 // By default, delay the start of writes a bit to avoid affecting early page load.
207 // Completing writes will dispatch more writes without delay.
208 Seconds m_initialWriteDelay { 1_s };
209};
210
211}
212}
213