Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / platform / network / curl / CurlCacheManager.cpp
1 /*
2  * Copyright (C) 2013 University of Szeged
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if USE(CURL)
30
31 #include "CurlCacheManager.h"
32
33 #include "FileSystem.h"
34 #include "HTTPHeaderMap.h"
35 #include "Logging.h"
36 #include "ResourceHandleClient.h"
37 #include "ResourceHandleInternal.h"
38 #include "ResourceRequest.h"
39 #include <wtf/HashMap.h>
40 #include <wtf/text/CString.h>
41
42 #define IO_BUFFERSIZE 4096
43
44 namespace WebCore {
45
46 CurlCacheManager& CurlCacheManager::getInstance()
47 {
48     static CurlCacheManager instance;
49     return instance;
50 }
51
52 CurlCacheManager::CurlCacheManager()
53     : m_disabled(true)
54     , m_currentStorageSize(0)
55     , m_storageSizeLimit(52428800) // 50 * 1024 * 1024 bytes
56 {
57     // Call setCacheDirectory() to enable the Cache Manager
58 }
59
60 CurlCacheManager::~CurlCacheManager()
61 {
62     if (m_disabled)
63         return;
64
65     saveIndex();
66 }
67
68 void CurlCacheManager::setCacheDirectory(const String& directory)
69 {
70     m_cacheDir = directory;
71
72     if (m_cacheDir.isEmpty()) {
73         LOG(Network, "Cache Error: Cache location is not set! CacheManager disabled.\n");
74         m_disabled = true;
75         return;
76     }
77
78     if (!fileExists(m_cacheDir)) {
79         if (!makeAllDirectories(m_cacheDir)) {
80             LOG(Network, "Cache Error: Could not open or create cache directory! CacheManager disabled.\n");
81             m_disabled = true;
82             return;
83         }
84     }
85
86     m_cacheDir.append("/");
87
88     m_disabled = false;
89     loadIndex();
90 }
91
92 void CurlCacheManager::setStorageSizeLimit(size_t sizeLimit)
93 {
94     m_storageSizeLimit = sizeLimit;
95 }
96
97 void CurlCacheManager::loadIndex()
98 {
99     if (m_disabled)
100         return;
101
102     String indexFilePath(m_cacheDir);
103     indexFilePath.append("index.dat");
104
105     PlatformFileHandle indexFile = openFile(indexFilePath, OpenForRead);
106     if (!isHandleValid(indexFile)) {
107         LOG(Network, "Cache Warning: Could not open %s for read\n", indexFilePath.latin1().data());
108         return;
109     }
110
111     long long filesize = -1;
112     if (!getFileSize(indexFilePath, filesize)) {
113         LOG(Network, "Cache Error: Could not get file size of %s\n", indexFilePath.latin1().data());
114         return;
115     }
116
117     // Load the file content into buffer
118     Vector<char> buffer;
119     buffer.resize(filesize);
120     int bufferPosition = 0;
121     int bufferReadSize = IO_BUFFERSIZE;
122     while (filesize > bufferPosition) {
123         if (filesize - bufferPosition < bufferReadSize)
124             bufferReadSize = filesize - bufferPosition;
125
126         readFromFile(indexFile, buffer.data() + bufferPosition, bufferReadSize);
127         bufferPosition += bufferReadSize;
128     }
129     closeFile(indexFile);
130
131     // Create strings from buffer
132     String headerContent = String(buffer.data(), buffer.size());
133     Vector<String> indexURLs;
134     headerContent.split('\n', indexURLs);
135     buffer.clear();
136
137     // Add entries to index
138     Vector<String>::const_iterator it = indexURLs.begin();
139     Vector<String>::const_iterator end = indexURLs.end();
140     if (indexURLs.size() > 1)
141         --end; // Last line is empty
142     while (it != end) {
143         String url = it->stripWhiteSpace();
144         auto cacheEntry = std::make_unique<CurlCacheEntry>(url, nullptr, m_cacheDir);
145
146         if (cacheEntry->isCached() && cacheEntry->entrySize() < m_storageSizeLimit) {
147             m_currentStorageSize += cacheEntry->entrySize();
148             makeRoomForNewEntry();
149             m_LRUEntryList.prependOrMoveToFirst(url);
150             m_index.set(url, WTFMove(cacheEntry));
151         } else
152             cacheEntry->invalidate();
153
154         ++it;
155     }
156 }
157
158 void CurlCacheManager::saveIndex()
159 {
160     if (m_disabled)
161         return;
162
163     String indexFilePath(m_cacheDir);
164     indexFilePath.append("index.dat");
165
166     deleteFile(indexFilePath);
167     PlatformFileHandle indexFile = openFile(indexFilePath, OpenForWrite);
168     if (!isHandleValid(indexFile)) {
169         LOG(Network, "Cache Error: Could not open %s for write\n", indexFilePath.latin1().data());
170         return;
171     }
172
173     auto it = m_LRUEntryList.begin();
174     const auto& end = m_LRUEntryList.end();
175     while (it != end) {
176         const CString& urlLatin1 = it->latin1();
177         writeToFile(indexFile, urlLatin1.data(), urlLatin1.length());
178         writeToFile(indexFile, "\n", 1);
179         ++it;
180     }
181
182     closeFile(indexFile);
183 }
184
185 void CurlCacheManager::makeRoomForNewEntry()
186 {
187     if (m_disabled)
188         return;
189
190     while ((m_currentStorageSize > m_storageSizeLimit) && m_LRUEntryList.size() > 0) {
191         ASSERT(m_index.find(m_LRUEntryList.last()) != m_index.end());
192         invalidateCacheEntry(m_LRUEntryList.last());
193     }
194 }
195
196 void CurlCacheManager::didReceiveResponse(ResourceHandle& job, ResourceResponse& response)
197 {
198     if (m_disabled)
199         return;
200
201     ResourceHandleInternal* d = job.getInternal();
202     if (d->m_cancelled)
203         return;
204
205     const String& url = job.firstRequest().url().string();
206
207     removeCacheEntryClient(url, &job);
208
209     if (response.source() == ResourceResponseBase::Source::DiskCache) {
210         readCachedData(url, &job, response);
211         m_LRUEntryList.prependOrMoveToFirst(url);
212     }
213     else if (response.httpStatusCode() == 200) {
214         auto it = m_index.find(url);
215         if (it != m_index.end() && (it->value->isLoading() || it->value->hasClients()))
216             return;
217
218         invalidateCacheEntry(url); // Invalidate existing entry on 200
219
220         auto cacheEntry = std::make_unique<CurlCacheEntry>(url, &job, m_cacheDir);
221         bool cacheable = cacheEntry->parseResponseHeaders(response);
222         if (cacheable) {
223             cacheEntry->setIsLoading(true);
224             m_LRUEntryList.prependOrMoveToFirst(url);
225             m_index.set(url, WTFMove(cacheEntry));
226             saveResponseHeaders(url, response);
227         }
228     } else
229         invalidateCacheEntry(url);
230 }
231
232 void CurlCacheManager::didFinishLoading(ResourceHandle& job)
233 {
234     if (m_disabled)
235         return;
236
237     const String& url = job.firstRequest().url().string();
238
239     auto it = m_index.find(url);
240     if (it != m_index.end())
241         it->value->didFinishLoading();
242 }
243
244 bool CurlCacheManager::isCached(const String& url) const
245 {
246     if (m_disabled)
247         return false;
248
249     auto it = m_index.find(url);
250     if (it != m_index.end())
251         return it->value->isCached() && !it->value->isLoading();
252
253     return false;
254 }
255
256 HTTPHeaderMap& CurlCacheManager::requestHeaders(const String& url)
257 {
258     ASSERT(isCached(url));
259     return m_index.find(url)->value->requestHeaders();
260 }
261
262 bool CurlCacheManager::getCachedResponse(const String& url, ResourceResponse& response)
263 {
264     auto it = m_index.find(url);
265     if (it != m_index.end()) {
266         it->value->setResponseFromCachedHeaders(response);
267         return true;
268     }
269     return false;
270 }
271
272 void CurlCacheManager::didReceiveData(ResourceHandle& job, const char* data, size_t size)
273 {
274     if (m_disabled)
275         return;
276
277     const String& url = job.firstRequest().url().string();
278
279     auto it = m_index.find(url);
280     if (it != m_index.end()) {
281         if (it->value->getJob() != &job)
282             return;
283
284         if (!it->value->saveCachedData(data, size))
285             invalidateCacheEntry(url);
286
287         else {
288             m_currentStorageSize += size;
289             m_LRUEntryList.prependOrMoveToFirst(url);
290             makeRoomForNewEntry();
291         }
292     }
293 }
294
295 void CurlCacheManager::saveResponseHeaders(const String& url, ResourceResponse& response)
296 {
297     if (m_disabled)
298         return;
299
300     auto it = m_index.find(url);
301     if (it != m_index.end())
302         if (!it->value->saveResponseHeaders(response))
303             invalidateCacheEntry(url);
304 }
305
306 void CurlCacheManager::invalidateCacheEntry(const String& url)
307 {
308     if (m_disabled)
309         return;
310
311     auto it = m_index.find(url);
312     if (it != m_index.end()) {
313         if (m_currentStorageSize < it->value->entrySize())
314             m_currentStorageSize = 0;
315         else
316             m_currentStorageSize -= it->value->entrySize();
317
318         it->value->invalidate();
319         m_index.remove(url);
320     }
321     m_LRUEntryList.remove(url);
322 }
323
324 void CurlCacheManager::didFail(ResourceHandle &job)
325 {
326     const String& url = job.firstRequest().url().string();
327
328     invalidateCacheEntry(url);
329 }
330
331 void CurlCacheManager::addCacheEntryClient(const String& url, ResourceHandle* job)
332 {
333     if (m_disabled)
334         return;
335
336     auto it = m_index.find(url);
337     if (it != m_index.end())
338         it->value->addClient(job);
339 }
340
341 void CurlCacheManager::removeCacheEntryClient(const String& url, ResourceHandle* job)
342 {
343     if (m_disabled)
344         return;
345
346     auto it = m_index.find(url);
347     if (it != m_index.end())
348         it->value->removeClient(job);
349 }
350
351 void CurlCacheManager::readCachedData(const String& url, ResourceHandle* job, ResourceResponse& response)
352 {
353     if (m_disabled)
354         return;
355
356     auto it = m_index.find(url);
357     if (it != m_index.end()) {
358         it->value->setResponseFromCachedHeaders(response);
359         m_LRUEntryList.prependOrMoveToFirst(url);
360         if (!it->value->readCachedData(job))
361             invalidateCacheEntry(url);
362     }
363 }
364
365 }
366
367 #endif