CurlCacheManager.cpp [plain text]
#include "config.h"
#if USE(CURL)
#include "CurlCacheManager.h"
#include "FileSystem.h"
#include "HTTPHeaderMap.h"
#include "Logging.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"
#include <wtf/HashMap.h>
#include <wtf/text/CString.h>
#define IO_BUFFERSIZE 4096
namespace WebCore {
CurlCacheManager& CurlCacheManager::getInstance()
{
static CurlCacheManager instance;
return instance;
}
CurlCacheManager::CurlCacheManager()
: m_disabled(true)
, m_currentStorageSize(0)
, m_storageSizeLimit(52428800) {
}
CurlCacheManager::~CurlCacheManager()
{
if (m_disabled)
return;
saveIndex();
}
void CurlCacheManager::setCacheDirectory(const String& directory)
{
m_cacheDir = directory;
if (m_cacheDir.isEmpty()) {
LOG(Network, "Cache Error: Cache location is not set! CacheManager disabled.\n");
m_disabled = true;
return;
}
if (!fileExists(m_cacheDir)) {
if (!makeAllDirectories(m_cacheDir)) {
LOG(Network, "Cache Error: Could not open or create cache directory! CacheManager disabled.\n");
m_disabled = true;
return;
}
}
m_cacheDir.append("/");
m_disabled = false;
loadIndex();
}
void CurlCacheManager::setStorageSizeLimit(size_t sizeLimit)
{
m_storageSizeLimit = sizeLimit;
}
void CurlCacheManager::loadIndex()
{
if (m_disabled)
return;
String indexFilePath(m_cacheDir);
indexFilePath.append("index.dat");
PlatformFileHandle indexFile = openFile(indexFilePath, OpenForRead);
if (!isHandleValid(indexFile)) {
LOG(Network, "Cache Warning: Could not open %s for read\n", indexFilePath.latin1().data());
return;
}
long long filesize = -1;
if (!getFileSize(indexFilePath, filesize)) {
LOG(Network, "Cache Error: Could not get file size of %s\n", indexFilePath.latin1().data());
return;
}
Vector<char> buffer;
buffer.resize(filesize);
int bufferPosition = 0;
int bufferReadSize = IO_BUFFERSIZE;
while (filesize > bufferPosition) {
if (filesize - bufferPosition < bufferReadSize)
bufferReadSize = filesize - bufferPosition;
readFromFile(indexFile, buffer.data() + bufferPosition, bufferReadSize);
bufferPosition += bufferReadSize;
}
closeFile(indexFile);
String headerContent = String(buffer.data(), buffer.size());
Vector<String> indexURLs;
headerContent.split('\n', indexURLs);
buffer.clear();
Vector<String>::const_iterator it = indexURLs.begin();
Vector<String>::const_iterator end = indexURLs.end();
if (indexURLs.size() > 1)
--end; while (it != end) {
String url = it->stripWhiteSpace();
auto cacheEntry = std::make_unique<CurlCacheEntry>(url, nullptr, m_cacheDir);
if (cacheEntry->isCached() && cacheEntry->entrySize() < m_storageSizeLimit) {
m_currentStorageSize += cacheEntry->entrySize();
makeRoomForNewEntry();
m_LRUEntryList.prependOrMoveToFirst(url);
m_index.set(url, WTFMove(cacheEntry));
} else
cacheEntry->invalidate();
++it;
}
}
void CurlCacheManager::saveIndex()
{
if (m_disabled)
return;
String indexFilePath(m_cacheDir);
indexFilePath.append("index.dat");
deleteFile(indexFilePath);
PlatformFileHandle indexFile = openFile(indexFilePath, OpenForWrite);
if (!isHandleValid(indexFile)) {
LOG(Network, "Cache Error: Could not open %s for write\n", indexFilePath.latin1().data());
return;
}
auto it = m_LRUEntryList.begin();
const auto& end = m_LRUEntryList.end();
while (it != end) {
const CString& urlLatin1 = it->latin1();
writeToFile(indexFile, urlLatin1.data(), urlLatin1.length());
writeToFile(indexFile, "\n", 1);
++it;
}
closeFile(indexFile);
}
void CurlCacheManager::makeRoomForNewEntry()
{
if (m_disabled)
return;
while ((m_currentStorageSize > m_storageSizeLimit) && m_LRUEntryList.size() > 0) {
ASSERT(m_index.find(m_LRUEntryList.last()) != m_index.end());
invalidateCacheEntry(m_LRUEntryList.last());
}
}
void CurlCacheManager::didReceiveResponse(ResourceHandle& job, ResourceResponse& response)
{
if (m_disabled)
return;
ResourceHandleInternal* d = job.getInternal();
if (d->m_cancelled)
return;
const String& url = job.firstRequest().url().string();
removeCacheEntryClient(url, &job);
if (response.source() == ResourceResponseBase::Source::DiskCache) {
readCachedData(url, &job, response);
m_LRUEntryList.prependOrMoveToFirst(url);
}
else if (response.httpStatusCode() == 200) {
auto it = m_index.find(url);
if (it != m_index.end() && (it->value->isLoading() || it->value->hasClients()))
return;
invalidateCacheEntry(url);
auto cacheEntry = std::make_unique<CurlCacheEntry>(url, &job, m_cacheDir);
bool cacheable = cacheEntry->parseResponseHeaders(response);
if (cacheable) {
cacheEntry->setIsLoading(true);
m_LRUEntryList.prependOrMoveToFirst(url);
m_index.set(url, WTFMove(cacheEntry));
saveResponseHeaders(url, response);
}
} else
invalidateCacheEntry(url);
}
void CurlCacheManager::didFinishLoading(ResourceHandle& job)
{
if (m_disabled)
return;
const String& url = job.firstRequest().url().string();
auto it = m_index.find(url);
if (it != m_index.end())
it->value->didFinishLoading();
}
bool CurlCacheManager::isCached(const String& url) const
{
if (m_disabled)
return false;
auto it = m_index.find(url);
if (it != m_index.end())
return it->value->isCached() && !it->value->isLoading();
return false;
}
HTTPHeaderMap& CurlCacheManager::requestHeaders(const String& url)
{
ASSERT(isCached(url));
return m_index.find(url)->value->requestHeaders();
}
bool CurlCacheManager::getCachedResponse(const String& url, ResourceResponse& response)
{
auto it = m_index.find(url);
if (it != m_index.end()) {
it->value->setResponseFromCachedHeaders(response);
return true;
}
return false;
}
void CurlCacheManager::didReceiveData(ResourceHandle& job, const char* data, size_t size)
{
if (m_disabled)
return;
const String& url = job.firstRequest().url().string();
auto it = m_index.find(url);
if (it != m_index.end()) {
if (it->value->getJob() != &job)
return;
if (!it->value->saveCachedData(data, size))
invalidateCacheEntry(url);
else {
m_currentStorageSize += size;
m_LRUEntryList.prependOrMoveToFirst(url);
makeRoomForNewEntry();
}
}
}
void CurlCacheManager::saveResponseHeaders(const String& url, ResourceResponse& response)
{
if (m_disabled)
return;
auto it = m_index.find(url);
if (it != m_index.end())
if (!it->value->saveResponseHeaders(response))
invalidateCacheEntry(url);
}
void CurlCacheManager::invalidateCacheEntry(const String& url)
{
if (m_disabled)
return;
auto it = m_index.find(url);
if (it != m_index.end()) {
if (m_currentStorageSize < it->value->entrySize())
m_currentStorageSize = 0;
else
m_currentStorageSize -= it->value->entrySize();
it->value->invalidate();
m_index.remove(url);
}
m_LRUEntryList.remove(url);
}
void CurlCacheManager::didFail(ResourceHandle &job)
{
const String& url = job.firstRequest().url().string();
invalidateCacheEntry(url);
}
void CurlCacheManager::addCacheEntryClient(const String& url, ResourceHandle* job)
{
if (m_disabled)
return;
auto it = m_index.find(url);
if (it != m_index.end())
it->value->addClient(job);
}
void CurlCacheManager::removeCacheEntryClient(const String& url, ResourceHandle* job)
{
if (m_disabled)
return;
auto it = m_index.find(url);
if (it != m_index.end())
it->value->removeClient(job);
}
void CurlCacheManager::readCachedData(const String& url, ResourceHandle* job, ResourceResponse& response)
{
if (m_disabled)
return;
auto it = m_index.find(url);
if (it != m_index.end()) {
it->value->setResponseFromCachedHeaders(response);
m_LRUEntryList.prependOrMoveToFirst(url);
if (!it->value->readCachedData(job))
invalidateCacheEntry(url);
}
}
}
#endif