Diff
Modified: trunk/Source/WebCore/ChangeLog (159431 => 159432)
--- trunk/Source/WebCore/ChangeLog 2013-11-18 18:30:16 UTC (rev 159431)
+++ trunk/Source/WebCore/ChangeLog 2013-11-18 18:35:10 UTC (rev 159432)
@@ -1,3 +1,57 @@
+2013-11-18 Mátyás Mustoha <[email protected]>
+
+ [curl] Add file cache
+ https://bugs.webkit.org/show_bug.cgi?id=123333
+
+ Reviewed by Brent Fulgham.
+
+ Implementation of on disc file cache
+ for the curl network backend.
+
+ * WebCore.vcxproj/WebCore.vcxproj:
+ * WebCore.vcxproj/WebCore.vcxproj.filters:
+ * platform/network/curl/CurlCacheEntry.cpp: Added.
+ (WebCore::CurlCacheEntry::CurlCacheEntry):
+ (WebCore::CurlCacheEntry::~CurlCacheEntry):
+ (WebCore::CurlCacheEntry::isCached):
+ (WebCore::CurlCacheEntry::requestHeaders):
+ (WebCore::CurlCacheEntry::saveCachedData):
+ (WebCore::CurlCacheEntry::loadCachedData):
+ (WebCore::CurlCacheEntry::saveResponseHeaders):
+ (WebCore::CurlCacheEntry::loadResponseHeaders):
+ (WebCore::CurlCacheEntry::setResponseFromCachedHeaders):
+ (WebCore::CurlCacheEntry::didFail):
+ (WebCore::CurlCacheEntry::didFinishLoading):
+ (WebCore::CurlCacheEntry::generateBaseFilename):
+ (WebCore::CurlCacheEntry::loadFileToBuffer):
+ (WebCore::CurlCacheEntry::invalidate):
+ (WebCore::CurlCacheEntry::parseResponseHeaders):
+ * platform/network/curl/CurlCacheEntry.h: Added.
+ (WebCore::CurlCacheEntry::isInMemory):
+ * platform/network/curl/CurlCacheManager.cpp: Added.
+ (WebCore::CurlCacheManager::getInstance):
+ (WebCore::CurlCacheManager::CurlCacheManager):
+ (WebCore::CurlCacheManager::~CurlCacheManager):
+ (WebCore::CurlCacheManager::setCacheDirectory):
+ (WebCore::CurlCacheManager::loadIndex):
+ (WebCore::CurlCacheManager::saveIndex):
+ (WebCore::CurlCacheManager::didReceiveResponse):
+ (WebCore::CurlCacheManager::didFinishLoading):
+ (WebCore::CurlCacheManager::isCached):
+ (WebCore::CurlCacheManager::requestHeaders):
+ (WebCore::CurlCacheManager::didReceiveData):
+ (WebCore::CurlCacheManager::saveResponseHeaders):
+ (WebCore::CurlCacheManager::invalidateCacheEntry):
+ (WebCore::CurlCacheManager::didFail):
+ (WebCore::CurlCacheManager::loadCachedData):
+ * platform/network/curl/CurlCacheManager.h: Added.
+ (WebCore::CurlCacheManager::getCacheDirectory):
+ * platform/network/curl/ResourceHandleManager.cpp:
+ (WebCore::writeCallback):
+ (WebCore::headerCallback):
+ (WebCore::ResourceHandleManager::downloadTimerCallback):
+ (WebCore::ResourceHandleManager::initializeHandle):
+
2013-11-18 [email protected] <[email protected]>
[Win] WebKit version in user agent string is incorrect.
Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj (159431 => 159432)
--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2013-11-18 18:30:16 UTC (rev 159431)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2013-11-18 18:35:10 UTC (rev 159432)
@@ -8316,6 +8316,18 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
</ClCompile>
+ <ClCompile Include="..\platform\network\curl\CurlCacheEntry.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\platform\network\curl\CurlCacheManager.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+ </ClCompile>
<ClCompile Include="..\platform\network\curl\CurlDownload.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -19474,6 +19486,18 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
</CustomBuildStep>
+ <CustomBuildStep Include="..\platform\network\curl\CurlCacheManager.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+ </CustomBuildStep>
+ <CustomBuildStep Include="..\platform\network\curl\CurlCacheEntry.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+ </CustomBuildStep>
<CustomBuildStep Include="..\platform\network\curl\FormDataStreamCurl.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters (159431 => 159432)
--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters 2013-11-18 18:30:16 UTC (rev 159431)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters 2013-11-18 18:35:10 UTC (rev 159432)
@@ -1896,6 +1896,12 @@
<ClCompile Include="..\platform\network\curl\CredentialStorageCurl.cpp">
<Filter>platform\network\curl</Filter>
</ClCompile>
+ <ClCompile Include="..\platform\network\curl\CurlCacheEntry.cpp">
+ <Filter>platform\network\curl</Filter>
+ </ClCompile>
+ <ClCompile Include="..\platform\network\curl\CurlCacheManager.cpp">
+ <Filter>platform\network\curl</Filter>
+ </ClCompile>
<ClCompile Include="..\platform\network\curl\CurlDownload.cpp">
<Filter>platform\network\curl</Filter>
</ClCompile>
@@ -15056,6 +15062,12 @@
<CustomBuildStep Include="..\platform\network\curl\AuthenticationChallenge.h">
<Filter>platform\network\curl</Filter>
</CustomBuildStep>
+ <CustomBuildStep Include="..\platform\network\curl\CurlCacheEntry.h">
+ <Filter>platform\network\curl</Filter>
+ </CustomBuildStep>
+ <CustomBuildStep Include="..\platform\network\curl\CurlCacheManager.h">
+ <Filter>platform\network\curl</Filter>
+ </CustomBuildStep>
<CustomBuildStep Include="..\platform\network\curl\FormDataStreamCurl.h">
<Filter>platform\network\curl</Filter>
</CustomBuildStep>
Added: trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.cpp (0 => 159432)
--- trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.cpp (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.cpp 2013-11-18 18:35:10 UTC (rev 159432)
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "CurlCacheEntry.h"
+
+#include "FileSystem.h"
+#include "HTTPHeaderMap.h"
+#include "HTTPParsers.h"
+#include "Logging.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceHandleInternal.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/DateMath.h>
+#include <wtf/HexNumber.h>
+#include <wtf/MD5.h>
+
+namespace WebCore {
+
+CurlCacheEntry::CurlCacheEntry(const String& url, const String& cacheDir)
+ : m_headerFilename(cacheDir)
+ , m_contentFilename(cacheDir)
+ , m_expireDate(-1)
+ , m_headerInMemory(false)
+{
+ generateBaseFilename(url.latin1());
+
+ m_headerFilename.append(m_basename);
+ m_headerFilename.append(".header");
+
+ m_contentFilename.append(m_basename);
+ m_contentFilename.append(".content");
+}
+
+CurlCacheEntry::~CurlCacheEntry()
+{
+}
+
+// cache manager should invalidate the entry on false
+bool CurlCacheEntry::isCached()
+{
+ if (!fileExists(m_contentFilename) || !fileExists(m_headerFilename))
+ return false;
+
+ if (!m_headerInMemory) {
+ if (!loadResponseHeaders())
+ return false;
+ }
+
+ if (m_expireDate < currentTimeMS()) {
+ m_headerInMemory = false;
+ return false;
+ }
+
+ return true;
+}
+
+HTTPHeaderMap& CurlCacheEntry::requestHeaders()
+{
+ return m_requestHeaders;
+}
+
+bool CurlCacheEntry::saveCachedData(const char* data, size_t size)
+{
+ PlatformFileHandle contentFile = openFile(m_contentFilename, OpenForWrite);
+ if (!isHandleValid(contentFile)) {
+ LOG(Network, "Cache Error: Could not open %s for write\n", m_contentFilename.latin1().data());
+ return false;
+ }
+
+ // append
+ seekFile(contentFile, 0, SeekFromEnd);
+ writeToFile(contentFile, data, size);
+ closeFile(contentFile);
+ return true;
+}
+
+bool CurlCacheEntry::loadCachedData(ResourceHandle* job)
+{
+ ASSERT(job->client());
+
+ Vector<char> buffer;
+ if (!loadFileToBuffer(m_contentFilename, buffer))
+ return false;
+
+ job->getInternal()->client()->didReceiveData(job, buffer.data(), buffer.size(), 0);
+ return true;
+}
+
+bool CurlCacheEntry::saveResponseHeaders(ResourceResponse& response)
+{
+ PlatformFileHandle headerFile = openFile(m_headerFilename, OpenForWrite);
+ if (!isHandleValid(headerFile)) {
+ LOG(Network, "Cache Error: Could not open %s for write\n", m_headerFilename.latin1().data());
+ return false;
+ }
+
+ // Headers
+ HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin();
+ HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end();
+ while (it != end) {
+ String headerField = it->key;
+ headerField.append(": ");
+ headerField.append(it->value);
+ headerField.append("\n");
+ CString headerFieldLatin1 = headerField.latin1();
+ writeToFile(headerFile, headerFieldLatin1.data(), headerFieldLatin1.length());
+ ++it;
+ }
+
+ closeFile(headerFile);
+ return true;
+}
+
+bool CurlCacheEntry::loadResponseHeaders()
+{
+ Vector<char> buffer;
+ if (!loadFileToBuffer(m_headerFilename, buffer))
+ return false;
+
+ String headerContent = String(buffer.data());
+ Vector<String> headerFields;
+ headerContent.split("\n", headerFields);
+
+ Vector<String>::const_iterator it = headerFields.begin();
+ Vector<String>::const_iterator end = headerFields.end();
+ while (it != end) {
+ size_t splitPosition = it->find(":");
+ if (splitPosition != notFound)
+ m_cachedResponse.setHTTPHeaderField(it->left(splitPosition), it->substring(splitPosition+1).stripWhiteSpace());
+ ++it;
+ }
+
+ return parseResponseHeaders(m_cachedResponse);
+}
+
+// set response headers from memory
+void CurlCacheEntry::setResponseFromCachedHeaders(ResourceResponse& response)
+{
+ response.setHTTPStatusCode(304);
+ response.setWasCached(true);
+
+ // Integrate the headers in the response with the cached ones.
+ HTTPHeaderMap::const_iterator it = m_cachedResponse.httpHeaderFields().begin();
+ HTTPHeaderMap::const_iterator end = m_cachedResponse.httpHeaderFields().end();
+ while (it != end) {
+ if (response.httpHeaderField(it->key).isNull())
+ response.setHTTPHeaderField(it->key, it->value);
+ ++it;
+ }
+
+ // Try to parse expected content length
+ long long contentLength = -1;
+ if (!response.httpHeaderField("Content-Length").isNull()) {
+ bool success = false;
+ long long parsedContentLength = response.httpHeaderField("Content-Length").toInt64(&success);
+ if (success)
+ contentLength = parsedContentLength;
+ }
+ response.setExpectedContentLength(contentLength); // -1 on parse error or null
+
+ response.setMimeType(extractMIMETypeFromMediaType(response.httpHeaderField("Content-Type")));
+ response.setTextEncodingName(extractCharsetFromMediaType(response.httpHeaderField("Content-Type")));
+ response.setSuggestedFilename(filenameFromHTTPContentDisposition(response.httpHeaderField("Content-Disposition")));
+}
+
+void CurlCacheEntry::didFail()
+{
+ // the cache manager will call invalidate()
+}
+
+void CurlCacheEntry::didFinishLoading()
+{
+ // nothing to do here yet
+}
+
+void CurlCacheEntry::generateBaseFilename(const CString& url)
+{
+ MD5 md5;
+ md5.addBytes(reinterpret_cast<const uint8_t*>(url.data()), url.length());
+
+ Vector<uint8_t, 16> sum;
+ md5.checksum(sum);
+ uint8_t* rawdata = sum.data();
+
+ for (unsigned i = 0; i < 16; i++)
+ appendByteAsHex(rawdata[i], m_basename, Lowercase);
+}
+
+bool CurlCacheEntry::loadFileToBuffer(const String& filepath, Vector<char>& buffer)
+{
+ // open the file
+ PlatformFileHandle inputFile = openFile(filepath, OpenForRead);
+ if (!isHandleValid(inputFile)) {
+ LOG(Network, "Cache Error: Could not open %s for read\n", filepath.latin1().data());
+ return false;
+ }
+
+ long long filesize = -1;
+ if (!getFileSize(filepath, filesize)) {
+ LOG(Network, "Cache Error: Could not get file size of %s\n", filepath.latin1().data());
+ closeFile(inputFile);
+ return false;
+ }
+
+ // load the file content into buffer
+ buffer.resize(filesize);
+ int bufferPosition = 0;
+ int bufferReadSize = 4096;
+ int bytesRead = 0;
+ while (filesize > bufferPosition) {
+ if (filesize - bufferPosition < bufferReadSize)
+ bufferReadSize = filesize - bufferPosition;
+
+ bytesRead = readFromFile(inputFile, buffer.data() + bufferPosition, bufferReadSize);
+ if (bytesRead != bufferReadSize) {
+ LOG(Network, "Cache Error: Could not read from %s\n", filepath.latin1().data());
+ closeFile(inputFile);
+ return false;
+ }
+
+ bufferPosition += bufferReadSize;
+ }
+ closeFile(inputFile);
+ return true;
+}
+
+void CurlCacheEntry::invalidate()
+{
+ deleteFile(m_headerFilename);
+ deleteFile(m_contentFilename);
+ LOG(Network, "Cache: invalidated %s\n", m_basename.latin1().data());
+}
+
+bool CurlCacheEntry::parseResponseHeaders(ResourceResponse& response)
+{
+ double fileTime;
+ time_t fileModificationDate;
+
+ if (getFileModificationTime(m_headerFilename, fileModificationDate)) {
+ fileTime = difftime(fileModificationDate, 0);
+ fileTime *= 1000.0;
+ } else
+ fileTime = currentTimeMS(); // GMT
+
+ if (response.cacheControlContainsNoCache() || response.cacheControlContainsNoStore())
+ return false;
+
+
+ double maxAge = 0;
+ bool maxAgeIsValid = false;
+
+ if (response.cacheControlContainsMustRevalidate())
+ maxAge = 0;
+ else {
+ maxAge = response.cacheControlMaxAge();
+ if (std::isnan(maxAge))
+ maxAge = 0;
+ else
+ maxAgeIsValid = true;
+ }
+
+ if (!response.hasCacheValidatorFields())
+ return false;
+
+
+ double lastModificationDate = 0;
+ double responseDate = 0;
+ double expirationDate = 0;
+
+ lastModificationDate = response.lastModified();
+ if (std::isnan(lastModificationDate))
+ lastModificationDate = 0;
+
+ responseDate = response.date();
+ if (std::isnan(responseDate))
+ responseDate = 0;
+
+ expirationDate = response.expires();
+ if (std::isnan(expirationDate))
+ expirationDate = 0;
+
+
+ if (maxAgeIsValid) {
+ // when both the cache entry and the response contain max-age, the lesser one takes priority
+ double expires = fileTime + maxAge * 1000;
+ if (m_expireDate == -1 || m_expireDate > expires)
+ m_expireDate = expires;
+ } else if (responseDate > 0 && expirationDate >= responseDate)
+ m_expireDate = fileTime + (expirationDate - responseDate);
+
+ // if there were no lifetime information
+ if (m_expireDate == -1) {
+ if (lastModificationDate > 0)
+ m_expireDate = fileTime + (fileTime - lastModificationDate) * 0.1;
+ else
+ m_expireDate = 0;
+ }
+
+ String etag = response.httpHeaderField("ETag");
+ if (!etag.isNull())
+ m_requestHeaders.set("If-None-Match", etag);
+
+ String lastModified = response.httpHeaderField("Last-Modified");
+ if (!lastModified.isNull())
+ m_requestHeaders.set("If-Modified-Since", lastModified);
+
+ if (etag.isNull() && lastModified.isNull())
+ return false;
+
+ m_headerInMemory = true;
+ return true;
+}
+
+}
Added: trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.h (0 => 159432)
--- trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.h (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlCacheEntry.h 2013-11-18 18:35:10 UTC (rev 159432)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CurlCacheEntry_h
+#define CurlCacheEntry_h
+
+#include "HTTPHeaderMap.h"
+#include "ResourceHandle.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include <wtf/Vector.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CurlCacheEntry {
+
+public:
+ CurlCacheEntry(const String& url, const String& cacheDir);
+ ~CurlCacheEntry();
+
+ bool isCached();
+ const bool& isInMemory() { return m_headerInMemory; }
+ HTTPHeaderMap& requestHeaders();
+
+ bool saveCachedData(const char* data, size_t);
+ bool loadCachedData(ResourceHandle*);
+
+ bool saveResponseHeaders(ResourceResponse&);
+ void setResponseFromCachedHeaders(ResourceResponse&);
+
+ void invalidate();
+ void didFail();
+ void didFinishLoading();
+
+ bool parseResponseHeaders(ResourceResponse&);
+
+private:
+ String m_basename;
+ String m_headerFilename;
+ String m_contentFilename;
+
+ double m_expireDate;
+ bool m_headerInMemory;
+
+ ResourceResponse m_cachedResponse;
+ HTTPHeaderMap m_requestHeaders;
+
+ void generateBaseFilename(const CString& url);
+ bool loadFileToBuffer(const String& filepath, Vector<char>& buffer);
+ bool loadResponseHeaders();
+};
+
+}
+
+#endif // CurlCacheEntry_h
Added: trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp (0 => 159432)
--- trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp 2013-11-18 18:35:10 UTC (rev 159432)
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#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)
+{
+ // call setCacheDirectory() to enable
+}
+
+CurlCacheManager::~CurlCacheManager()
+{
+ if (m_disabled)
+ return;
+
+ saveIndex();
+}
+
+void CurlCacheManager::setCacheDirectory(const String& directory)
+{
+ m_cacheDir = directory;
+ m_cacheDir.append("/");
+ 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_disabled = false;
+ loadIndex();
+}
+
+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;
+ }
+
+ // load the file content into buffer
+ 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);
+
+ // create strings from buffer
+ String headerContent = String(buffer.data());
+ Vector<String> indexURLs;
+ headerContent.split("\n", indexURLs);
+ buffer.clear();
+
+ // add entries to index
+ Vector<String>::const_iterator it = indexURLs.begin();
+ Vector<String>::const_iterator end = indexURLs.end();
+ if (indexURLs.size() > 1)
+ --end; // last line is empty
+ while (it != end) {
+ String url = ""
+
+ std::unique_ptr<CurlCacheEntry> cacheEntry(new CurlCacheEntry(url, m_cacheDir));
+ if (cacheEntry->isCached())
+ m_index.set(url, std::move(cacheEntry));
+
+ ++it;
+ }
+}
+
+void CurlCacheManager::saveIndex()
+{
+ if (m_disabled)
+ return;
+
+ String indexFilePath(m_cacheDir);
+ indexFilePath.append("index.dat");
+
+ PlatformFileHandle indexFile = openFile(indexFilePath, OpenForWrite);
+ if (!isHandleValid(indexFile)) {
+ LOG(Network, "Cache Error: Could not open %s for write\n", indexFilePath.latin1().data());
+ return;
+ }
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::const_iterator it = m_index.begin();
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::const_iterator end = m_index.end();
+ while (it != end) {
+ const CString& urlLatin1 = it->key.latin1();
+ writeToFile(indexFile, urlLatin1.data(), urlLatin1.length());
+ writeToFile(indexFile, "\n", 1);
+ ++it;
+ }
+
+ closeFile(indexFile);
+}
+
+void CurlCacheManager::didReceiveResponse(ResourceHandle* job, ResourceResponse& response)
+{
+ if (m_disabled)
+ return;
+
+ String url = ""
+
+ if (response.httpStatusCode() == 304)
+ loadCachedData(url, job, response);
+ else if (response.httpStatusCode() == 200) {
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end())
+ invalidateCacheEntry(url);
+
+ std::unique_ptr<CurlCacheEntry> entry(new CurlCacheEntry(url, m_cacheDir));
+ bool cacheable = entry->parseResponseHeaders(response);
+ if (cacheable) {
+ m_index.set(url, std::move(entry));
+ saveResponseHeaders(url, response);
+ } else
+ entry->invalidate();
+ } else
+ invalidateCacheEntry(url);
+}
+
+void CurlCacheManager::didFinishLoading(const String& url)
+{
+ if (m_disabled)
+ return;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end())
+ it->value->didFinishLoading();
+}
+
+bool CurlCacheManager::isCached(const String& url)
+{
+ if (m_disabled)
+ return false;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end()) {
+ if (it->value->isCached())
+ return true;
+
+ invalidateCacheEntry(url);
+ }
+ return false;
+}
+
+HTTPHeaderMap& CurlCacheManager::requestHeaders(const String& url)
+{
+ ASSERT(isCached(url));
+ return m_index.find(url)->value->requestHeaders();
+}
+
+void CurlCacheManager::didReceiveData(const String& url, const char* data, size_t size)
+{
+ if (m_disabled)
+ return;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end())
+ if (!it->value->saveCachedData(data, size))
+ invalidateCacheEntry(url);
+}
+
+void CurlCacheManager::saveResponseHeaders(const String& url, ResourceResponse& response)
+{
+ if (m_disabled)
+ return;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator 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;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end()) {
+ it->value->invalidate();
+ m_index.remove(url);
+ }
+}
+
+void CurlCacheManager::didFail(const String& url)
+{
+ invalidateCacheEntry(url);
+}
+
+void CurlCacheManager::loadCachedData(const String& url, ResourceHandle* job, ResourceResponse& response)
+{
+ if (m_disabled)
+ return;
+
+ HashMap<String, std::unique_ptr<CurlCacheEntry>>::iterator it = m_index.find(url);
+ if (it != m_index.end()) {
+ it->value->setResponseFromCachedHeaders(response);
+ if (!it->value->loadCachedData(job))
+ invalidateCacheEntry(url);
+ }
+}
+
+}
Added: trunk/Source/WebCore/platform/network/curl/CurlCacheManager.h (0 => 159432)
--- trunk/Source/WebCore/platform/network/curl/CurlCacheManager.h (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlCacheManager.h 2013-11-18 18:35:10 UTC (rev 159432)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CurlCacheManager_h
+#define CurlCacheManager_h
+
+#include "CurlCacheEntry.h"
+#include "ResourceHandle.h"
+#include "ResourceResponse.h"
+#include <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CurlCacheManager {
+
+public:
+ static CurlCacheManager& getInstance();
+
+ const String& getCacheDirectory() { return m_cacheDir; }
+ void setCacheDirectory(const String&);
+
+ bool isCached(const String&);
+ HTTPHeaderMap& requestHeaders(const String&); // load headers
+
+ void didReceiveResponse(ResourceHandle*, ResourceResponse&);
+ void didReceiveData(const String&, const char*, size_t); // save data
+ void didFail(const String&);
+ void didFinishLoading(const String&);
+
+private:
+ CurlCacheManager();
+ ~CurlCacheManager();
+ CurlCacheManager(CurlCacheManager const&);
+ void operator=(CurlCacheManager const&);
+
+ String m_cacheDir;
+ HashMap<String, std::unique_ptr<CurlCacheEntry>> m_index;
+ bool m_disabled;
+
+ void saveIndex();
+ void loadIndex();
+
+ void saveResponseHeaders(const String&, ResourceResponse&);
+ void invalidateCacheEntry(const String&);
+ void loadCachedData(const String&, ResourceHandle*, ResourceResponse&);
+};
+
+}
+
+#endif // CurlCacheManager_h
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp (159431 => 159432)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp 2013-11-18 18:30:16 UTC (rev 159431)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp 2013-11-18 18:35:10 UTC (rev 159432)
@@ -9,6 +9,7 @@
* Copyright (C) 2009 Brent Fulgham <[email protected]>
* Copyright (C) 2013 Peter Gal <[email protected]>, University of Szeged
* Copyright (C) 2013 Alex Christensen <[email protected]>
+ * Copyright (C) 2013 University of Szeged
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,6 +38,7 @@
#include "ResourceHandleManager.h"
#include "CredentialStorage.h"
+#include "CurlCacheManager.h"
#include "DataURL.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
@@ -268,8 +270,10 @@
if (d->m_multipartHandle)
d->m_multipartHandle->contentReceived(static_cast<const char*>(ptr), totalSize);
- else if (d->client())
+ else if (d->client()) {
d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0);
+ CurlCacheManager::getInstance().didReceiveData(job->firstRequest().url().string(), static_cast<char*>(ptr), totalSize);
+ }
return totalSize;
}
@@ -459,8 +463,10 @@
}
}
- if (client)
+ if (client) {
client->didReceiveResponse(job, d->m_response);
+ CurlCacheManager::getInstance().didReceiveResponse(job, d->m_response);
+ }
d->m_response.setResponseFired(true);
} else {
@@ -605,16 +611,20 @@
if (d->m_multipartHandle)
d->m_multipartHandle->contentEnded();
- if (d->client())
+ if (d->client()) {
d->client()->didFinishLoading(job, 0);
+ CurlCacheManager::getInstance().didFinishLoading(job->firstRequest().url().string());
+ }
} else {
char* url = ""
curl_easy_getinfo(d->m_handle, CURLINFO_EFFECTIVE_URL, &url);
#ifndef NDEBUG
fprintf(stderr, "Curl ERROR for url='', error: '%s'\n", url, curl_easy_strerror(msg->data.result));
#endif
- if (d->client())
+ if (d->client()) {
d->client()->didFail(job, ResourceError(String(), msg->data.result, String(url), String(curl_easy_strerror(msg->data.result))));
+ CurlCacheManager::getInstance().didFail(job->firstRequest().url().string());
+ }
}
removeFromCurl(job);
@@ -969,6 +979,19 @@
struct curl_slist* headers = 0;
if (job->firstRequest().httpHeaderFields().size() > 0) {
HTTPHeaderMap customHeaders = job->firstRequest().httpHeaderFields();
+
+ if (CurlCacheManager::getInstance().isCached(url)) {
+ HTTPHeaderMap& requestHeaders = CurlCacheManager::getInstance().requestHeaders(url);
+
+ // append additional cache information
+ HTTPHeaderMap::const_iterator it = requestHeaders.begin();
+ HTTPHeaderMap::const_iterator end = requestHeaders.end();
+ while (it != end) {
+ customHeaders.set(it->key, it->value);
+ ++it;
+ }
+ }
+
HTTPHeaderMap::const_iterator end = customHeaders.end();
for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
String key = it->key;