summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/network
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/network')
-rw-r--r--WebCore/platform/network/BlobData.cpp44
-rw-r--r--WebCore/platform/network/BlobData.h70
-rw-r--r--WebCore/platform/network/BlobRegistryImpl.cpp7
-rw-r--r--WebCore/platform/network/BlobResourceHandle.cpp4
-rw-r--r--WebCore/platform/network/CredentialStorage.cpp8
-rw-r--r--WebCore/platform/network/DataURL.cpp92
-rw-r--r--WebCore/platform/network/DataURL.h37
-rw-r--r--WebCore/platform/network/HTTPHeaderMap.cpp20
-rw-r--r--WebCore/platform/network/HTTPHeaderMap.h13
-rw-r--r--WebCore/platform/network/ProxyServer.cpp75
-rw-r--r--WebCore/platform/network/ProxyServer.h79
-rw-r--r--WebCore/platform/network/ResourceHandle.h19
-rw-r--r--WebCore/platform/network/ResourceHandleInternal.h23
-rw-r--r--WebCore/platform/network/ResourceRawHeaders.h41
-rw-r--r--WebCore/platform/network/ResourceRequestBase.cpp7
-rw-r--r--WebCore/platform/network/ResourceRequestBase.h18
-rw-r--r--WebCore/platform/network/ResourceResponseBase.cpp23
-rw-r--r--WebCore/platform/network/ResourceResponseBase.h10
-rw-r--r--WebCore/platform/network/android/ResourceRequest.h6
-rw-r--r--WebCore/platform/network/android/ResourceResponse.h6
-rw-r--r--WebCore/platform/network/cf/ProxyServerCFNet.cpp98
-rw-r--r--WebCore/platform/network/cf/ResourceRequest.h8
-rw-r--r--WebCore/platform/network/cf/ResourceResponse.h6
-rw-r--r--WebCore/platform/network/cf/SocketStreamHandle.h1
-rw-r--r--WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp6
-rw-r--r--WebCore/platform/network/chromium/ResourceRequest.cpp15
-rw-r--r--WebCore/platform/network/chromium/ResourceRequest.h9
-rw-r--r--WebCore/platform/network/chromium/ResourceResponse.cpp58
-rw-r--r--WebCore/platform/network/chromium/ResourceResponse.h15
-rw-r--r--WebCore/platform/network/curl/ProxyServerCurl.cpp40
-rw-r--r--WebCore/platform/network/curl/ResourceHandleCurl.cpp15
-rw-r--r--WebCore/platform/network/curl/ResourceHandleManager.cpp75
-rw-r--r--WebCore/platform/network/curl/ResourceRequest.h6
-rw-r--r--WebCore/platform/network/curl/ResourceResponse.h10
-rw-r--r--WebCore/platform/network/mac/FormDataStreamMac.mm2
-rw-r--r--WebCore/platform/network/mac/ResourceRequest.h8
-rw-r--r--WebCore/platform/network/mac/ResourceResponse.h7
-rw-r--r--WebCore/platform/network/qt/ProxyServerQt.cpp73
-rw-r--r--WebCore/platform/network/qt/QNetworkReplyHandler.cpp66
-rw-r--r--WebCore/platform/network/qt/QNetworkReplyHandler.h5
-rw-r--r--WebCore/platform/network/qt/ResourceRequest.h6
-rw-r--r--WebCore/platform/network/qt/ResourceResponse.h9
-rw-r--r--WebCore/platform/network/qt/SocketStreamHandle.h4
-rw-r--r--WebCore/platform/network/qt/SocketStreamHandlePrivate.h2
-rw-r--r--WebCore/platform/network/qt/SocketStreamHandleQt.cpp2
-rw-r--r--WebCore/platform/network/soup/ProxyServerSoup.cpp39
-rw-r--r--WebCore/platform/network/soup/ResourceHandleSoup.cpp572
-rw-r--r--WebCore/platform/network/soup/ResourceRequest.h9
-rw-r--r--WebCore/platform/network/soup/ResourceRequestSoup.cpp23
-rw-r--r--WebCore/platform/network/soup/ResourceResponse.h10
-rw-r--r--WebCore/platform/network/soup/cache/soup-directory-input-stream.c200
-rw-r--r--WebCore/platform/network/soup/cache/soup-directory-input-stream.h62
-rw-r--r--WebCore/platform/network/soup/cache/soup-http-input-stream.c921
-rw-r--r--WebCore/platform/network/soup/cache/soup-http-input-stream.h77
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-data.c170
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-data.h52
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-file.c331
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-file.h54
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-http.c340
-rw-r--r--WebCore/platform/network/soup/cache/soup-request-http.h54
-rw-r--r--WebCore/platform/network/soup/cache/soup-request.c312
-rw-r--r--WebCore/platform/network/soup/cache/soup-request.h100
-rw-r--r--WebCore/platform/network/soup/cache/soup-requester.c188
-rw-r--r--WebCore/platform/network/soup/cache/soup-requester.h81
-rw-r--r--WebCore/platform/network/soup/cache/webkit/soup-cache-private.h42
-rw-r--r--WebCore/platform/network/soup/cache/webkit/soup-cache.c1653
-rw-r--r--WebCore/platform/network/soup/cache/webkit/soup-cache.h102
-rw-r--r--WebCore/platform/network/win/CookieJarWin.cpp10
-rw-r--r--WebCore/platform/network/win/ResourceHandleWin.cpp8
69 files changed, 6047 insertions, 511 deletions
diff --git a/WebCore/platform/network/BlobData.cpp b/WebCore/platform/network/BlobData.cpp
index 21e8917..ff39ecc 100644
--- a/WebCore/platform/network/BlobData.cpp
+++ b/WebCore/platform/network/BlobData.cpp
@@ -31,40 +31,46 @@
#include "config.h"
#include "BlobData.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
namespace WebCore {
const long long BlobDataItem::toEndOfFile = -1;
const double BlobDataItem::doNotCheckFileChange = 0;
-void BlobDataItem::copy(const BlobDataItem& item)
+RawData::RawData()
{
- type = item.type;
- data = item.data; // This is OK because the underlying storage is Vector<char>.
- path = item.path.crossThreadString();
- url = item.url.copy();
- offset = item.offset;
- length = item.length;
- expectedModificationTime = item.expectedModificationTime;
}
-PassOwnPtr<BlobData> BlobData::copy() const
+void RawData::detachFromCurrentThread()
{
- OwnPtr<BlobData> blobData = adoptPtr(new BlobData());
- blobData->m_contentType = m_contentType.crossThreadString();
- blobData->m_contentDisposition = m_contentDisposition.crossThreadString();
- blobData->m_items.resize(m_items.size());
- for (size_t i = 0; i < m_items.size(); ++i)
- blobData->m_items.at(i).copy(m_items.at(i));
+}
+
+void BlobDataItem::detachFromCurrentThread()
+{
+ data->detachFromCurrentThread();
+ path = path.crossThreadString();
+ url = url.copy();
+}
- return blobData.release();
+PassOwnPtr<BlobData> BlobData::create()
+{
+ return adoptPtr(new BlobData());
}
-void BlobData::appendData(const CString& data)
+void BlobData::detachFromCurrentThread()
{
- m_items.append(BlobDataItem(data));
+ m_contentType = m_contentType.crossThreadString();
+ m_contentDisposition = m_contentDisposition.crossThreadString();
+ for (size_t i = 0; i < m_items.size(); ++i)
+ m_items.at(i).detachFromCurrentThread();
}
-void BlobData::appendData(const CString& data, long long offset, long long length)
+void BlobData::appendData(PassRefPtr<RawData> data, long long offset, long long length)
{
m_items.append(BlobDataItem(data, offset, length));
}
diff --git a/WebCore/platform/network/BlobData.h b/WebCore/platform/network/BlobData.h
index 13e3b9c..1ff6344 100644
--- a/WebCore/platform/network/BlobData.h
+++ b/WebCore/platform/network/BlobData.h
@@ -33,12 +33,30 @@
#include "KURL.h"
#include "PlatformString.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/CString.h>
+#include <wtf/Forward.h>
+#include <wtf/ThreadSafeShared.h>
namespace WebCore {
+class RawData : public ThreadSafeShared<RawData> {
+public:
+ static PassRefPtr<RawData> create()
+ {
+ return adoptRef(new RawData());
+ }
+
+ void detachFromCurrentThread();
+
+ const char* data() const { return m_data.data(); }
+ size_t length() const { return m_data.size(); }
+ Vector<char>* mutableData() { return &m_data; }
+
+private:
+ RawData();
+
+ Vector<char> m_data;
+};
+
struct BlobDataItem {
static const long long toEndOfFile;
static const double doNotCheckFileChange;
@@ -53,7 +71,7 @@ struct BlobDataItem {
}
// Constructor for String type (complete string).
- explicit BlobDataItem(const CString& data)
+ explicit BlobDataItem(PassRefPtr<RawData> data)
: type(Data)
, data(data)
, offset(0)
@@ -62,16 +80,6 @@ struct BlobDataItem {
{
}
- // Constructor for String type (partial string).
- BlobDataItem(const CString& data, long long offset, long long length)
- : type(Data)
- , data(data)
- , offset(offset)
- , length(length)
- , expectedModificationTime(doNotCheckFileChange)
- {
- }
-
// Constructor for File type (complete file).
explicit BlobDataItem(const String& path)
: type(File)
@@ -102,13 +110,13 @@ struct BlobDataItem {
{
}
- // Gets a copy of the data suitable for passing to another thread.
- void copy(const BlobDataItem&);
+ // Detaches from current thread so that it can be passed to another thread.
+ void detachFromCurrentThread();
enum { Data, File, Blob } type;
// For Data type.
- CString data;
+ RefPtr<RawData> data;
// For File type.
String path;
@@ -119,19 +127,29 @@ struct BlobDataItem {
long long offset;
long long length;
double expectedModificationTime;
+
+private:
+ friend class BlobData;
+
+ // Constructor for String type (partial string).
+ BlobDataItem(PassRefPtr<RawData> data, long long offset, long long length)
+ : type(Data)
+ , data(data)
+ , offset(offset)
+ , length(length)
+ , expectedModificationTime(doNotCheckFileChange)
+ {
+ }
};
typedef Vector<BlobDataItem> BlobDataItemList;
class BlobData {
public:
- static PassOwnPtr<BlobData> create()
- {
- return adoptPtr(new BlobData());
- }
+ static PassOwnPtr<BlobData> create();
- // Gets a copy of the data suitable for passing to another thread.
- PassOwnPtr<BlobData> copy() const;
+ // Detaches from current thread so that it can be passed to another thread.
+ void detachFromCurrentThread();
const String& contentType() const { return m_contentType; }
void setContentType(const String& contentType) { m_contentType = contentType; }
@@ -141,8 +159,8 @@ public:
const BlobDataItemList& items() const { return m_items; }
void swapItems(BlobDataItemList&);
-
- void appendData(const CString&);
+
+ void appendData(PassRefPtr<RawData>, long long offset, long long length);
void appendFile(const String& path);
void appendFile(const String& path, long long offset, long long length, double expectedModificationTime);
void appendBlob(const KURL&, long long offset, long long length);
@@ -154,7 +172,7 @@ private:
BlobData() { }
// This is only exposed to BlobStorageData.
- void appendData(const CString&, long long offset, long long length);
+ void appendData(const RawData&, long long offset, long long length);
String m_contentType;
String m_contentDisposition;
diff --git a/WebCore/platform/network/BlobRegistryImpl.cpp b/WebCore/platform/network/BlobRegistryImpl.cpp
index c5beb64..2c4e8fa 100644
--- a/WebCore/platform/network/BlobRegistryImpl.cpp
+++ b/WebCore/platform/network/BlobRegistryImpl.cpp
@@ -134,7 +134,7 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blo
for (BlobDataItemList::const_iterator iter = blobData->items().begin(); iter != blobData->items().end(); ++iter) {
switch (iter->type) {
case BlobDataItem::Data:
- blobStorageData->m_data.appendData(iter->data, 0, iter->data.length());
+ blobStorageData->m_data.appendData(iter->data, 0, iter->data->length());
break;
case BlobDataItem::File:
blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime);
@@ -158,10 +158,7 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, const KURL& srcURL)
if (!src)
return;
- RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(src->contentType(), src->contentDisposition());
- appendStorageItems(blobStorageData.get(), src->items());
-
- m_blobs.set(url.string(), blobStorageData);
+ m_blobs.set(url.string(), src);
}
void BlobRegistryImpl::unregisterBlobURL(const KURL& url)
diff --git a/WebCore/platform/network/BlobResourceHandle.cpp b/WebCore/platform/network/BlobResourceHandle.cpp
index 48ac2c0..753052a 100644
--- a/WebCore/platform/network/BlobResourceHandle.cpp
+++ b/WebCore/platform/network/BlobResourceHandle.cpp
@@ -359,7 +359,7 @@ int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int le
int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length;
if (bytesToRead > m_totalRemainingSize)
bytesToRead = static_cast<int>(m_totalRemainingSize);
- memcpy(buf, item.data.data() + item.offset + m_currentItemReadSize, bytesToRead);
+ memcpy(buf, item.data->data() + item.offset + m_currentItemReadSize, bytesToRead);
m_totalRemainingSize -= bytesToRead;
m_currentItemReadSize += bytesToRead;
@@ -434,7 +434,7 @@ void BlobResourceHandle::readDataAsync(const BlobDataItem& item)
long long bytesToRead = item.length - m_currentItemReadSize;
if (bytesToRead > m_totalRemainingSize)
bytesToRead = m_totalRemainingSize;
- consumeData(item.data.data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead));
+ consumeData(item.data->data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead));
m_currentItemReadSize = 0;
}
diff --git a/WebCore/platform/network/CredentialStorage.cpp b/WebCore/platform/network/CredentialStorage.cpp
index 38f71a4..428181d 100644
--- a/WebCore/platform/network/CredentialStorage.cpp
+++ b/WebCore/platform/network/CredentialStorage.cpp
@@ -29,7 +29,7 @@
#include "Credential.h"
#include "KURL.h"
#include "ProtectionSpaceHash.h"
-#include <wtf/text/CString.h>
+#include <wtf/text/StringConcatenate.h>
#include <wtf/text/StringHash.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
@@ -60,9 +60,9 @@ static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap()
static String originStringFromURL(const KURL& url)
{
if (url.port())
- return url.protocol() + "://" + url.host() + String::format(":%i/", url.port());
-
- return url.protocol() + "://" + url.host() + "/";
+ return makeString(url.protocol(), "://", url.host(), ':', String::number(url.port()), '/');
+
+ return makeString(url.protocol(), "://", url.host(), '/');
}
static String protectionSpaceMapKeyFromURL(const KURL& url)
diff --git a/WebCore/platform/network/DataURL.cpp b/WebCore/platform/network/DataURL.cpp
new file mode 100644
index 0000000..5de1b34
--- /dev/null
+++ b/WebCore/platform/network/DataURL.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ *
+ * 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 APPLE AND ITS CONTRIBUTORS "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 APPLE COMPUTER, INC. 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 "DataURL.h"
+
+#include "Base64.h"
+#include "HTTPParsers.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "TextEncoding.h"
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+void handleDataURL(ResourceHandle* handle)
+{
+ ASSERT(handle->firstRequest().url().protocolIs("data"));
+ String url = handle->firstRequest().url().string();
+
+ int index = url.find(',');
+ if (index == -1) {
+ handle->client()->cannotShowURL(handle);
+ return;
+ }
+
+ String mediaType = url.substring(5, index - 5);
+ String data = url.substring(index + 1);
+
+ bool base64 = mediaType.endsWith(";base64", false);
+ if (base64)
+ mediaType = mediaType.left(mediaType.length() - 7);
+
+ if (mediaType.isEmpty())
+ mediaType = "text/plain;charset=US-ASCII";
+
+ String mimeType = extractMIMETypeFromMediaType(mediaType);
+ String charset = extractCharsetFromMediaType(mediaType);
+
+ ResourceResponse response;
+ response.setMimeType(mimeType);
+ response.setTextEncodingName(charset);
+ response.setURL(handle->firstRequest().url());
+
+ if (base64) {
+ data = decodeURLEscapeSequences(data);
+ handle->client()->didReceiveResponse(handle, response);
+
+ Vector<char> out;
+ if (base64Decode(data, out, IgnoreWhitespace) && out.size() > 0) {
+ response.setExpectedContentLength(out.size());
+ handle->client()->didReceiveData(handle, out.data(), out.size(), 0);
+ }
+ } else {
+ data = decodeURLEscapeSequences(data, TextEncoding(charset));
+ handle->client()->didReceiveResponse(handle, response);
+
+ CString encodedData = TextEncoding().encode(data.characters(), data.length(), URLEncodedEntitiesForUnencodables);
+ response.setExpectedContentLength(encodedData.length());
+ if (encodedData.length())
+ handle->client()->didReceiveData(handle, encodedData.data(), encodedData.length(), 0);
+ }
+
+ handle->client()->didFinishLoading(handle, 0);
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/DataURL.h b/WebCore/platform/network/DataURL.h
new file mode 100644
index 0000000..33076a0
--- /dev/null
+++ b/WebCore/platform/network/DataURL.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ *
+ * 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 APPLE AND ITS CONTRIBUTORS "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 APPLE COMPUTER, INC. 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 DataURL_h
+#define DataURL_h
+
+namespace WebCore {
+
+class ResourceHandle;
+
+void handleDataURL(ResourceHandle*);
+
+}
+
+#endif // DataURL_h
diff --git a/WebCore/platform/network/HTTPHeaderMap.cpp b/WebCore/platform/network/HTTPHeaderMap.cpp
index e304ffa..92079a0 100644
--- a/WebCore/platform/network/HTTPHeaderMap.cpp
+++ b/WebCore/platform/network/HTTPHeaderMap.cpp
@@ -37,6 +37,14 @@ using namespace std;
namespace WebCore {
+HTTPHeaderMap::HTTPHeaderMap()
+{
+}
+
+HTTPHeaderMap::~HTTPHeaderMap()
+{
+}
+
PassOwnPtr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::copyData() const
{
OwnPtr<CrossThreadHTTPHeaderMapData> data(new CrossThreadHTTPHeaderMapData());
@@ -58,7 +66,17 @@ void HTTPHeaderMap::adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData> data)
set(header.first, header.second);
}
}
-
+
+String HTTPHeaderMap::get(const AtomicString& name) const
+{
+ return HashMap<AtomicString, String, CaseFoldingHash>::get(name);
+}
+
+pair<HTTPHeaderMap::iterator, bool> HTTPHeaderMap::add(const AtomicString& name, const String& value)
+{
+ return HashMap<AtomicString, String, CaseFoldingHash>::add(name, value);
+}
+
// Adapter that allows the HashMap to take C strings as keys.
struct CaseFoldingCStringTranslator {
static unsigned hash(const char* cString)
diff --git a/WebCore/platform/network/HTTPHeaderMap.h b/WebCore/platform/network/HTTPHeaderMap.h
index c6145bd..02071e3 100644
--- a/WebCore/platform/network/HTTPHeaderMap.h
+++ b/WebCore/platform/network/HTTPHeaderMap.h
@@ -41,20 +41,17 @@ namespace WebCore {
class HTTPHeaderMap : public HashMap<AtomicString, String, CaseFoldingHash> {
public:
+ HTTPHeaderMap();
+ ~HTTPHeaderMap();
+
// Gets a copy of the data suitable for passing to another thread.
PassOwnPtr<CrossThreadHTTPHeaderMapData> copyData() const;
void adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData>);
- String get(const AtomicString& name) const
- {
- return HashMap<AtomicString, String, CaseFoldingHash>::get(name);
- }
+ String get(const AtomicString& name) const;
- pair<iterator, bool> add(const AtomicString& name, const String& value)
- {
- return HashMap<AtomicString, String, CaseFoldingHash>::add(name, value);
- }
+ pair<iterator, bool> add(const AtomicString& name, const String& value);
// Alternate accessors that are faster than converting the char* to AtomicString first.
bool contains(const char*) const;
diff --git a/WebCore/platform/network/ProxyServer.cpp b/WebCore/platform/network/ProxyServer.cpp
new file mode 100644
index 0000000..7ef283b
--- /dev/null
+++ b/WebCore/platform/network/ProxyServer.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "ProxyServer.h"
+
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+static void appendProxyServerString(StringBuilder& builder, const ProxyServer& proxyServer)
+{
+ switch (proxyServer.type()) {
+ case ProxyServer::Direct:
+ builder.append("DIRECT");
+ return;
+ case ProxyServer::HTTP:
+ case ProxyServer::HTTPS:
+ builder.append("PROXY");
+ break;
+ case ProxyServer::SOCKS:
+ builder.append("SOCKS");
+ break;
+ }
+
+ builder.append(' ');
+
+ ASSERT(!proxyServer.hostName().isNull());
+ builder.append(proxyServer.hostName());
+
+ builder.append(':');
+ ASSERT(proxyServer.port() != -1);
+ builder.append(String::number(proxyServer.port()));
+}
+
+String toString(const Vector<ProxyServer>& proxyServers)
+{
+ if (proxyServers.isEmpty())
+ return "DIRECT";
+
+ StringBuilder stringBuilder;
+ for (size_t i = 0; i < proxyServers.size(); ++i) {
+ if (i)
+ stringBuilder.append("; ");
+
+ appendProxyServerString(stringBuilder, proxyServers[i]);
+ }
+
+ return stringBuilder.toString();
+}
+
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/ProxyServer.h b/WebCore/platform/network/ProxyServer.h
new file mode 100644
index 0000000..8f7612e
--- /dev/null
+++ b/WebCore/platform/network/ProxyServer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 ProxyServer_h
+#define ProxyServer_h
+
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class KURL;
+class NetworkingContext;
+
+// Represents a single proxy server.
+class ProxyServer {
+public:
+ enum Type {
+ Direct,
+ HTTP,
+ HTTPS,
+ SOCKS,
+ };
+
+ ProxyServer()
+ : m_type(Direct)
+ , m_port(-1)
+ {
+ }
+
+ ProxyServer(Type type, const String& hostName, int port)
+ : m_type(type)
+ , m_hostName(hostName)
+ , m_port(port)
+ {
+ }
+
+ Type type() const { return m_type; }
+ const String& hostName() const { return m_hostName; }
+ int port() const { return m_port; }
+
+private:
+ Type m_type;
+ String m_hostName;
+ int m_port;
+};
+
+// Return a vector of proxy servers for the given URL.
+Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*);
+
+// Converts the given vector of proxy servers to a PAC string, as described in
+// http://web.archive.org/web/20060424005037/wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html
+String toString(const Vector<ProxyServer>&);
+
+} // namespace WebCore
+
+#endif // ProxyServer_h
diff --git a/WebCore/platform/network/ResourceHandle.h b/WebCore/platform/network/ResourceHandle.h
index 2ea42b1..bb94b59 100644
--- a/WebCore/platform/network/ResourceHandle.h
+++ b/WebCore/platform/network/ResourceHandle.h
@@ -94,16 +94,6 @@ class ResourceHandle : public RefCounted<ResourceHandle>
, public AuthenticationClient
#endif
{
-protected:
- ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff);
-
-private:
- enum FailureType {
- NoFailure,
- BlockedFailure,
- InvalidURLFailure
- };
-
public:
static PassRefPtr<ResourceHandle> create(NetworkingContext*, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff);
static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data);
@@ -203,7 +193,16 @@ public:
using RefCounted<ResourceHandle>::ref;
using RefCounted<ResourceHandle>::deref;
+protected:
+ ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff);
+
private:
+ enum FailureType {
+ NoFailure,
+ BlockedFailure,
+ InvalidURLFailure
+ };
+
void platformSetDefersLoading(bool);
void scheduleFailure(FailureType);
diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h
index 96fbf00..9cccecd 100644
--- a/WebCore/platform/network/ResourceHandleInternal.h
+++ b/WebCore/platform/network/ResourceHandleInternal.h
@@ -46,6 +46,8 @@
#endif
#if USE(SOUP)
+#include "soup-requester.h"
+#include <GRefPtr.h>
#include <libsoup/soup.h>
class Frame;
#endif
@@ -108,15 +110,11 @@ namespace WebCore {
, m_formDataStream(loader)
#endif
#if USE(SOUP)
- , m_msg(0)
, m_cancelled(false)
- , m_gfile(0)
- , m_inputStream(0)
- , m_cancellable(0)
, m_buffer(0)
- , m_bufferSize(0)
, m_total(0)
, m_idleHandler(0)
+ , m_gotChunkHandler(0)
#endif
#if PLATFORM(QT)
, m_job(0)
@@ -133,6 +131,9 @@ namespace WebCore {
m_user = url.user();
m_pass = url.pass();
m_firstRequest.removeCredentials();
+#if USE(SOUP)
+ m_requester = adoptPlatformRef(webkit_soup_requester_new());
+#endif
}
~ResourceHandleInternal();
@@ -185,16 +186,18 @@ namespace WebCore {
Vector<char> m_postBytes;
#endif
#if USE(SOUP)
- SoupMessage* m_msg;
+ PlatformRefPtr<SoupMessage> m_soupMessage;
ResourceResponse m_response;
bool m_cancelled;
- GFile* m_gfile;
- GInputStream* m_inputStream;
- GCancellable* m_cancellable;
+ PlatformRefPtr<WebKitSoupRequest> m_soupRequest;
+ PlatformRefPtr<WebKitSoupRequester> m_requester;
+ PlatformRefPtr<GInputStream> m_inputStream;
+ PlatformRefPtr<GCancellable> m_cancellable;
char* m_buffer;
- gsize m_bufferSize, m_total;
+ gsize m_total;
guint m_idleHandler;
RefPtr<NetworkingContext> m_context;
+ gulong m_gotChunkHandler;
#endif
#if PLATFORM(QT)
QNetworkReplyHandler* m_job;
diff --git a/WebCore/platform/network/ResourceRawHeaders.h b/WebCore/platform/network/ResourceRawHeaders.h
new file mode 100644
index 0000000..e5f8d99
--- /dev/null
+++ b/WebCore/platform/network/ResourceRawHeaders.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 Google, Inc. 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 APPLE INC. ``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 APPLE INC. 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 ResourceRawHeaders_h
+#define ResourceRawHeaders_h
+
+#include "HTTPHeaderMap.h"
+
+namespace WebCore {
+
+struct ResourceRawHeaders : RefCounted<ResourceRawHeaders> {
+ HTTPHeaderMap requestHeaders;
+ HTTPHeaderMap responseHeaders;
+};
+
+}
+
+#endif
diff --git a/WebCore/platform/network/ResourceRequestBase.cpp b/WebCore/platform/network/ResourceRequestBase.cpp
index fd8832f..5312007 100644
--- a/WebCore/platform/network/ResourceRequestBase.cpp
+++ b/WebCore/platform/network/ResourceRequestBase.cpp
@@ -65,6 +65,7 @@ PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadRes
}
request->setHTTPBody(data->m_httpBody);
request->setAllowCookies(data->m_allowCookies);
+ request->doPlatformAdopt(data);
return request.release();
}
@@ -87,7 +88,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const
if (m_httpBody)
data->m_httpBody = m_httpBody->deepCopy();
data->m_allowCookies = m_allowCookies;
- return data.release();
+ return asResourceRequest().doPlatformCopyData(data.release());
}
bool ResourceRequestBase::isEmpty() const
@@ -355,7 +356,7 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque
return true;
}
-bool operator==(const ResourceRequestBase& a, const ResourceRequestBase& b)
+bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceRequest& b)
{
if (!equalIgnoringHeaderFields(a, b))
return false;
@@ -363,7 +364,7 @@ bool operator==(const ResourceRequestBase& a, const ResourceRequestBase& b)
if (a.httpHeaderFields() != b.httpHeaderFields())
return false;
- return true;
+ return ResourceRequest::platformCompare(a, b);
}
bool ResourceRequestBase::isConditional() const
diff --git a/WebCore/platform/network/ResourceRequestBase.h b/WebCore/platform/network/ResourceRequestBase.h
index 1622cdd..33a184e 100644
--- a/WebCore/platform/network/ResourceRequestBase.h
+++ b/WebCore/platform/network/ResourceRequestBase.h
@@ -136,10 +136,16 @@ namespace WebCore {
bool reportLoadTiming() const { return m_reportLoadTiming; }
void setReportLoadTiming(bool reportLoadTiming) { m_reportLoadTiming = reportLoadTiming; }
+ // Whether actual headers being sent/received should be collected and reported for the request.
+ bool reportRawHeaders() const { return m_reportRawHeaders; }
+ void setReportRawHeaders(bool reportRawHeaders) { m_reportRawHeaders = reportRawHeaders; }
+
// What this request is for.
TargetType targetType() const { return m_targetType; }
void setTargetType(TargetType type) { m_targetType = type; }
+ static bool compare(const ResourceRequest&, const ResourceRequest&);
+
protected:
// Used when ResourceRequest is initialized from a platform representation of the request
ResourceRequestBase()
@@ -147,6 +153,7 @@ namespace WebCore {
, m_platformRequestUpdated(true)
, m_reportUploadProgress(false)
, m_reportLoadTiming(false)
+ , m_reportRawHeaders(false)
, m_targetType(TargetIsSubresource)
{
}
@@ -161,6 +168,7 @@ namespace WebCore {
, m_platformRequestUpdated(false)
, m_reportUploadProgress(false)
, m_reportLoadTiming(false)
+ , m_reportRawHeaders(false)
, m_targetType(TargetIsSubresource)
{
}
@@ -168,6 +176,9 @@ namespace WebCore {
void updatePlatformRequest() const;
void updateResourceRequest() const;
+ // The ResourceRequest subclass may "shadow" this method to compare platform specific fields
+ static bool platformCompare(const ResourceRequest&, const ResourceRequest&) { return true; }
+
KURL m_url;
ResourceRequestCachePolicy m_cachePolicy;
@@ -182,6 +193,7 @@ namespace WebCore {
mutable bool m_platformRequestUpdated;
bool m_reportUploadProgress;
bool m_reportLoadTiming;
+ bool m_reportRawHeaders;
TargetType m_targetType;
private:
@@ -190,10 +202,10 @@ namespace WebCore {
bool equalIgnoringHeaderFields(const ResourceRequestBase&, const ResourceRequestBase&);
- bool operator==(const ResourceRequestBase&, const ResourceRequestBase&);
- inline bool operator!=(ResourceRequestBase& a, const ResourceRequestBase& b) { return !(a == b); }
+ inline bool operator==(const ResourceRequest& a, const ResourceRequest& b) { return ResourceRequestBase::compare(a, b); }
+ inline bool operator!=(ResourceRequest& a, const ResourceRequest& b) { return !(a == b); }
- struct CrossThreadResourceRequestData : Noncopyable {
+ struct CrossThreadResourceRequestDataBase : Noncopyable {
KURL m_url;
ResourceRequestCachePolicy m_cachePolicy;
diff --git a/WebCore/platform/network/ResourceResponseBase.cpp b/WebCore/platform/network/ResourceResponseBase.cpp
index e231652..a254168 100644
--- a/WebCore/platform/network/ResourceResponseBase.cpp
+++ b/WebCore/platform/network/ResourceResponseBase.cpp
@@ -39,6 +39,11 @@ namespace WebCore {
static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result);
+inline const ResourceResponse& ResourceResponseBase::asResourceResponse() const
+{
+ return *static_cast<const ResourceResponse*>(this);
+}
+
ResourceResponseBase::ResourceResponseBase()
: m_expectedContentLength(0)
, m_httpStatusCode(0)
@@ -107,7 +112,7 @@ PassOwnPtr<ResourceResponse> ResourceResponseBase::adopt(PassOwnPtr<CrossThreadR
response->m_httpHeaderFields.adopt(data->m_httpHeaders.release());
response->setLastModifiedDate(data->m_lastModifiedDate);
response->setResourceLoadTiming(data->m_resourceLoadTiming.release());
-
+ response->doPlatformAdopt(data);
return response.release();
}
@@ -125,7 +130,7 @@ PassOwnPtr<CrossThreadResourceResponseData> ResourceResponseBase::copyData() con
data->m_lastModifiedDate = lastModifiedDate();
if (m_resourceLoadTiming)
data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy();
- return data.release();
+ return asResourceResponse().doPlatformCopyData(data.release());
}
bool ResourceResponseBase::isHTTP() const
@@ -515,6 +520,20 @@ void ResourceResponseBase::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming>
m_resourceLoadTiming = resourceLoadTiming;
}
+PassRefPtr<ResourceRawHeaders> ResourceResponseBase::resourceRawHeaders() const
+{
+ lazyInit();
+
+ return m_resourceRawHeaders.get();
+}
+
+void ResourceResponseBase::setResourceRawHeaders(PassRefPtr<ResourceRawHeaders> headers)
+{
+ lazyInit();
+
+ m_resourceRawHeaders = headers;
+}
+
void ResourceResponseBase::lazyInit() const
{
const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit();
diff --git a/WebCore/platform/network/ResourceResponseBase.h b/WebCore/platform/network/ResourceResponseBase.h
index 65e24ad..4da13bb 100644
--- a/WebCore/platform/network/ResourceResponseBase.h
+++ b/WebCore/platform/network/ResourceResponseBase.h
@@ -30,6 +30,7 @@
#include "HTTPHeaderMap.h"
#include "KURL.h"
#include "ResourceLoadTiming.h"
+#include "ResourceRawHeaders.h"
#include <wtf/PassOwnPtr.h>
#include <wtf/RefPtr.h>
@@ -109,6 +110,9 @@ public:
ResourceLoadTiming* resourceLoadTiming() const;
void setResourceLoadTiming(PassRefPtr<ResourceLoadTiming>);
+ PassRefPtr<ResourceRawHeaders> resourceRawHeaders() const;
+ void setResourceRawHeaders(PassRefPtr<ResourceRawHeaders>);
+
// The ResourceResponse subclass may "shadow" this method to provide platform-specific memory usage information
unsigned memoryUsage() const
{
@@ -116,7 +120,7 @@ public:
return 1280;
}
- static bool compare(const ResourceResponse& a, const ResourceResponse& b);
+ static bool compare(const ResourceResponse&, const ResourceResponse&);
protected:
ResourceResponseBase();
@@ -143,10 +147,12 @@ protected:
unsigned m_connectionID;
bool m_connectionReused : 1;
RefPtr<ResourceLoadTiming> m_resourceLoadTiming;
+ RefPtr<ResourceRawHeaders> m_resourceRawHeaders;
bool m_isNull : 1;
private:
+ const ResourceResponse& asResourceResponse() const;
void parseCacheControlDirectives() const;
mutable bool m_haveParsedCacheControlHeader : 1;
@@ -169,7 +175,7 @@ private:
inline bool operator==(const ResourceResponse& a, const ResourceResponse& b) { return ResourceResponseBase::compare(a, b); }
inline bool operator!=(const ResourceResponse& a, const ResourceResponse& b) { return !(a == b); }
-struct CrossThreadResourceResponseData : Noncopyable {
+struct CrossThreadResourceResponseDataBase : Noncopyable {
KURL m_url;
String m_mimeType;
long long m_expectedContentLength;
diff --git a/WebCore/platform/network/android/ResourceRequest.h b/WebCore/platform/network/android/ResourceRequest.h
index 1edd4bf..745c2ef 100644
--- a/WebCore/platform/network/android/ResourceRequest.h
+++ b/WebCore/platform/network/android/ResourceRequest.h
@@ -53,6 +53,12 @@ public:
private:
friend class ResourceRequestBase;
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+};
+
+struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/android/ResourceResponse.h b/WebCore/platform/network/android/ResourceResponse.h
index e6ae2cc..78307b9 100644
--- a/WebCore/platform/network/android/ResourceResponse.h
+++ b/WebCore/platform/network/android/ResourceResponse.h
@@ -46,6 +46,12 @@ private:
{
notImplemented();
}
+
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+};
+
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/cf/ProxyServerCFNet.cpp b/WebCore/platform/network/cf/ProxyServerCFNet.cpp
new file mode 100644
index 0000000..3bef808
--- /dev/null
+++ b/WebCore/platform/network/cf/ProxyServerCFNet.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "ProxyServer.h"
+
+#include "KURL.h"
+#include <wtf/RetainPtr.h>
+
+namespace WebCore {
+
+#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+static void addProxyServersForURL(Vector<ProxyServer>& proxyServers, const KURL& url)
+{
+ RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, CFNetworkCopySystemProxySettings());
+ if (!proxySettings)
+ return;
+
+ RetainPtr<CFURLRef> cfURL(AdoptCF, url.createCFURL());
+ RetainPtr<CFArrayRef> proxiesForURL(AdoptCF, CFNetworkCopyProxiesForURL(cfURL.get(), proxySettings.get()));
+ if (!proxiesForURL)
+ return;
+
+ CFIndex numProxies = CFArrayGetCount(proxiesForURL.get());
+ for (CFIndex i = 0; i < numProxies; ++i) {
+ CFDictionaryRef proxyDictionary = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxiesForURL.get(), i));
+
+ ProxyServer::Type type = ProxyServer::Direct;
+ CFStringRef typeString = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyTypeKey));
+ if (CFEqual(typeString, kCFProxyTypeAutoConfigurationURL)) {
+ // FIXME: Handle PAC URLs.
+ continue;
+ }
+
+ if (CFEqual(typeString, kCFProxyTypeNone)) {
+ proxyServers.append(ProxyServer(ProxyServer::Direct, String(), -1));
+ continue;
+ }
+
+ if (CFEqual(typeString, kCFProxyTypeHTTP))
+ type = ProxyServer::HTTP;
+ else if (CFEqual(typeString, kCFProxyTypeHTTPS))
+ type = ProxyServer::HTTPS;
+ else if (CFEqual(typeString, kCFProxyTypeSOCKS))
+ type = ProxyServer::SOCKS;
+ else {
+ // We don't know how to handle this type.
+ continue;
+ }
+
+ CFStringRef host = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyHostNameKey));
+ CFNumberRef port = static_cast<CFNumberRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyPortNumberKey));
+ SInt32 portValue;
+ CFNumberGetValue(port, kCFNumberSInt32Type, &portValue);
+
+ proxyServers.append(ProxyServer(type, host, portValue));
+ }
+}
+
+Vector<ProxyServer> proxyServersForURL(const KURL& url, const NetworkingContext*)
+{
+ Vector<ProxyServer> proxyServers;
+
+ addProxyServersForURL(proxyServers, url);
+ return proxyServers;
+
+}
+#else
+Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*)
+{
+ // FIXME: Implement.
+ return Vector<ProxyServer>();
+}
+#endif
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/cf/ResourceRequest.h b/WebCore/platform/network/cf/ResourceRequest.h
index e361af5..9ed79f2 100644
--- a/WebCore/platform/network/cf/ResourceRequest.h
+++ b/WebCore/platform/network/cf/ResourceRequest.h
@@ -68,10 +68,16 @@ namespace WebCore {
void doUpdatePlatformRequest();
void doUpdateResourceRequest();
-
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+
RetainPtr<CFURLRequestRef> m_cfRequest;
};
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
+ };
+
} // namespace WebCore
#endif // ResourceRequest_h
diff --git a/WebCore/platform/network/cf/ResourceResponse.h b/WebCore/platform/network/cf/ResourceResponse.h
index 04cc82c..5e27670 100644
--- a/WebCore/platform/network/cf/ResourceResponse.h
+++ b/WebCore/platform/network/cf/ResourceResponse.h
@@ -70,12 +70,18 @@ private:
friend class ResourceResponseBase;
void platformLazyInit();
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+
static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b);
RetainPtr<CFURLResponseRef> m_cfResponse;
bool m_isUpToDate;
};
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
+};
+
} // namespace WebCore
#endif // ResourceResponse_h
diff --git a/WebCore/platform/network/cf/SocketStreamHandle.h b/WebCore/platform/network/cf/SocketStreamHandle.h
index 63bf9a7..8643db7 100644
--- a/WebCore/platform/network/cf/SocketStreamHandle.h
+++ b/WebCore/platform/network/cf/SocketStreamHandle.h
@@ -36,6 +36,7 @@
#include "SocketStreamHandleBase.h"
#include <wtf/RetainPtr.h>
+
namespace WebCore {
class AuthenticationChallenge;
diff --git a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
index c66de33..800301a 100644
--- a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
+++ b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
@@ -194,8 +194,10 @@ void SocketStreamHandle::chooseProxy()
void SocketStreamHandle::chooseProxyFromArray(CFArrayRef proxyArray)
{
- if (!proxyArray)
+ if (!proxyArray) {
m_connectionType = Direct;
+ return;
+ }
CFIndex proxyArrayCount = CFArrayGetCount(proxyArray);
@@ -565,7 +567,7 @@ void SocketStreamHandle::writeStreamCallback(CFStreamEventType type)
break;
}
- ASSERT(m_state = Open);
+ ASSERT(m_state == Open);
ASSERT(m_connectingSubstate == Connected);
sendPendingData();
diff --git a/WebCore/platform/network/chromium/ResourceRequest.cpp b/WebCore/platform/network/chromium/ResourceRequest.cpp
index 016bd34..a33895a 100644
--- a/WebCore/platform/network/chromium/ResourceRequest.cpp
+++ b/WebCore/platform/network/chromium/ResourceRequest.cpp
@@ -37,4 +37,19 @@ unsigned initializeMaximumHTTPConnectionCountPerHost()
return 10000;
}
+PassOwnPtr<CrossThreadResourceRequestData> ResourceRequest::doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const
+{
+ data->m_requestorID = m_requestorID;
+ data->m_requestorProcessID = m_requestorProcessID;
+ data->m_appCacheHostID = m_appCacheHostID;
+ return data;
+}
+
+void ResourceRequest::doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData> data)
+{
+ m_requestorID = data->m_requestorID;
+ m_requestorProcessID = data->m_requestorProcessID;
+ m_appCacheHostID = data->m_appCacheHostID;
+}
+
} // namespace WebCore
diff --git a/WebCore/platform/network/chromium/ResourceRequest.h b/WebCore/platform/network/chromium/ResourceRequest.h
index 8ef0c5e..8571cf4 100644
--- a/WebCore/platform/network/chromium/ResourceRequest.h
+++ b/WebCore/platform/network/chromium/ResourceRequest.h
@@ -90,6 +90,15 @@ namespace WebCore {
void doUpdatePlatformRequest() {}
void doUpdateResourceRequest() {}
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData>) const;
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>);
+
+ int m_requestorID;
+ int m_requestorProcessID;
+ int m_appCacheHostID;
+ };
+
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
int m_requestorID;
int m_requestorProcessID;
int m_appCacheHostID;
diff --git a/WebCore/platform/network/chromium/ResourceResponse.cpp b/WebCore/platform/network/chromium/ResourceResponse.cpp
new file mode 100644
index 0000000..acd44d3
--- /dev/null
+++ b/WebCore/platform/network/chromium/ResourceResponse.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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 APPLE AND ITS CONTRIBUTORS "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 APPLE COMPUTER, INC. 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 "ResourceResponse.h"
+
+namespace WebCore {
+
+PassOwnPtr<CrossThreadResourceResponseData> ResourceResponse::doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const
+{
+ data->m_appCacheID = m_appCacheID;
+ data->m_appCacheManifestURL = m_appCacheManifestURL.copy();
+ data->m_isContentFiltered = m_isContentFiltered;
+ data->m_isMultipartPayload = m_isMultipartPayload;
+ data->m_wasFetchedViaSPDY = m_wasFetchedViaSPDY;
+ data->m_wasNpnNegotiated = m_wasNpnNegotiated;
+ data->m_wasAlternateProtocolAvailable = m_wasAlternateProtocolAvailable;
+ data->m_wasFetchedViaProxy = m_wasFetchedViaProxy;
+ data->m_responseTime = m_responseTime;
+ return data;
+}
+
+void ResourceResponse::doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData> data)
+{
+ m_appCacheID = data->m_appCacheID;
+ m_appCacheManifestURL = data->m_appCacheManifestURL.copy();
+ m_isContentFiltered = data->m_isContentFiltered;
+ m_isMultipartPayload = data->m_isMultipartPayload;
+ m_wasFetchedViaSPDY = data->m_wasFetchedViaSPDY;
+ m_wasNpnNegotiated = data->m_wasNpnNegotiated;
+ m_wasAlternateProtocolAvailable = data->m_wasAlternateProtocolAvailable;
+ m_wasFetchedViaProxy = data->m_wasFetchedViaProxy;
+ m_responseTime = data->m_responseTime;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/chromium/ResourceResponse.h b/WebCore/platform/network/chromium/ResourceResponse.h
index 852b9f5..5e99994 100644
--- a/WebCore/platform/network/chromium/ResourceResponse.h
+++ b/WebCore/platform/network/chromium/ResourceResponse.h
@@ -109,6 +109,9 @@ namespace WebCore {
notImplemented();
}
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData>) const;
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>);
+
// The id of the appcache this response was retrieved from, or zero if
// the response was not retrieved from an appcache.
long long m_appCacheID;
@@ -142,6 +145,18 @@ namespace WebCore {
double m_responseTime;
};
+ struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
+ long long m_appCacheID;
+ KURL m_appCacheManifestURL;
+ bool m_isContentFiltered;
+ bool m_isMultipartPayload;
+ bool m_wasFetchedViaSPDY;
+ bool m_wasNpnNegotiated;
+ bool m_wasAlternateProtocolAvailable;
+ bool m_wasFetchedViaProxy;
+ double m_responseTime;
+ };
+
} // namespace WebCore
#endif
diff --git a/WebCore/platform/network/curl/ProxyServerCurl.cpp b/WebCore/platform/network/curl/ProxyServerCurl.cpp
new file mode 100644
index 0000000..212768a
--- /dev/null
+++ b/WebCore/platform/network/curl/ProxyServerCurl.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org>. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "ProxyServer.h"
+
+#include "KURL.h"
+#include <wtf/RetainPtr.h>
+
+namespace WebCore {
+
+Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*)
+{
+ // FIXME: Implement.
+ return Vector<ProxyServer>();
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/curl/ResourceHandleCurl.cpp b/WebCore/platform/network/curl/ResourceHandleCurl.cpp
index 052ac56..f360f86 100644
--- a/WebCore/platform/network/curl/ResourceHandleCurl.cpp
+++ b/WebCore/platform/network/curl/ResourceHandleCurl.cpp
@@ -83,14 +83,6 @@ void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& err
m_error = error;
}
-
-static HashSet<String>& allowsAnyHTTPSCertificateHosts()
-{
- static HashSet<String> hosts;
-
- return hosts;
-}
-
ResourceHandleInternal::~ResourceHandleInternal()
{
fastFree(m_url);
@@ -133,6 +125,13 @@ bool ResourceHandle::supportsBufferedData()
}
#if PLATFORM(WIN) && PLATFORM(CF)
+static HashSet<String>& allowsAnyHTTPSCertificateHosts()
+{
+ static HashSet<String> hosts;
+
+ return hosts;
+}
+
void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
{
allowsAnyHTTPSCertificateHosts().add(host.lower());
diff --git a/WebCore/platform/network/curl/ResourceHandleManager.cpp b/WebCore/platform/network/curl/ResourceHandleManager.cpp
index a4d71e0..2e4259c 100644
--- a/WebCore/platform/network/curl/ResourceHandleManager.cpp
+++ b/WebCore/platform/network/curl/ResourceHandleManager.cpp
@@ -34,14 +34,13 @@
#include "config.h"
#include "ResourceHandleManager.h"
-#include "Base64.h"
+#include "DataURL.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "NotImplemented.h"
#include "ResourceError.h"
#include "ResourceHandle.h"
#include "ResourceHandleInternal.h"
-#include "TextEncoding.h"
#include <errno.h>
#include <stdio.h>
@@ -121,8 +120,9 @@ static void curl_unlock_callback(CURL* handle, curl_lock_data data, void* userPt
ResourceHandleManager::ResourceHandleManager()
: m_downloadTimer(this, &ResourceHandleManager::downloadTimerCallback)
, m_cookieJarFileName(0)
- , m_runningJobs(0)
, m_certificatePath (certificatePath())
+ , m_runningJobs(0)
+
{
curl_global_init(CURL_GLOBAL_ALL);
m_curlMultiHandle = curl_multi_init();
@@ -164,7 +164,7 @@ static void handleLocalReceiveResponse (CURL* handle, ResourceHandle* job, Resou
// TODO: See if there is a better approach for handling this.
const char* hdr;
CURLcode err = curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &hdr);
- ASSERT(CURLE_OK == err);
+ ASSERT_UNUSED(err, CURLE_OK == err);
d->m_response.setURL(KURL(ParsedURLString, hdr));
if (d->client())
d->client()->didReceiveResponse(job, d->m_response);
@@ -372,7 +372,7 @@ void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>*
ASSERT(handle);
ResourceHandle* job = 0;
CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
- ASSERT(CURLE_OK == err);
+ ASSERT_UNUSED(err, CURLE_OK == err);
ASSERT(job);
if (!job)
continue;
@@ -572,67 +572,12 @@ bool ResourceHandleManager::startScheduledJobs()
return started;
}
-static void parseDataUrl(ResourceHandle* handle)
-{
- ResourceHandleClient* client = handle->client();
-
- ASSERT(client);
- if (!client)
- return;
-
- String url = handle->firstRequest().url().string();
- ASSERT(url.startsWith("data:", false));
-
- int index = url.find(',');
- if (index == -1) {
- client->cannotShowURL(handle);
- return;
- }
-
- String mediaType = url.substring(5, index - 5);
- String data = url.substring(index + 1);
-
- bool base64 = mediaType.endsWith(";base64", false);
- if (base64)
- mediaType = mediaType.left(mediaType.length() - 7);
-
- if (mediaType.isEmpty())
- mediaType = "text/plain;charset=US-ASCII";
-
- String mimeType = extractMIMETypeFromMediaType(mediaType);
- String charset = extractCharsetFromMediaType(mediaType);
-
- ResourceResponse response;
- response.setMimeType(mimeType);
-
- if (base64) {
- data = decodeURLEscapeSequences(data);
- response.setTextEncodingName(charset);
- client->didReceiveResponse(handle, response);
-
- // WebCore's decoder fails on Acid3 test 97 (whitespace).
- Vector<char> out;
- CString latin1 = data.latin1();
- if (base64Decode(latin1.data(), latin1.length(), out) && out.size() > 0)
- client->didReceiveData(handle, out.data(), out.size(), 0);
- } else {
- // We have to convert to UTF-16 early due to limitations in KURL
- data = decodeURLEscapeSequences(data, TextEncoding(charset));
- response.setTextEncodingName("UTF-16");
- client->didReceiveResponse(handle, response);
- if (data.length() > 0)
- client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0);
- }
-
- client->didFinishLoading(handle, 0);
-}
-
void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job)
{
KURL kurl = job->firstRequest().url();
- if (kurl.protocolIs("data")) {
- parseDataUrl(job);
+ if (kurl.protocolIsData()) {
+ handleDataURL(job);
return;
}
@@ -662,8 +607,8 @@ void ResourceHandleManager::startJob(ResourceHandle* job)
{
KURL kurl = job->firstRequest().url();
- if (kurl.protocolIs("data")) {
- parseDataUrl(job);
+ if (kurl.protocolIsData()) {
+ handleDataURL(job);
return;
}
@@ -711,7 +656,7 @@ void ResourceHandleManager::initializeHandle(ResourceHandle* job)
CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL);
// If we did not pause the handle, we would ASSERT in the
// header callback. So just assert here.
- ASSERT(error == CURLE_OK);
+ ASSERT_UNUSED(error, error == CURLE_OK);
}
#endif
#ifndef NDEBUG
diff --git a/WebCore/platform/network/curl/ResourceRequest.h b/WebCore/platform/network/curl/ResourceRequest.h
index 12dc214..c88501a 100644
--- a/WebCore/platform/network/curl/ResourceRequest.h
+++ b/WebCore/platform/network/curl/ResourceRequest.h
@@ -69,6 +69,12 @@ namespace WebCore {
void doUpdatePlatformRequest() {}
void doUpdateResourceRequest() {}
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+ };
+
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/curl/ResourceResponse.h b/WebCore/platform/network/curl/ResourceResponse.h
index 860e902..49dd56c 100644
--- a/WebCore/platform/network/curl/ResourceResponse.h
+++ b/WebCore/platform/network/curl/ResourceResponse.h
@@ -28,7 +28,7 @@
#include "ResourceResponseBase.h"
-typedef const struct _CFURLResponse* CFURLResponseRef;
+typedef struct _CFURLResponse* CFURLResponseRef;
namespace WebCore {
@@ -52,9 +52,17 @@ public:
CFURLResponseRef cfURLResponse() const { return 0; }
private:
+ friend class ResourceResponseBase;
+
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+
bool m_responseFired;
};
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
+};
+
} // namespace WebCore
#endif // ResourceResponse_h
diff --git a/WebCore/platform/network/mac/FormDataStreamMac.mm b/WebCore/platform/network/mac/FormDataStreamMac.mm
index ed98356..9b5884f 100644
--- a/WebCore/platform/network/mac/FormDataStreamMac.mm
+++ b/WebCore/platform/network/mac/FormDataStreamMac.mm
@@ -424,7 +424,7 @@ void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
for (size_t j = 0; j < blobData->items().size(); ++j) {
const BlobDataItem& blobItem = blobData->items()[j];
if (blobItem.type == BlobDataItem::Data) {
- newFormData->appendData(blobItem.data.data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
+ newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
} else {
ASSERT(blobItem.type == BlobDataItem::File);
newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime);
diff --git a/WebCore/platform/network/mac/ResourceRequest.h b/WebCore/platform/network/mac/ResourceRequest.h
index b09e72d..ea0d13a 100644
--- a/WebCore/platform/network/mac/ResourceRequest.h
+++ b/WebCore/platform/network/mac/ResourceRequest.h
@@ -73,10 +73,16 @@ namespace WebCore {
void doUpdatePlatformRequest();
void doUpdateResourceRequest();
-
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+
RetainPtr<NSURLRequest> m_nsRequest;
};
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
+ };
+
} // namespace WebCore
#endif // ResourceRequest_h
diff --git a/WebCore/platform/network/mac/ResourceResponse.h b/WebCore/platform/network/mac/ResourceResponse.h
index 16b0cbf..2867e9f 100644
--- a/WebCore/platform/network/mac/ResourceResponse.h
+++ b/WebCore/platform/network/mac/ResourceResponse.h
@@ -75,12 +75,19 @@ private:
friend class ResourceResponseBase;
void platformLazyInit();
+
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+
static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b);
RetainPtr<NSURLResponse> m_nsResponse;
bool m_isUpToDate;
};
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
+};
+
} // namespace WebCore
#endif // ResourceResponse_h
diff --git a/WebCore/platform/network/qt/ProxyServerQt.cpp b/WebCore/platform/network/qt/ProxyServerQt.cpp
new file mode 100644
index 0000000..0bf3687
--- /dev/null
+++ b/WebCore/platform/network/qt/ProxyServerQt.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "ProxyServer.h"
+
+#include "KURL.h"
+#include "NetworkingContext.h"
+
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+#include <QNetworkProxyFactory>
+#include <QNetworkProxyQuery>
+#include <QUrl>
+
+namespace WebCore {
+
+Vector<ProxyServer> proxyServersForURL(const KURL& url, const NetworkingContext* context)
+{
+ Vector<ProxyServer> servers;
+
+ const QNetworkAccessManager* accessManager = context ? context->networkAccessManager() : 0;
+ QNetworkProxyFactory* proxyFactory = accessManager ? accessManager->proxyFactory() : 0;
+
+ if (proxyFactory) {
+ const QList<QNetworkProxy> proxies = proxyFactory->queryProxy(QNetworkProxyQuery(url));
+ Q_FOREACH(const QNetworkProxy& proxy, proxies) {
+ ProxyServer::Type proxyType;
+ switch (proxy.type()) {
+ case QNetworkProxy::Socks5Proxy:
+ proxyType = ProxyServer::SOCKS;
+ break;
+ case QNetworkProxy::HttpProxy:
+ case QNetworkProxy::HttpCachingProxy:
+ case QNetworkProxy::FtpCachingProxy:
+ proxyType = ProxyServer::HTTP;
+ break;
+ case QNetworkProxy::DefaultProxy:
+ case QNetworkProxy::NoProxy:
+ default:
+ proxyType = ProxyServer::Direct;
+ break;
+ }
+ servers.append(ProxyServer(proxyType, proxy.hostName(), proxy.port()));
+ }
+ }
+
+ return servers;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
index ca3af75..01e624e 100644
--- a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
+++ b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
@@ -30,6 +30,7 @@
#include "ResourceRequest.h"
#include <QDateTime>
#include <QFile>
+#include <QFileInfo>
#include <QNetworkReply>
#include <QNetworkCookie>
#include <qwebframe.h>
@@ -59,11 +60,14 @@ FormDataIODevice::FormDataIODevice(FormData* data)
: m_formElements(data ? data->elements() : Vector<FormDataElement>())
, m_currentFile(0)
, m_currentDelta(0)
+ , m_fileSize(0)
+ , m_dataSize(0)
{
setOpenMode(FormDataIODevice::ReadOnly);
if (!m_formElements.isEmpty() && m_formElements[0].m_type == FormDataElement::encodedFile)
openFileForCurrentElement();
+ computeSize();
}
FormDataIODevice::~FormDataIODevice()
@@ -71,6 +75,20 @@ FormDataIODevice::~FormDataIODevice()
delete m_currentFile;
}
+qint64 FormDataIODevice::computeSize()
+{
+ for (int i = 0; i < m_formElements.size(); ++i) {
+ const FormDataElement& element = m_formElements[i];
+ if (element.m_type == FormDataElement::data)
+ m_dataSize += element.m_data.size();
+ else {
+ QFileInfo fi(element.m_filename);
+ m_fileSize += fi.size();
+ }
+ }
+ return m_dataSize + m_fileSize;
+}
+
void FormDataIODevice::moveToNextElement()
{
if (m_currentFile)
@@ -137,6 +155,29 @@ bool FormDataIODevice::isSequential() const
return true;
}
+String QNetworkReplyHandler::httpMethod() const
+{
+ switch (m_method) {
+ case QNetworkAccessManager::GetOperation:
+ return "GET";
+ case QNetworkAccessManager::HeadOperation:
+ return "HEAD";
+ case QNetworkAccessManager::PostOperation:
+ return "POST";
+ case QNetworkAccessManager::PutOperation:
+ return "PUT";
+ case QNetworkAccessManager::DeleteOperation:
+ return "DELETE";
+#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
+ case QNetworkAccessManager::CustomOperation:
+ return m_resourceHandle->firstRequest().httpMethod();
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ return "GET";
+ }
+}
+
QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode loadMode)
: QObject(0)
, m_reply(0)
@@ -164,11 +205,12 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode load
else if (r.httpMethod() == "DELETE")
m_method = QNetworkAccessManager::DeleteOperation;
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
- else if (r.httpMethod() == "OPTIONS")
+ else
m_method = QNetworkAccessManager::CustomOperation;
-#endif
+#else
else
m_method = QNetworkAccessManager::UnknownOperation;
+#endif
QObject* originatingObject = 0;
if (m_resourceHandle->getInternal()->m_context)
@@ -359,13 +401,17 @@ void QNetworkReplyHandler::sendResponseIfNeeded()
}
m_redirected = true;
- ResourceRequest newRequest = m_resourceHandle->firstRequest();
- newRequest.setURL(newUrl);
- if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && newRequest.httpMethod() == "POST") {
+ // Status Code 301 (Moved Permanently), 302 (Moved Temporarily), 303 (See Other):
+ // - If original request is POST convert to GET and redirect automatically
+ // Status Code 307 (Temporary Redirect) and all other redirect status codes:
+ // - Use the HTTP method from the previous request
+ if ((statusCode >= 301 && statusCode <= 303) && m_resourceHandle->firstRequest().httpMethod() == "POST")
m_method = QNetworkAccessManager::GetOperation;
- newRequest.setHTTPMethod("GET");
- }
+
+ ResourceRequest newRequest = m_resourceHandle->firstRequest();
+ newRequest.setHTTPMethod(httpMethod());
+ newRequest.setURL(newUrl);
// Should not set Referer after a redirect from a secure resource to non-secure one.
if (!newRequest.url().protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https"))
@@ -453,6 +499,9 @@ void QNetworkReplyHandler::start()
break;
case QNetworkAccessManager::PostOperation: {
FormDataIODevice* postDevice = new FormDataIODevice(d->m_firstRequest.httpBody());
+ // We may be uploading files so prevent QNR from buffering data
+ m_request.setHeader(QNetworkRequest::ContentLengthHeader, postDevice->getFormDataSize());
+ m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true));
m_reply = manager->post(m_request, postDevice);
postDevice->setParent(m_reply);
break;
@@ -462,6 +511,9 @@ void QNetworkReplyHandler::start()
break;
case QNetworkAccessManager::PutOperation: {
FormDataIODevice* putDevice = new FormDataIODevice(d->m_firstRequest.httpBody());
+ // We may be uploading files so prevent QNR from buffering data
+ m_request.setHeader(QNetworkRequest::ContentLengthHeader, putDevice->getFormDataSize());
+ m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true));
m_reply = manager->put(m_request, putDevice);
putDevice->setParent(m_reply);
break;
diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.h b/WebCore/platform/network/qt/QNetworkReplyHandler.h
index f6b2358..884a1a4 100644
--- a/WebCore/platform/network/qt/QNetworkReplyHandler.h
+++ b/WebCore/platform/network/qt/QNetworkReplyHandler.h
@@ -67,6 +67,7 @@ private slots:
private:
void start();
void resetState();
+ String httpMethod() const;
QNetworkReply* m_reply;
ResourceHandle* m_resourceHandle;
@@ -97,6 +98,7 @@ public:
~FormDataIODevice();
bool isSequential() const;
+ qint64 getFormDataSize() const { return m_fileSize + m_dataSize; }
protected:
qint64 readData(char*, qint64);
@@ -104,12 +106,15 @@ protected:
private:
void moveToNextElement();
+ qint64 computeSize();
void openFileForCurrentElement();
private:
Vector<FormDataElement> m_formElements;
QFile* m_currentFile;
qint64 m_currentDelta;
+ qint64 m_fileSize;
+ qint64 m_dataSize;
};
}
diff --git a/WebCore/platform/network/qt/ResourceRequest.h b/WebCore/platform/network/qt/ResourceRequest.h
index fb69326..0c7b62b 100644
--- a/WebCore/platform/network/qt/ResourceRequest.h
+++ b/WebCore/platform/network/qt/ResourceRequest.h
@@ -66,6 +66,12 @@ namespace WebCore {
void doUpdatePlatformRequest() {}
void doUpdateResourceRequest() {}
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+ };
+
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/qt/ResourceResponse.h b/WebCore/platform/network/qt/ResourceResponse.h
index 345ef25..288cf84 100644
--- a/WebCore/platform/network/qt/ResourceResponse.h
+++ b/WebCore/platform/network/qt/ResourceResponse.h
@@ -40,6 +40,15 @@ public:
: ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename)
{
}
+
+private:
+ friend class ResourceResponseBase;
+
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+};
+
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/qt/SocketStreamHandle.h b/WebCore/platform/network/qt/SocketStreamHandle.h
index 5c55749..e725344 100644
--- a/WebCore/platform/network/qt/SocketStreamHandle.h
+++ b/WebCore/platform/network/qt/SocketStreamHandle.h
@@ -38,6 +38,10 @@
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
+#if !PLATFORM(QT)
+#error This should only be built on Qt
+#endif
+
namespace WebCore {
class AuthenticationChallenge;
diff --git a/WebCore/platform/network/qt/SocketStreamHandlePrivate.h b/WebCore/platform/network/qt/SocketStreamHandlePrivate.h
index 235f1b1..d074f42 100644
--- a/WebCore/platform/network/qt/SocketStreamHandlePrivate.h
+++ b/WebCore/platform/network/qt/SocketStreamHandlePrivate.h
@@ -54,7 +54,7 @@ public slots:
void socketReadyRead();
int send(const char* data, int len);
void close();
- void socketSentdata();
+ void socketSentData();
void socketClosed();
void socketError(QAbstractSocket::SocketError);
void socketClosedCallback();
diff --git a/WebCore/platform/network/qt/SocketStreamHandleQt.cpp b/WebCore/platform/network/qt/SocketStreamHandleQt.cpp
index cc508b6..10550ae 100644
--- a/WebCore/platform/network/qt/SocketStreamHandleQt.cpp
+++ b/WebCore/platform/network/qt/SocketStreamHandleQt.cpp
@@ -110,7 +110,7 @@ void SocketStreamHandlePrivate::close()
m_socket->close();
}
-void SocketStreamHandlePrivate::socketSentdata()
+void SocketStreamHandlePrivate::socketSentData()
{
if (m_streamHandle)
m_streamHandle->sendPendingData();
diff --git a/WebCore/platform/network/soup/ProxyServerSoup.cpp b/WebCore/platform/network/soup/ProxyServerSoup.cpp
new file mode 100644
index 0000000..50b32de
--- /dev/null
+++ b/WebCore/platform/network/soup/ProxyServerSoup.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org>. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "ProxyServer.h"
+
+#include "KURL.h"
+
+namespace WebCore {
+
+Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*)
+{
+ // FIXME: Implement.
+ return Vector<ProxyServer>();
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp
index 0d84388..05c659f 100644
--- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp
+++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp
@@ -45,8 +45,8 @@
#include "ResourceHandleInternal.h"
#include "ResourceResponse.h"
#include "SharedBuffer.h"
+#include "soup-request-http.h"
#include "TextEncoding.h"
-
#include <errno.h>
#include <fcntl.h>
#include <gio/gio.h>
@@ -58,6 +58,8 @@
namespace WebCore {
+#define READ_BUFFER_SIZE 8192
+
class WebCoreSynchronousLoader : public ResourceHandleClient, public Noncopyable {
public:
WebCoreSynchronousLoader(ResourceError&, ResourceResponse &, Vector<char>&);
@@ -120,16 +122,16 @@ void WebCoreSynchronousLoader::run()
g_main_loop_run(m_mainLoop);
}
-static void cleanupGioOperation(ResourceHandle* handle, bool isDestroying);
-static bool startData(ResourceHandle* handle, String urlString);
-static bool startGio(ResourceHandle* handle, KURL url);
+static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying);
+static void sendRequestCallback(GObject*, GAsyncResult*, gpointer);
+static void readCallback(GObject*, GAsyncResult*, gpointer);
+static void closeCallback(GObject*, GAsyncResult*, gpointer);
+static bool startGio(ResourceHandle*, KURL);
ResourceHandleInternal::~ResourceHandleInternal()
{
- if (m_msg) {
- g_object_unref(m_msg);
- m_msg = 0;
- }
+ if (m_soupRequest)
+ g_object_set_data(G_OBJECT(m_soupRequest.get()), "webkit-resource", 0);
if (m_idleHandler) {
g_source_remove(m_idleHandler);
@@ -139,11 +141,7 @@ ResourceHandleInternal::~ResourceHandleInternal()
ResourceHandle::~ResourceHandle()
{
- if (d->m_msg)
- g_signal_handlers_disconnect_matched(d->m_msg, G_SIGNAL_MATCH_DATA,
- 0, 0, 0, 0, this);
-
- cleanupGioOperation(this, true);
+ cleanupSoupRequestOperation(this, true);
}
void ResourceHandle::prepareForURL(const KURL &url)
@@ -185,9 +183,8 @@ static void restartedCallback(SoupMessage* msg, gpointer data)
if (d->m_cancelled)
return;
- char* uri = soup_uri_to_string(soup_message_get_uri(msg), false);
- String location = String(uri);
- g_free(uri);
+ GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(msg), false));
+ String location = String::fromUTF8(uri.get());
KURL newURL = KURL(handle->firstRequest().url(), location);
ResourceRequest request = handle->firstRequest();
@@ -213,7 +210,7 @@ static void restartedCallback(SoupMessage* msg, gpointer data)
String firstPartyString = request.firstPartyForCookies().string();
if (!firstPartyString.isEmpty()) {
GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data()));
- soup_message_set_first_party(d->m_msg, firstParty.get());
+ soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get());
}
#endif
}
@@ -221,8 +218,10 @@ static void restartedCallback(SoupMessage* msg, gpointer data)
static void gotHeadersCallback(SoupMessage* msg, gpointer data)
{
// For 401, we will accumulate the resource body, and only use it
- // in case authentication with the soup feature doesn't happen
- if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
+ // in case authentication with the soup feature doesn't happen.
+ // For 302 we accumulate the body too because it could be used by
+ // some servers to redirect with a clunky http-equiv=REFRESH
+ if (statusWillBeHandledBySoup(msg->status_code)) {
soup_message_body_set_accumulate(msg->response_body, TRUE);
return;
}
@@ -295,51 +294,6 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data)
client->didReceiveData(handle.get(), chunk->data, chunk->length, false);
}
-// Called at the end of the message, with all the necessary about the last informations.
-// Doesn't get called for redirects.
-static void finishedCallback(SoupSession *session, SoupMessage* msg, gpointer data)
-{
- RefPtr<ResourceHandle> handle = adoptRef(static_cast<ResourceHandle*>(data));
- // TODO: maybe we should run this code even if there's no client?
- if (!handle)
- return;
-
- ResourceHandleInternal* d = handle->getInternal();
-
- ResourceHandleClient* client = handle->client();
- if (!client)
- return;
-
- if (d->m_cancelled)
- return;
-
- if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) {
- char* uri = soup_uri_to_string(soup_message_get_uri(msg), false);
- ResourceError error(g_quark_to_string(SOUP_HTTP_ERROR),
- msg->status_code,
- uri,
- String::fromUTF8(msg->reason_phrase));
- g_free(uri);
- client->didFail(handle.get(), error);
- return;
- }
-
- if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
- fillResponseFromMessage(msg, &d->m_response);
- client->didReceiveResponse(handle.get(), d->m_response);
-
- // WebCore might have cancelled the job in the while
- if (d->m_cancelled)
- return;
-
- if (msg->response_body->data)
- client->didReceiveData(handle.get(), msg->response_body->data, msg->response_body->length, true);
- }
-
- client->didFinishLoading(handle.get(), 0);
-}
-
-// parseDataUrl() is taken from the CURL http backend.
static gboolean parseDataUrl(gpointer callbackData)
{
ResourceHandle* handle = static_cast<ResourceHandle*>(callbackData);
@@ -364,7 +318,6 @@ static gboolean parseDataUrl(gpointer callbackData)
}
String mediaType = url.substring(5, index - 5);
- String data = url.substring(index + 1);
bool isBase64 = mediaType.endsWith(";base64", false);
if (isBase64)
@@ -380,42 +333,43 @@ static gboolean parseDataUrl(gpointer callbackData)
response.setURL(handle->firstRequest().url());
response.setMimeType(mimeType);
- if (isBase64) {
- data = decodeURLEscapeSequences(data);
- response.setTextEncodingName(charset);
- client->didReceiveResponse(handle, response);
-
- // The load may be cancelled, and the client may be destroyed
- // by any of the client reporting calls, so we check, and bail
- // out in either of those cases.
- if (d->m_cancelled || !handle->client())
- return false;
-
- // Use the GLib Base64, since WebCore's decoder isn't
- // general-purpose and fails on Acid3 test 97 (whitespace).
- size_t outLength = 0;
- char* outData = 0;
- outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength));
- if (outData && outLength > 0)
- client->didReceiveData(handle, outData, outLength, 0);
- g_free(outData);
- } else {
- // We have to convert to UTF-16 early due to limitations in KURL
- data = decodeURLEscapeSequences(data, TextEncoding(charset));
- response.setTextEncodingName("UTF-16");
- client->didReceiveResponse(handle, response);
-
- if (d->m_cancelled || !handle->client())
- return false;
-
- if (data.length() > 0)
- client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0);
- }
+ // For non base64 encoded data we have to convert to UTF-16 early
+ // due to limitations in KURL
+ response.setTextEncodingName(isBase64 ? charset : "UTF-16");
+ client->didReceiveResponse(handle, response);
+ // The load may be cancelled, and the client may be destroyed
+ // by any of the client reporting calls, so we check, and bail
+ // out in either of those cases.
if (d->m_cancelled || !handle->client())
return false;
- client->didFinishLoading(handle, 0);
+ SoupSession* session = handle->defaultSession();
+ GOwnPtr<GError> error;
+ d->m_soupRequest = adoptPlatformRef(webkit_soup_requester_request(d->m_requester.get(), handle->firstRequest().url().string().utf8().data(), session, &error.outPtr()));
+ if (error) {
+ d->m_soupRequest = 0;
+ client->didFinishLoading(handle, 0);
+ return false;
+ }
+
+ d->m_inputStream = adoptPlatformRef(webkit_soup_request_send(d->m_soupRequest.get(), 0, &error.outPtr()));
+ if (error) {
+ d->m_inputStream = 0;
+ client->didFinishLoading(handle, 0);
+ return false;
+ }
+
+ d->m_buffer = static_cast<char*>(g_slice_alloc0(READ_BUFFER_SIZE));
+ d->m_total = 0;
+
+ g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle);
+ // balanced by a deref() in cleanupSoupRequestOperation, which should always run
+ handle->ref();
+
+ d->m_cancellable = adoptPlatformRef(g_cancellable_new());
+ g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT,
+ d->m_cancellable.get(), readCallback, GINT_TO_POINTER(!isBase64));
return false;
}
@@ -469,6 +423,132 @@ static void ensureSessionIsInitialized(SoupSession* session)
g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef));
}
+static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying = false)
+{
+ ResourceHandleInternal* d = handle->getInternal();
+
+ if (d->m_soupRequest) {
+ g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", 0);
+ d->m_soupRequest.clear();
+ }
+
+ if (d->m_inputStream) {
+ g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", 0);
+ d->m_inputStream.clear();
+ }
+
+ d->m_cancellable.clear();
+
+ if (d->m_soupMessage) {
+ g_signal_handlers_disconnect_matched(d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA,
+ 0, 0, 0, 0, handle);
+ d->m_soupMessage.clear();
+ }
+
+ if (d->m_buffer) {
+ g_slice_free1(READ_BUFFER_SIZE, d->m_buffer);
+ d->m_buffer = 0;
+ }
+
+ if (!isDestroying)
+ handle->deref();
+}
+
+static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer userData)
+{
+ RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource"));
+ if (!handle)
+ return;
+
+ ResourceHandleInternal* d = handle->getInternal();
+ ResourceHandleClient* client = handle->client();
+
+ if (d->m_gotChunkHandler) {
+ // No need to call gotChunkHandler anymore. Received data will
+ // be reported by readCallback
+ if (g_signal_handler_is_connected(d->m_soupMessage.get(), d->m_gotChunkHandler))
+ g_signal_handler_disconnect(d->m_soupMessage.get(), d->m_gotChunkHandler);
+ }
+
+ if (d->m_cancelled || !client) {
+ cleanupSoupRequestOperation(handle.get());
+ return;
+ }
+
+ GOwnPtr<GError> error;
+ GInputStream* in = webkit_soup_request_send_finish(d->m_soupRequest.get(), res, &error.outPtr());
+
+ if (error) {
+ SoupMessage* soupMsg = d->m_soupMessage.get();
+ gboolean isTransportError = d->m_soupMessage && SOUP_STATUS_IS_TRANSPORT_ERROR(soupMsg->status_code);
+
+ if (isTransportError || (error->domain == G_IO_ERROR)) {
+ SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get());
+ GOwnPtr<char> uriStr(soup_uri_to_string(uri, false));
+ gint errorCode = isTransportError ? soupMsg->status_code : error->code;
+ const gchar* errorMsg = isTransportError ? soupMsg->reason_phrase : error->message;
+ const gchar* quarkStr = isTransportError ? g_quark_to_string(SOUP_HTTP_ERROR) : g_quark_to_string(G_IO_ERROR);
+ ResourceError resourceError(quarkStr, errorCode, uriStr.get(), String::fromUTF8(errorMsg));
+
+ cleanupSoupRequestOperation(handle.get());
+ client->didFail(handle.get(), resourceError);
+ return;
+ }
+
+ if (d->m_soupMessage && statusWillBeHandledBySoup(d->m_soupMessage->status_code)) {
+ fillResponseFromMessage(soupMsg, &d->m_response);
+ client->didReceiveResponse(handle.get(), d->m_response);
+
+ // WebCore might have cancelled the job in the while
+ if (!d->m_cancelled && soupMsg->response_body->data)
+ client->didReceiveData(handle.get(), soupMsg->response_body->data, soupMsg->response_body->length, true);
+ }
+
+ // didReceiveData above might have cancelled it
+ if (d->m_cancelled || !client) {
+ cleanupSoupRequestOperation(handle.get());
+ return;
+ }
+
+ client->didFinishLoading(handle.get(), 0);
+ return;
+ }
+
+ if (d->m_cancelled) {
+ cleanupSoupRequestOperation(handle.get());
+ return;
+ }
+
+ d->m_inputStream = adoptPlatformRef(in);
+ d->m_buffer = static_cast<char*>(g_slice_alloc0(READ_BUFFER_SIZE));
+ d->m_total = 0;
+
+ // readCallback needs it
+ g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle.get());
+
+ // We need to check if it's a file: URL and if it is a regular
+ // file as it could be a directory. In that case Soup properly
+ // returns a stream whose content is a HTML with a list of files
+ // in the directory
+ if (equalIgnoringCase(handle->firstRequest().url().protocol(), "file")
+ && G_IS_FILE_INPUT_STREAM(in)) {
+ ResourceResponse response;
+
+ response.setURL(handle->firstRequest().url());
+ response.setMimeType(webkit_soup_request_get_content_type(d->m_soupRequest.get()));
+ response.setExpectedContentLength(webkit_soup_request_get_content_length(d->m_soupRequest.get()));
+ client->didReceiveResponse(handle.get(), response);
+
+ if (d->m_cancelled) {
+ cleanupSoupRequestOperation(handle.get());
+ return;
+ }
+ }
+
+ g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0);
+}
+
static bool startHttp(ResourceHandle* handle)
{
ASSERT(handle);
@@ -483,26 +563,37 @@ static bool startHttp(ResourceHandle* handle)
url.removeFragmentIdentifier();
request.setURL(url);
- d->m_msg = request.toSoupMessage();
- if (!d->m_msg)
+ GOwnPtr<GError> error;
+ d->m_soupRequest = adoptPlatformRef(webkit_soup_requester_request(d->m_requester.get(), url.string().utf8().data(), session, &error.outPtr()));
+ if (error) {
+ d->m_soupRequest = 0;
return false;
+ }
+
+ g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle);
+
+ d->m_soupMessage = adoptPlatformRef(webkit_soup_request_http_get_message(WEBKIT_SOUP_REQUEST_HTTP(d->m_soupRequest.get())));
+ if (!d->m_soupMessage)
+ return false;
+
+ SoupMessage* soupMessage = d->m_soupMessage.get();
+ request.updateSoupMessage(soupMessage);
if (!handle->shouldContentSniff())
- soup_message_disable_feature(d->m_msg, SOUP_TYPE_CONTENT_SNIFFER);
+ soup_message_disable_feature(soupMessage, SOUP_TYPE_CONTENT_SNIFFER);
- g_signal_connect(d->m_msg, "restarted", G_CALLBACK(restartedCallback), handle);
- g_signal_connect(d->m_msg, "got-headers", G_CALLBACK(gotHeadersCallback), handle);
- g_signal_connect(d->m_msg, "content-sniffed", G_CALLBACK(contentSniffedCallback), handle);
- g_signal_connect(d->m_msg, "got-chunk", G_CALLBACK(gotChunkCallback), handle);
+ g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle);
+ g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), handle);
+ g_signal_connect(soupMessage, "content-sniffed", G_CALLBACK(contentSniffedCallback), handle);
+ d->m_gotChunkHandler = g_signal_connect(soupMessage, "got-chunk", G_CALLBACK(gotChunkCallback), handle);
#ifdef HAVE_LIBSOUP_2_29_90
String firstPartyString = request.firstPartyForCookies().string();
if (!firstPartyString.isEmpty()) {
GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data()));
- soup_message_set_first_party(d->m_msg, firstParty.get());
+ soup_message_set_first_party(soupMessage, firstParty.get());
}
#endif
- g_object_set_data(G_OBJECT(d->m_msg), "resourceHandle", reinterpret_cast<void*>(handle));
FormData* httpBody = d->m_firstRequest.httpBody();
if (httpBody && !httpBody->isEmpty()) {
@@ -512,7 +603,7 @@ static bool startHttp(ResourceHandle* handle)
if (numElements < 2) {
Vector<char> body;
httpBody->flatten(body);
- soup_message_set_request(d->m_msg, d->m_firstRequest.httpContentType().utf8().data(),
+ soup_message_set_request(soupMessage, d->m_firstRequest.httpContentType().utf8().data(),
SOUP_MEMORY_COPY, body.data(), body.size());
} else {
/*
@@ -521,27 +612,25 @@ static bool startHttp(ResourceHandle* handle)
* copying into memory; TODO: support upload of non-local
* (think sftp://) files by using GIO?
*/
- soup_message_body_set_accumulate(d->m_msg->request_body, FALSE);
+ soup_message_body_set_accumulate(soupMessage->request_body, FALSE);
for (size_t i = 0; i < numElements; i++) {
const FormDataElement& element = httpBody->elements()[i];
if (element.m_type == FormDataElement::data)
- soup_message_body_append(d->m_msg->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size());
+ soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size());
else {
/*
* mapping for uploaded files code inspired by technique used in
* libsoup's simple-httpd test
*/
- GError* error = 0;
+ GOwnPtr<GError> error;
CString fileName = fileSystemRepresentation(element.m_filename);
- GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error);
+ GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr());
if (error) {
- g_error_free(error);
- g_signal_handlers_disconnect_matched(d->m_msg, G_SIGNAL_MATCH_DATA,
+ g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA,
0, 0, 0, 0, handle);
- g_object_unref(d->m_msg);
- d->m_msg = 0;
+ d->m_soupMessage.clear();
return false;
}
@@ -549,38 +638,31 @@ static bool startHttp(ResourceHandle* handle)
SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping),
g_mapped_file_get_length(fileMapping),
fileMapping,
-#if GLIB_CHECK_VERSION(2, 21, 3)
reinterpret_cast<GDestroyNotify>(g_mapped_file_unref));
-#else
- reinterpret_cast<GDestroyNotify>(g_mapped_file_free));
-#endif
- soup_message_body_append_buffer(d->m_msg->request_body, soupBuffer);
+ soup_message_body_append_buffer(soupMessage->request_body, soupBuffer);
soup_buffer_free(soupBuffer);
}
}
}
}
- // balanced by a deref() in finishedCallback, which should always run
+ // balanced by a deref() in cleanupSoupRequestOperation, which should always run
handle->ref();
// Make sure we have an Accept header for subresources; some sites
// want this to serve some of their subresources
- if (!soup_message_headers_get_one(d->m_msg->request_headers, "Accept"))
- soup_message_headers_append(d->m_msg->request_headers, "Accept", "*/*");
+ if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept"))
+ soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*");
- // Balanced in ResourceHandleInternal's destructor; we need to
- // keep our own ref, because after queueing the message, the
- // session owns the initial reference.
- g_object_ref(d->m_msg);
- soup_session_queue_message(session, d->m_msg, finishedCallback, handle);
+ d->m_cancellable = adoptPlatformRef(g_cancellable_new());
+ webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0);
return true;
}
bool ResourceHandle::start(NetworkingContext* context)
{
- ASSERT(!d->m_msg);
+ ASSERT(!d->m_soupMessage);
// The frame could be null if the ResourceHandle is not associated to any
// Frame, e.g. if we are downloading a file.
@@ -620,10 +702,10 @@ bool ResourceHandle::start(NetworkingContext* context)
void ResourceHandle::cancel()
{
d->m_cancelled = true;
- if (d->m_msg)
- soup_session_cancel_message(defaultSession(), d->m_msg, SOUP_STATUS_CANCELLED);
+ if (d->m_soupMessage)
+ soup_session_cancel_message(defaultSession(), d->m_soupMessage.get(), SOUP_STATUS_CANCELLED);
else if (d->m_cancellable)
- g_cancellable_cancel(d->m_cancellable);
+ g_cancellable_cancel(d->m_cancellable.get());
}
PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
@@ -667,38 +749,6 @@ void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const
syncLoader.run();
}
-// GIO-based loader
-
-static void cleanupGioOperation(ResourceHandle* handle, bool isDestroying = false)
-{
- ResourceHandleInternal* d = handle->getInternal();
-
- if (d->m_gfile) {
- g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", 0);
- g_object_unref(d->m_gfile);
- d->m_gfile = 0;
- }
-
- if (d->m_cancellable) {
- g_object_unref(d->m_cancellable);
- d->m_cancellable = 0;
- }
-
- if (d->m_inputStream) {
- g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", 0);
- g_object_unref(d->m_inputStream);
- d->m_inputStream = 0;
- }
-
- if (d->m_buffer) {
- g_free(d->m_buffer);
- d->m_buffer = 0;
- }
-
- if (!isDestroying)
- handle->deref();
-}
-
static void closeCallback(GObject* source, GAsyncResult* res, gpointer)
{
RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource"));
@@ -708,8 +758,8 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer)
ResourceHandleInternal* d = handle->getInternal();
ResourceHandleClient* client = handle->client();
- g_input_stream_close_finish(d->m_inputStream, res, 0);
- cleanupGioOperation(handle.get());
+ g_input_stream_close_finish(d->m_inputStream.get(), res, 0);
+ cleanupSoupRequestOperation(handle.get());
// The load may have been cancelled, the client may have been
// destroyed already. In such cases calling didFinishLoading is a
@@ -720,210 +770,92 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer)
client->didFinishLoading(handle.get(), 0);
}
-static void readCallback(GObject* source, GAsyncResult* res, gpointer)
+static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer data)
{
RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource"));
if (!handle)
return;
+ bool convertToUTF16 = static_cast<bool>(data);
ResourceHandleInternal* d = handle->getInternal();
ResourceHandleClient* client = handle->client();
if (d->m_cancelled || !client) {
- cleanupGioOperation(handle.get());
+ cleanupSoupRequestOperation(handle.get());
return;
}
- GError* error = 0;
+ GOwnPtr<GError> error;
- gssize bytesRead = g_input_stream_read_finish(d->m_inputStream, res, &error);
+ gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
if (error) {
- char* uri = g_file_get_uri(d->m_gfile);
- ResourceError resourceError(g_quark_to_string(G_IO_ERROR),
- error->code,
- uri,
+ SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get());
+ GOwnPtr<char> uriStr(soup_uri_to_string(uri, false));
+ ResourceError resourceError(g_quark_to_string(G_IO_ERROR), error->code, uriStr.get(),
error ? String::fromUTF8(error->message) : String());
- g_free(uri);
- g_error_free(error);
- cleanupGioOperation(handle.get());
+ cleanupSoupRequestOperation(handle.get());
client->didFail(handle.get(), resourceError);
return;
}
if (!bytesRead) {
- g_input_stream_close_async(d->m_inputStream, G_PRIORITY_DEFAULT,
+ g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT,
0, closeCallback, 0);
return;
}
d->m_total += bytesRead;
- client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total);
-
- // didReceiveData may cancel the load, which may release the last reference.
- if (d->m_cancelled) {
- cleanupGioOperation(handle.get());
- return;
+ if (G_LIKELY(!convertToUTF16))
+ client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total);
+ else {
+ // We have to convert it to UTF-16 due to limitations in KURL
+ String data = String::fromUTF8(d->m_buffer, bytesRead);
+ client->didReceiveData(handle.get(), reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0);
}
- g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize,
- G_PRIORITY_DEFAULT, d->m_cancellable,
- readCallback, 0);
-}
-
-static void openCallback(GObject* source, GAsyncResult* res, gpointer)
-{
- RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource"));
- if (!handle)
- return;
-
- ResourceHandleInternal* d = handle->getInternal();
- ResourceHandleClient* client = handle->client();
-
+ // didReceiveData may cancel the load, which may release the last reference.
if (d->m_cancelled || !client) {
- cleanupGioOperation(handle.get());
- return;
- }
-
- GError* error = 0;
- GFileInputStream* in = g_file_read_finish(G_FILE(source), res, &error);
- if (error) {
- char* uri = g_file_get_uri(d->m_gfile);
- ResourceError resourceError(g_quark_to_string(G_IO_ERROR),
- error->code,
- uri,
- error ? String::fromUTF8(error->message) : String());
- g_free(uri);
- g_error_free(error);
- cleanupGioOperation(handle.get());
- client->didFail(handle.get(), resourceError);
+ cleanupSoupRequestOperation(handle.get());
return;
}
- d->m_inputStream = G_INPUT_STREAM(in);
- d->m_bufferSize = 8192;
- d->m_buffer = static_cast<char*>(g_malloc(d->m_bufferSize));
- d->m_total = 0;
-
- g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", handle.get());
- g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize,
- G_PRIORITY_DEFAULT, d->m_cancellable,
- readCallback, 0);
+ g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT,
+ d->m_cancellable.get(), readCallback, data);
}
-static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer)
-{
- RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource"));
- if (!handle)
- return;
-
- ResourceHandleInternal* d = handle->getInternal();
- ResourceHandleClient* client = handle->client();
-
- if (d->m_cancelled) {
- cleanupGioOperation(handle.get());
- return;
- }
-
- ResourceResponse response;
-
- char* uri = g_file_get_uri(d->m_gfile);
- response.setURL(KURL(KURL(), uri));
- g_free(uri);
-
- GError* error = 0;
- GFileInfo* info = g_file_query_info_finish(d->m_gfile, res, &error);
-
- if (error) {
- // FIXME: to be able to handle ftp URIs properly, we must
- // check if the error is G_IO_ERROR_NOT_MOUNTED, and if so,
- // call g_file_mount_enclosing_volume() to mount the ftp
- // server (and then keep track of the fact that we mounted it,
- // and set a timeout to unmount it later after it's been idle
- // for a while).
- char* uri = g_file_get_uri(d->m_gfile);
- ResourceError resourceError(g_quark_to_string(G_IO_ERROR),
- error->code,
- uri,
- error ? String::fromUTF8(error->message) : String());
- g_free(uri);
- g_error_free(error);
- cleanupGioOperation(handle.get());
- client->didFail(handle.get(), resourceError);
- return;
- }
-
- if (g_file_info_get_file_type(info) != G_FILE_TYPE_REGULAR) {
- // FIXME: what if the URI points to a directory? Should we
- // generate a listing? How? What do other backends do here?
- char* uri = g_file_get_uri(d->m_gfile);
- ResourceError resourceError(g_quark_to_string(G_IO_ERROR),
- G_IO_ERROR_FAILED,
- uri,
- String());
- g_free(uri);
- cleanupGioOperation(handle.get());
- client->didFail(handle.get(), resourceError);
- return;
- }
-
- // According to http://library.gnome.org/devel/gio/stable/gio-GContentType.html
- // GContentType on Unix is the mime type, but not on Win32.
- GOwnPtr<gchar> mimeType(g_content_type_get_mime_type(g_file_info_get_content_type(info)));
- response.setMimeType(mimeType.get());
- response.setExpectedContentLength(g_file_info_get_size(info));
-
- GTimeVal tv;
- g_file_info_get_modification_time(info, &tv);
- response.setLastModifiedDate(tv.tv_sec);
-
- client->didReceiveResponse(handle.get(), response);
-
- if (d->m_cancelled) {
- cleanupGioOperation(handle.get());
- return;
- }
-
- g_file_read_async(d->m_gfile, G_PRIORITY_DEFAULT, d->m_cancellable,
- openCallback, 0);
-}
static bool startGio(ResourceHandle* handle, KURL url)
{
ASSERT(handle);
- ResourceHandleInternal* d = handle->getInternal();
-
if (handle->firstRequest().httpMethod() != "GET" && handle->firstRequest().httpMethod() != "POST")
return false;
+ SoupSession* session = handle->defaultSession();
+ ResourceHandleInternal* d = handle->getInternal();
+
// GIO doesn't know how to handle refs and queries, so remove them
// TODO: use KURL.fileSystemPath after KURLGtk and FileSystemGtk are
// using GIO internally, and providing URIs instead of file paths
url.removeFragmentIdentifier();
url.setQuery(String());
url.removePort();
+ CString urlStr = url.string().utf8();
-#if !OS(WINDOWS)
- // we avoid the escaping for local files, because
- // g_filename_from_uri (used internally by GFile) has problems
- // decoding strings with arbitrary percent signs
- if (url.isLocalFile())
- d->m_gfile = g_file_new_for_path(url.prettyURL().utf8().data() + sizeof("file://") - 1);
- else
-#endif
- d->m_gfile = g_file_new_for_uri(url.string().utf8().data());
- g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", handle);
+ GOwnPtr<GError> error;
+ d->m_soupRequest = adoptPlatformRef(webkit_soup_requester_request(d->m_requester.get(), urlStr.data(), session, &error.outPtr()));
+ if (error) {
+ d->m_soupRequest = 0;
+ return false;
+ }
+
+ g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle);
- // balanced by a deref() in cleanupGioOperation, which should always run
+ // balanced by a deref() in cleanupSoupRequestOperation, which should always run
handle->ref();
- d->m_cancellable = g_cancellable_new();
- g_file_query_info_async(d->m_gfile,
- G_FILE_ATTRIBUTE_STANDARD_TYPE ","
- G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
- G_FILE_ATTRIBUTE_STANDARD_SIZE,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT, d->m_cancellable,
- queryInfoCallback, 0);
+ d->m_cancellable = adoptPlatformRef(g_cancellable_new());
+ webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0);
+
return true;
}
diff --git a/WebCore/platform/network/soup/ResourceRequest.h b/WebCore/platform/network/soup/ResourceRequest.h
index a1d916f..879a47f 100644
--- a/WebCore/platform/network/soup/ResourceRequest.h
+++ b/WebCore/platform/network/soup/ResourceRequest.h
@@ -67,8 +67,9 @@ namespace WebCore {
updateFromSoupMessage(soupMessage);
}
+ void updateSoupMessage(SoupMessage*) const;
SoupMessage* toSoupMessage() const;
- void updateFromSoupMessage(SoupMessage* soupMessage);
+ void updateFromSoupMessage(SoupMessage*);
SoupMessageFlags soupMessageFlags() const { return m_soupFlags; }
void setSoupMessageFlags(SoupMessageFlags soupFlags) { m_soupFlags = soupFlags; }
@@ -80,6 +81,12 @@ namespace WebCore {
void doUpdatePlatformRequest() {};
void doUpdateResourceRequest() {};
+
+ PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { }
+ };
+
+ struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/WebCore/platform/network/soup/ResourceRequestSoup.cpp
index 62deb01..380fc84 100644
--- a/WebCore/platform/network/soup/ResourceRequestSoup.cpp
+++ b/WebCore/platform/network/soup/ResourceRequestSoup.cpp
@@ -33,6 +33,29 @@ using namespace std;
namespace WebCore {
+void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const
+{
+ g_object_set(soupMessage, SOUP_MESSAGE_METHOD, httpMethod().utf8().data(), NULL);
+
+ const HTTPHeaderMap& headers = httpHeaderFields();
+ SoupMessageHeaders* soupHeaders = soupMessage->request_headers;
+ if (!headers.isEmpty()) {
+ HTTPHeaderMap::const_iterator end = headers.end();
+ for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
+ soup_message_headers_append(soupHeaders, it->first.string().utf8().data(), it->second.utf8().data());
+ }
+
+#ifdef HAVE_LIBSOUP_2_29_90
+ String firstPartyString = firstPartyForCookies().string();
+ if (!firstPartyString.isEmpty()) {
+ GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data()));
+ soup_message_set_first_party(soupMessage, firstParty.get());
+ }
+#endif
+
+ soup_message_set_flags(soupMessage, m_soupFlags);
+}
+
SoupMessage* ResourceRequest::toSoupMessage() const
{
SoupMessage* soupMessage = soup_message_new(httpMethod().utf8().data(), url().string().utf8().data());
diff --git a/WebCore/platform/network/soup/ResourceResponse.h b/WebCore/platform/network/soup/ResourceResponse.h
index e6d872c..e7213f5 100644
--- a/WebCore/platform/network/soup/ResourceResponse.h
+++ b/WebCore/platform/network/soup/ResourceResponse.h
@@ -63,9 +63,13 @@ private:
SoupMessageFlags m_soupFlags;
- void doUpdateResourceResponse()
- {
- }
+ void doUpdateResourceResponse() { }
+
+ PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; }
+ void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { }
+};
+
+struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase {
};
} // namespace WebCore
diff --git a/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/WebCore/platform/network/soup/cache/soup-directory-input-stream.c
new file mode 100644
index 0000000..c14863b
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-directory-input-stream.c
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-directory-input-stream.h"
+
+#include <libsoup/soup.h>
+#include <stdio.h>
+#include <string.h>
+
+#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>"
+#define EXIT_STRING "</table></html>"
+
+G_DEFINE_TYPE (WebKitSoupDirectoryInputStream, webkit_soup_directory_input_stream, G_TYPE_INPUT_STREAM)
+
+static SoupBuffer *
+webkit_soup_directory_input_stream_parse_info (WebKitSoupDirectoryInputStream * stream,
+ GFileInfo * info)
+{
+ SoupBuffer *buffer;
+ GString *string;
+ const char *s;
+ char *escaped, *path, *xml_string;
+
+ if (!g_file_info_get_name (info))
+ return NULL;
+
+ s = g_file_info_get_display_name (info);
+ if (!s) {
+ s = g_file_info_get_name (info);
+ /* FIXME: convert somehow? */
+ if (!g_utf8_validate (s, -1, NULL))
+ return NULL;
+ }
+ string = g_string_new ("<tr>");
+
+ xml_string = g_markup_escape_text (s, -1);
+ escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE);
+ path = g_strconcat (stream->uri, "/", escaped, NULL);
+ g_free (escaped);
+ g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string);
+ g_free (path);
+ g_free (xml_string);
+ g_string_append (string, "</tr>");
+
+ buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
+ g_string_free (string, FALSE);
+
+ return buffer;
+}
+
+static SoupBuffer *
+webkit_soup_directory_input_stream_read_next_file (WebKitSoupDirectoryInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileInfo *info;
+ SoupBuffer *buffer;
+ GError *err = NULL;
+
+ do {
+ info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err);
+ if (info == NULL) {
+ if (err) {
+ g_propagate_error (error, err);
+ return NULL;
+ } else if (!stream->done) {
+ stream->done = TRUE;
+ return soup_buffer_new (SOUP_MEMORY_STATIC,
+ EXIT_STRING,
+ sizeof (EXIT_STRING));
+ } else {
+ return NULL;
+ }
+ }
+
+ buffer = webkit_soup_directory_input_stream_parse_info (stream, info);
+ } while (buffer == NULL);
+
+ return buffer;
+}
+
+static gssize
+webkit_soup_directory_input_stream_read (GInputStream *input,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input);
+ gsize total, size;
+
+ for (total = 0; total < count; total += size) {
+ if (stream->buffer == NULL) {
+ stream->buffer = webkit_soup_directory_input_stream_read_next_file (stream, cancellable, error);
+ if (stream->buffer == NULL) {
+ /* FIXME: Is this correct or should we forward the error? */
+ if (total)
+ g_clear_error (error);
+ return total;
+ }
+ }
+
+ size = MIN (stream->buffer->length, count - total);
+ memcpy ((char *)buffer + total, stream->buffer->data, size);
+ if (size == stream->buffer->length) {
+ soup_buffer_free (stream->buffer);
+ stream->buffer = NULL;
+ } else {
+ SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer,
+ size,
+ stream->buffer->length - size);
+ soup_buffer_free (stream->buffer);
+ stream->buffer = sub;
+ }
+ }
+
+ return total;
+}
+
+static gboolean
+webkit_soup_directory_input_stream_close (GInputStream *input,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input);
+ gboolean result;
+
+ if (stream->buffer) {
+ soup_buffer_free (stream->buffer);
+ stream->buffer = NULL;
+ }
+
+ result = g_file_enumerator_close (stream->enumerator,
+ cancellable,
+ error);
+ g_object_unref (stream->enumerator);
+ stream->enumerator = NULL;
+
+ g_free (stream->uri);
+ stream->uri = NULL;
+
+ return result;
+}
+
+static void
+webkit_soup_directory_input_stream_class_init (WebKitSoupDirectoryInputStreamClass *stream_class)
+{
+ GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class);
+
+ inputstream_class->read_fn = webkit_soup_directory_input_stream_read;
+ inputstream_class->close_fn = webkit_soup_directory_input_stream_close;
+}
+
+static void
+webkit_soup_directory_input_stream_init (WebKitSoupDirectoryInputStream *stream)
+{
+ stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
+ INIT_STRING,
+ sizeof (INIT_STRING));
+}
+
+GInputStream *
+webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator,
+ SoupURI *uri)
+{
+ GInputStream *stream;
+
+ g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ stream = g_object_new (WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, NULL);
+
+ WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
+ WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
+
+ return stream;
+}
+
diff --git a/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/WebCore/platform/network/soup/cache/soup-directory-input-stream.h
new file mode 100644
index 0000000..0c5b0be
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-directory-input-stream.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H 1
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM (webkit_soup_directory_input_stream_get_type ())
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStream))
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass))
+#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM))
+#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM))
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass))
+
+typedef struct _WebKitSoupDirectoryInputStream WebKitSoupDirectoryInputStream;
+typedef struct _WebKitSoupDirectoryInputStreamClass WebKitSoupDirectoryInputStreamClass;
+
+struct _WebKitSoupDirectoryInputStream {
+ GInputStream parent;
+
+ GFileEnumerator *enumerator;
+ char *uri;
+ SoupBuffer *buffer;
+ gboolean done;
+};
+
+struct _WebKitSoupDirectoryInputStreamClass {
+ GInputStreamClass parent_class;
+};
+
+GType webkit_soup_directory_input_stream_get_type (void);
+
+GInputStream *webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator,
+ SoupURI *uri);
+
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H */
diff --git a/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/WebCore/platform/network/soup/cache/soup-http-input-stream.c
new file mode 100644
index 0000000..dc95d6e
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-http-input-stream.c
@@ -0,0 +1,921 @@
+/* soup-input-stream.c, based on gsocketinputstream.c
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libsoup/soup.h>
+
+#include "soup-http-input-stream.h"
+
+static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupHTTPInputStream, webkit_soup_http_input_stream, G_TYPE_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+ webkit_soup_http_input_stream_seekable_iface_init))
+
+typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *);
+
+typedef struct {
+ SoupSession *session;
+ GMainContext *async_context;
+ SoupMessage *msg;
+ gboolean got_headers, finished;
+ goffset offset;
+
+ GCancellable *cancellable;
+ GSource *cancel_watch;
+ WebKitSoupHTTPInputStreamCallback got_headers_cb;
+ WebKitSoupHTTPInputStreamCallback got_chunk_cb;
+ WebKitSoupHTTPInputStreamCallback finished_cb;
+ WebKitSoupHTTPInputStreamCallback cancelled_cb;
+
+ guchar *leftover_buffer;
+ gsize leftover_bufsize, leftover_offset;
+
+ guchar *caller_buffer;
+ gsize caller_bufsize, caller_nread;
+ GAsyncReadyCallback outstanding_callback;
+ GSimpleAsyncResult *result;
+} WebKitSoupHTTPInputStreamPrivate;
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamPrivate))
+
+
+static gssize webkit_soup_http_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean webkit_soup_http_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+static void webkit_soup_http_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gssize webkit_soup_http_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+static void webkit_soup_http_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gboolean webkit_soup_http_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+
+static goffset webkit_soup_http_input_stream_tell (GSeekable *seekable);
+
+static gboolean webkit_soup_http_input_stream_can_seek (GSeekable *seekable);
+static gboolean webkit_soup_http_input_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable *seekable);
+static gboolean webkit_soup_http_input_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error);
+
+static void webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream);
+static void webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream);
+static void webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream);
+
+static void
+webkit_soup_http_input_stream_finalize (GObject *object)
+{
+ WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object);
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_object_unref (priv->session);
+
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
+ g_object_unref (priv->msg);
+ g_free (priv->leftover_buffer);
+
+ if (G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)
+ (*G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)(object);
+}
+
+static void
+webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate));
+
+ gobject_class->finalize = webkit_soup_http_input_stream_finalize;
+
+ stream_class->read_fn = webkit_soup_http_input_stream_read;
+ stream_class->close_fn = webkit_soup_http_input_stream_close;
+ stream_class->read_async = webkit_soup_http_input_stream_read_async;
+ stream_class->read_finish = webkit_soup_http_input_stream_read_finish;
+ stream_class->close_async = webkit_soup_http_input_stream_close_async;
+ stream_class->close_finish = webkit_soup_http_input_stream_close_finish;
+}
+
+static void
+webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
+{
+ seekable_iface->tell = webkit_soup_http_input_stream_tell;
+ seekable_iface->can_seek = webkit_soup_http_input_stream_can_seek;
+ seekable_iface->seek = webkit_soup_http_input_stream_seek;
+ seekable_iface->can_truncate = webkit_soup_http_input_stream_can_truncate;
+ seekable_iface->truncate_fn = webkit_soup_http_input_stream_truncate;
+}
+
+static void
+webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream)
+{
+ ;
+}
+
+static void
+webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->got_headers = priv->finished = FALSE;
+
+ /* Add an extra ref since soup_session_queue_message steals one */
+ g_object_ref (priv->msg);
+ soup_session_queue_message (priv->session, priv->msg, NULL, NULL);
+}
+
+/**
+ * webkit_soup_http_input_stream_new:
+ * @session: the #SoupSession to use
+ * @msg: the #SoupMessage whose response will be streamed
+ *
+ * Prepares to send @msg over @session, and returns a #GInputStream
+ * that can be used to read the response.
+ *
+ * @msg may not be sent until the first read call; if you need to look
+ * at the status code or response headers before reading the body, you
+ * can use webkit_soup_http_input_stream_send() or webkit_soup_http_input_stream_send_async()
+ * to force the message to be sent and the response headers read.
+ *
+ * If @msg gets a non-2xx result, the first read (or send) will return
+ * an error with type %WEBKIT_SOUP_HTTP_INPUT_STREAM_HTTP_ERROR.
+ *
+ * Internally, #WebKitSoupHTTPInputStream is implemented using asynchronous I/O,
+ * so if you are using the synchronous API (eg,
+ * g_input_stream_read()), you should create a new #GMainContext and
+ * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If
+ * you don't, then synchronous #GInputStream calls will cause the main
+ * loop to be run recursively.) The async #GInputStream API works fine
+ * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset.
+ *
+ * Returns: a new #GInputStream.
+ **/
+WebKitSoupHTTPInputStream *
+webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
+{
+ WebKitSoupHTTPInputStream *stream;
+ WebKitSoupHTTPInputStreamPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL);
+ priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->session = g_object_ref (session);
+ priv->async_context = soup_session_get_async_context (session);
+ priv->msg = g_object_ref (msg);
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream);
+ g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
+
+ webkit_soup_http_input_stream_queue_message (stream);
+ return stream;
+}
+
+static void
+webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ /* If the status is unsuccessful, we just ignore the signal and let
+ * libsoup keep going (eventually either it will requeue the request
+ * (after handling authentication/redirection), or else the
+ * "finished" handler will run).
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ priv->got_headers = TRUE;
+ if (!priv->caller_buffer) {
+ /* Not ready to read the body yet */
+ soup_session_pause_message (priv->session, msg);
+ }
+
+ if (priv->got_headers_cb)
+ priv->got_headers_cb (stream);
+}
+
+static void
+webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
+ gpointer stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ const gchar *chunk = chunk_buffer->data;
+ gsize chunk_size = chunk_buffer->length;
+
+ /* We only pay attention to the chunk if it's part of a successful
+ * response.
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ /* Sanity check */
+ if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0)
+ g_warning ("webkit_soup_http_input_stream_got_chunk called again before previous chunk was processed");
+
+ /* Copy what we can into priv->caller_buffer */
+ if (priv->caller_bufsize - priv->caller_nread > 0) {
+ gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread);
+
+ memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
+ priv->caller_nread += nread;
+ priv->offset += nread;
+ chunk += nread;
+ chunk_size -= nread;
+ }
+
+ if (chunk_size > 0) {
+ /* Copy the rest into priv->leftover_buffer. If
+ * there's already some data there, realloc and
+ * append. Otherwise just copy.
+ */
+ if (priv->leftover_bufsize) {
+ priv->leftover_buffer = g_realloc (priv->leftover_buffer,
+ priv->leftover_bufsize + chunk_size);
+ memcpy (priv->leftover_buffer + priv->leftover_bufsize,
+ chunk, chunk_size);
+ priv->leftover_bufsize += chunk_size;
+ } else {
+ priv->leftover_bufsize = chunk_size;
+ priv->leftover_buffer = g_memdup (chunk, chunk_size);
+ priv->leftover_offset = 0;
+ }
+ }
+
+ soup_session_pause_message (priv->session, msg);
+ if (priv->got_chunk_cb)
+ priv->got_chunk_cb (stream);
+}
+
+static void
+webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->finished = TRUE;
+
+ if (priv->finished_cb)
+ priv->finished_cb (stream);
+}
+
+static gboolean
+webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
+ gpointer stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->cancel_watch = NULL;
+
+ soup_session_pause_message (priv->session, priv->msg);
+ if (priv->cancelled_cb)
+ priv->cancelled_cb (stream);
+
+ return FALSE;
+}
+
+static void
+webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream,
+ GCancellable *cancellable,
+ guchar *buffer,
+ gsize count)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ int cancel_fd;
+
+ priv->cancellable = cancellable;
+ cancel_fd = g_cancellable_get_fd (cancellable);
+ if (cancel_fd != -1) {
+ GIOChannel *chan = g_io_channel_unix_new (cancel_fd);
+ priv->cancel_watch = soup_add_io_watch (priv->async_context, chan,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ webkit_soup_http_input_stream_cancelled,
+ stream);
+ g_io_channel_unref (chan);
+ }
+
+ priv->caller_buffer = buffer;
+ priv->caller_bufsize = count;
+ priv->caller_nread = 0;
+
+ if (priv->got_headers)
+ soup_session_unpause_message (priv->session, priv->msg);
+}
+
+static void
+webkit_soup_http_input_stream_done_io (GInputStream *stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->cancel_watch) {
+ g_source_destroy (priv->cancel_watch);
+ priv->cancel_watch = NULL;
+ g_cancellable_release_fd (priv->cancellable);
+ }
+ priv->cancellable = NULL;
+
+ priv->caller_buffer = NULL;
+ priv->caller_bufsize = 0;
+}
+
+static gboolean
+set_error_if_http_failed (SoupMessage *msg, GError **error)
+{
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ g_set_error_literal (error, SOUP_HTTP_ERROR,
+ msg->status_code, msg->reason_phrase);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gsize
+read_from_leftover (WebKitSoupHTTPInputStreamPrivate *priv,
+ gpointer buffer, gsize bufsize)
+{
+ gsize nread;
+
+ if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) {
+ nread = priv->leftover_bufsize - priv->leftover_offset;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+
+ g_free (priv->leftover_buffer);
+ priv->leftover_buffer = NULL;
+ priv->leftover_bufsize = priv->leftover_offset = 0;
+ } else {
+ nread = bufsize;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+ priv->leftover_offset += nread;
+ }
+
+ priv->offset += nread;
+ return nread;
+}
+
+/* This does the work of webkit_soup_http_input_stream_send(), assuming that the
+ * GInputStream pending flag has already been set. It is also used by
+ * webkit_soup_http_input_stream_send_async() in some circumstances.
+ */
+static gboolean
+webkit_soup_http_input_stream_send_internal (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ while (!priv->finished && !priv->got_headers &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ webkit_soup_http_input_stream_done_io (stream);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return FALSE;
+ return TRUE;
+}
+
+static void
+send_sync_finished (GInputStream *stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
+ set_error_if_http_failed (priv->msg, &error);
+
+ priv->got_headers_cb = NULL;
+ priv->finished_cb = NULL;
+
+ /* Wake up the main context iteration */
+ g_source_attach (g_idle_source_new (), NULL);
+}
+
+/**
+ * webkit_soup_http_input_stream_send:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Synchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after webkit_soup_http_input_stream_new()
+ * and before the first g_input_stream_read() if you want to check the
+ * HTTP status code before you start reading.
+ *
+ * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
+ * not.
+ **/
+gboolean
+webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+ GInputStream *istream = (GInputStream *)httpstream;
+ gboolean result;
+
+ g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE);
+
+ if (!g_input_stream_set_pending (istream, error))
+ return FALSE;
+
+ priv->got_headers_cb = send_sync_finished;
+ priv->finished_cb = send_sync_finished;
+
+ result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error);
+ g_input_stream_clear_pending (istream);
+
+ return result;
+}
+
+static gssize
+webkit_soup_http_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->finished)
+ return 0;
+
+ /* If there is data leftover from a previous read, return it. */
+ if (priv->leftover_bufsize)
+ return read_from_leftover (priv, buffer, count);
+
+ /* No leftover data, accept one chunk from the network */
+ webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+ while (!priv->finished && priv->caller_nread == 0 &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ webkit_soup_http_input_stream_done_io (stream);
+
+ if (priv->caller_nread > 0)
+ return priv->caller_nread;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return -1;
+ else
+ return 0;
+}
+
+static gboolean
+webkit_soup_http_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (!priv->finished)
+ soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
+
+ return TRUE;
+}
+
+static void
+wrapper_callback (GObject *source_object, GAsyncResult *res,
+ gpointer user_data)
+{
+ GInputStream *stream = G_INPUT_STREAM (source_object);
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_input_stream_clear_pending (stream);
+ if (priv->outstanding_callback)
+ (*priv->outstanding_callback)(source_object, res, user_data);
+ priv->outstanding_callback = NULL;
+ g_object_unref (stream);
+}
+
+static void
+send_async_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ success = webkit_soup_http_input_stream_send_internal (G_INPUT_STREAM (object),
+ cancellable, &error);
+ g_simple_async_result_set_op_res_gboolean (res, success);
+ if (error) {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ }
+}
+
+static void
+webkit_soup_http_input_stream_send_async_in_thread (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+ webkit_soup_http_input_stream_send_async_in_thread);
+ g_simple_async_result_run_in_thread (res, send_async_thread,
+ io_priority, cancellable);
+ g_object_unref (res);
+}
+
+static void
+send_async_finished (GInputStream *stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
+ set_error_if_http_failed (priv->msg, &error);
+
+ priv->got_headers_cb = NULL;
+ priv->finished_cb = NULL;
+ webkit_soup_http_input_stream_done_io (stream);
+
+ result = priv->result;
+ priv->result = NULL;
+
+ g_simple_async_result_set_op_res_gboolean (result, error == NULL);
+ if (error) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+ g_simple_async_result_complete (result);
+}
+
+static void
+webkit_soup_http_input_stream_send_async_internal (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_object_ref (stream);
+ priv->outstanding_callback = callback;
+
+ /* If the session uses the default GMainContext, then we can do
+ * async I/O directly. But if it has its own main context, it's
+ * easier to just run it in another thread.
+ */
+ if (soup_session_get_async_context (priv->session)) {
+ webkit_soup_http_input_stream_send_async_in_thread (stream, io_priority, cancellable,
+ wrapper_callback, user_data);
+ return;
+ }
+
+ priv->got_headers_cb = send_async_finished;
+ priv->finished_cb = send_async_finished;
+
+ webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ priv->result = g_simple_async_result_new (G_OBJECT (stream),
+ wrapper_callback, user_data,
+ webkit_soup_http_input_stream_send_async);
+}
+
+/**
+ * webkit_soup_http_input_stream_send_async:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after webkit_soup_http_input_stream_new()
+ * and before the first g_input_stream_read_async() if you want to
+ * check the HTTP status code before you start reading.
+ **/
+void
+webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GInputStream *istream = (GInputStream *)httpstream;
+ GError *error = NULL;
+
+ g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream));
+
+ if (!g_input_stream_set_pending (istream, &error)) {
+ g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
+ callback,
+ user_data,
+ error);
+ g_error_free (error);
+ return;
+ }
+ webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
+ callback, user_data);
+}
+
+/**
+ * webkit_soup_http_input_stream_send_finish:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to
+ * ignore.
+ *
+ * Finishes a webkit_soup_http_input_stream_send_async() operation.
+ *
+ * Return value: %TRUE if the message was sent successfully and
+ * received a successful status code, %FALSE if not.
+ **/
+gboolean
+webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_send_async, FALSE);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+static void
+read_async_done (GInputStream *stream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ result = priv->result;
+ priv->result = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) ||
+ set_error_if_http_failed (priv->msg, &error)) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ } else
+ g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
+
+ priv->got_chunk_cb = NULL;
+ priv->finished_cb = NULL;
+ priv->cancelled_cb = NULL;
+ webkit_soup_http_input_stream_done_io (stream);
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
+static void
+webkit_soup_http_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+
+ /* If the session uses the default GMainContext, then we can do
+ * async I/O directly. But if it has its own main context, we fall
+ * back to the async-via-sync-in-another-thread implementation.
+ */
+ if (soup_session_get_async_context (priv->session)) {
+ G_INPUT_STREAM_CLASS (webkit_soup_http_input_stream_parent_class)->
+ read_async (stream, buffer, count, io_priority,
+ cancellable, callback, user_data);
+ return;
+ }
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ webkit_soup_http_input_stream_read_async);
+
+ if (priv->finished) {
+ g_simple_async_result_set_op_res_gssize (result, 0);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ if (priv->leftover_bufsize) {
+ gsize nread = read_from_leftover (priv, buffer, count);
+ g_simple_async_result_set_op_res_gssize (result, nread);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ priv->result = result;
+
+ priv->got_chunk_cb = read_async_done;
+ priv->finished_cb = read_async_done;
+ priv->cancelled_cb = read_async_done;
+ webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+}
+
+static gssize
+webkit_soup_http_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_read_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+static void
+webkit_soup_http_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gboolean success;
+ GError *error = NULL;
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ webkit_soup_http_input_stream_close_async);
+ success = webkit_soup_http_input_stream_close (stream, cancellable, &error);
+ g_simple_async_result_set_op_res_gboolean (result, success);
+ if (error) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+static gboolean
+webkit_soup_http_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ /* Failures handled in generic close_finish code */
+ return TRUE;
+}
+
+static goffset
+webkit_soup_http_input_stream_tell (GSeekable *seekable)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
+
+ return priv->offset;
+}
+
+static gboolean
+webkit_soup_http_input_stream_can_seek (GSeekable *seekable)
+{
+ return TRUE;
+}
+
+extern void soup_message_io_cleanup (SoupMessage *msg);
+
+static gboolean
+webkit_soup_http_input_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInputStream *stream = G_INPUT_STREAM (seekable);
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
+ char *range;
+
+ if (type == G_SEEK_END) {
+ /* FIXME: we could send "bytes=-offset", but unless we
+ * know the Content-Length, we wouldn't be able to
+ * answer a tell() properly. We could find the
+ * Content-Length by doing a HEAD...
+ */
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "G_SEEK_END not currently supported");
+ return FALSE;
+ }
+
+ if (!g_input_stream_set_pending (stream, error))
+ return FALSE;
+
+ soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
+ soup_message_io_cleanup (priv->msg);
+
+ switch (type) {
+ case G_SEEK_CUR:
+ offset += priv->offset;
+ /* fall through */
+
+ case G_SEEK_SET:
+ range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset);
+ priv->offset = offset;
+ break;
+
+ case G_SEEK_END:
+ range = NULL; /* keep compilers happy */
+ g_return_val_if_reached (FALSE);
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ soup_message_headers_remove (priv->msg->request_headers, "Range");
+ soup_message_headers_append (priv->msg->request_headers, "Range", range);
+ g_free (range);
+
+ webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream));
+
+ g_input_stream_clear_pending (stream);
+ return TRUE;
+}
+
+static gboolean
+webkit_soup_http_input_stream_can_truncate (GSeekable *seekable)
+{
+ return FALSE;
+}
+
+static gboolean
+webkit_soup_http_input_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Truncate not allowed on input stream");
+ return FALSE;
+}
+
+SoupMessage *
+webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream)
+{
+ WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+ return priv->msg ? g_object_ref (priv->msg) : NULL;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/WebCore/platform/network/soup/cache/soup-http-input-stream.h
new file mode 100644
index 0000000..6b98559
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-http-input-stream.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, 2007, 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__
+#define __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM (webkit_soup_http_input_stream_get_type ())
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStream))
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass))
+#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM))
+#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM))
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass))
+
+typedef struct WebKitSoupHTTPInputStream WebKitSoupHTTPInputStream;
+typedef struct WebKitSoupHTTPInputStreamClass WebKitSoupHTTPInputStreamClass;
+
+struct WebKitSoupHTTPInputStream {
+ GInputStream parent;
+};
+
+struct WebKitSoupHTTPInputStreamClass {
+ GInputStreamClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_g_reserved1)(void);
+ void (*_g_reserved2)(void);
+ void (*_g_reserved3)(void);
+ void (*_g_reserved4)(void);
+ void (*_g_reserved5)(void);
+};
+
+GType webkit_soup_http_input_stream_get_type (void) G_GNUC_CONST;
+
+WebKitSoupHTTPInputStream *webkit_soup_http_input_stream_new (SoupSession *session,
+ SoupMessage *msg);
+
+gboolean webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream,
+ GCancellable *cancellable,
+ GError **error);
+
+void webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream,
+ GAsyncResult *result,
+ GError **error);
+
+SoupMessage *webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream);
+
+G_END_DECLS
+
+#endif /* __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ */
diff --git a/WebCore/platform/network/soup/cache/soup-request-data.c b/WebCore/platform/network/soup/cache/soup-request-data.c
new file mode 100644
index 0000000..ced5c4a
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-data.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-data.c: data: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "soup-request-data.h"
+
+#include "soup-requester.h"
+#include <libsoup/soup.h>
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (WebKitSoupRequestData, webkit_soup_request_data, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestDataPrivate {
+ gsize content_length;
+ char *content_type;
+};
+
+static void
+webkit_soup_request_data_init (WebKitSoupRequestData *data)
+{
+ data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataPrivate);
+}
+
+static void
+webkit_soup_request_data_finalize (GObject *object)
+{
+ WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (object);
+
+ g_free (data->priv->content_type);
+
+ G_OBJECT_CLASS (webkit_soup_request_data_parent_class)->finalize (object);
+}
+
+static gboolean
+webkit_soup_request_data_check_uri (WebKitSoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ return uri->host == NULL;
+}
+
+static GInputStream *
+webkit_soup_request_data_send (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+ SoupURI *uri = webkit_soup_request_get_uri (request);
+ GInputStream *memstream;
+ const char *comma, *semi, *start, *end;
+ gboolean base64 = FALSE;
+
+ gchar *uristr = soup_uri_to_string (uri, FALSE);
+ comma = strchr (uristr, ',');
+ if (comma && comma != uristr) {
+ /* Deal with MIME type / params */
+ semi = memchr (uristr, ';', comma - uristr);
+ end = semi ? semi : comma;
+
+ if (semi && !g_ascii_strncasecmp (semi, ";base64", MAX ((size_t) (comma - semi), strlen (";base64"))))
+ base64 = TRUE;
+
+ if (end != uristr)
+ if (base64)
+ data->priv->content_type = g_strndup (uristr, end - uristr);
+ else
+ data->priv->content_type =
+ webkit_soup_request_uri_decoded_copy (uristr, end - uristr);
+ }
+
+ memstream = g_memory_input_stream_new ();
+
+ start = comma ? comma + 1 : uristr;
+
+ if (*start) {
+ guchar *buf;
+
+ if (base64) {
+ int inlen, state = 0;
+ guint save = 0;
+
+ inlen = strlen (start);
+ buf = g_malloc0 (inlen * 3 / 4 + 3);
+ data->priv->content_length =
+ g_base64_decode_step (start, inlen, buf,
+ &state, &save);
+ if (state != 0) {
+ g_free (buf);
+ goto fail;
+ }
+ } else {
+ /* Cannot use g_uri_unescape_string nor
+ soup_uri_decode because we don't want to
+ fail for things like "%3E%%3C" -> ">%<" */
+ buf = (guchar *)webkit_soup_request_uri_decoded_copy (start, strlen (start));
+ if (!buf)
+ goto fail;
+ data->priv->content_length = strlen ((char *)buf);
+ }
+
+ g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream),
+ buf, data->priv->content_length,
+ g_free);
+ }
+ g_free (uristr);
+
+ return memstream;
+
+ fail:
+ g_free (uristr);
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+ _ ("Unable to decode URI: %s"), start);
+ g_object_unref (memstream);
+ return NULL;
+}
+
+static goffset
+webkit_soup_request_data_get_content_length (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+
+ return data->priv->content_length;
+}
+
+static const char *
+webkit_soup_request_data_get_content_type (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+
+ return data->priv->content_type;
+}
+
+static void
+webkit_soup_request_data_class_init (WebKitSoupRequestDataClass *request_data_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_data_class);
+ WebKitSoupRequestClass *request_class =
+ WEBKIT_SOUP_REQUEST_CLASS (request_data_class);
+
+ g_type_class_add_private (request_data_class, sizeof (WebKitSoupRequestDataPrivate));
+
+ object_class->finalize = webkit_soup_request_data_finalize;
+
+ request_class->check_uri = webkit_soup_request_data_check_uri;
+ request_class->send = webkit_soup_request_data_send;
+ request_class->get_content_length = webkit_soup_request_data_get_content_length;
+ request_class->get_content_type = webkit_soup_request_data_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-data.h b/WebCore/platform/network/soup/cache/soup-request-data.h
new file mode 100644
index 0000000..c9631a4
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-data.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_DATA_H
+#define WEBKIT_SOUP_REQUEST_DATA_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_DATA (webkit_soup_request_data_get_type ())
+#define WEBKIT_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestData))
+#define WEBKIT_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass))
+#define WEBKIT_IS_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA))
+#define WEBKIT_IS_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA))
+#define WEBKIT_SOUP_REQUEST_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass))
+
+typedef struct _WebKitSoupRequestDataPrivate WebKitSoupRequestDataPrivate;
+
+typedef struct {
+ WebKitSoupRequest parent;
+
+ WebKitSoupRequestDataPrivate *priv;
+} WebKitSoupRequestData;
+
+typedef struct {
+ WebKitSoupRequestClass parent;
+} WebKitSoupRequestDataClass;
+
+GType webkit_soup_request_data_get_type (void);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_DATA_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request-file.c b/WebCore/platform/network/soup/cache/soup-request-file.c
new file mode 100644
index 0000000..24ccb10
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-file.c
@@ -0,0 +1,331 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-file.c: file: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-request-file.h"
+#include "soup-directory-input-stream.h"
+#include "soup-requester.h"
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (WebKitSoupRequestFile, webkit_soup_request_file, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestFilePrivate {
+ GFile *gfile;
+
+ char *mime_type;
+ goffset size;
+};
+
+GFile *
+webkit_soup_request_file_get_file (WebKitSoupRequestFile *file)
+{
+ return g_object_ref (file->priv->gfile);
+}
+
+static void
+webkit_soup_request_file_init (WebKitSoupRequestFile *file)
+{
+ file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFilePrivate);
+
+ file->priv->size = -1;
+}
+
+static void
+webkit_soup_request_file_finalize (GObject *object)
+{
+ WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (object);
+
+ if (file->priv->gfile)
+ g_object_unref (file->priv->gfile);
+ g_free (file->priv->mime_type);
+
+ G_OBJECT_CLASS (webkit_soup_request_file_parent_class)->finalize (object);
+}
+
+static gboolean
+webkit_soup_request_file_check_uri (WebKitSoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ /* "file:/foo" is not valid */
+ if (!uri->host)
+ return FALSE;
+
+ /* but it must be "file:///..." or "file://localhost/..." */
+ if (uri->scheme == SOUP_URI_SCHEME_FILE &&
+ *uri->host &&
+ g_ascii_strcasecmp (uri->host, "localhost") != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+webkit_soup_request_file_ftp_main_loop_quit (GObject *object,
+ GAsyncResult *result,
+ gpointer loop)
+{
+ g_main_loop_quit (loop);
+}
+
+/* This is a somewhat hacky way to get FTP to almost work. The proper way to
+ * get FTP to _really_ work involves hacking GIO to have APIs to handle
+ * canoncial URLs.
+ */
+static GFile *
+webkit_soup_request_file_ensure_file_ftp (SoupURI *uri,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupURI *host;
+ char *s;
+ GFile *file, *result;
+ GMount *mount;
+
+ host = soup_uri_copy_host (uri);
+ s = soup_uri_to_string (host, FALSE);
+ file = g_file_new_for_uri (s);
+ soup_uri_free (host);
+ g_free (s);
+
+ mount = g_file_find_enclosing_mount (file, cancellable, error);
+ if (mount == NULL && g_file_supports_thread_contexts (file)) {
+ GMainContext *context = g_main_context_new ();
+ GMainLoop *loop = g_main_loop_new (context, FALSE);
+
+ g_clear_error (error);
+ g_main_context_push_thread_default (context);
+ g_file_mount_enclosing_volume (file,
+ G_MOUNT_MOUNT_NONE,
+ NULL, /* FIXME! */
+ cancellable,
+ webkit_soup_request_file_ftp_main_loop_quit,
+ loop);
+ g_main_loop_run (loop);
+ g_main_context_pop_thread_default (context);
+ g_main_loop_unref (loop);
+ g_main_context_unref (context);
+ mount = g_file_find_enclosing_mount (file, cancellable, error);
+ }
+ if (mount == NULL)
+ return NULL;
+ g_object_unref (file);
+
+ file = g_mount_get_default_location (mount);
+ g_object_unref (mount);
+
+ s = g_strdup (uri->path);
+ if (strchr (s, ';'))
+ *strchr (s, ';') = 0;
+
+ result = g_file_resolve_relative_path (file, s);
+ g_free (s);
+ g_object_unref (file);
+
+ return result;
+}
+
+static gboolean
+webkit_soup_request_file_ensure_file (WebKitSoupRequestFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupURI *uri;
+
+ if (file->priv->gfile)
+ return TRUE;
+
+ uri = webkit_soup_request_get_uri (WEBKIT_SOUP_REQUEST (file));
+ if (uri->scheme == SOUP_URI_SCHEME_FILE) {
+ /* We cannot use soup_uri_decode as it incorrectly
+ * returns NULL for incorrectly encoded URIs (that
+ * could be valid filenames). This will be hopefully
+ * shipped in libsoup 2.32.1 but we want to land this
+ * first. TODO: replace uri_decoded_copy by
+ * soup_uri_decode when the required libsoup version
+ * is bumped out to 2.32.1
+ */
+ gchar *decoded_uri = webkit_soup_request_uri_decoded_copy (uri->path, strlen (uri->path));
+
+ if (decoded_uri) {
+ /* Do not use new_for_uri() as the decoded URI
+ * could not be a valid URI
+ */
+ file->priv->gfile = g_file_new_for_path (decoded_uri);
+ g_free (decoded_uri);
+ }
+
+ return TRUE;
+ } else if (uri->scheme == SOUP_URI_SCHEME_FTP) {
+ file->priv->gfile = webkit_soup_request_file_ensure_file_ftp (uri,
+ cancellable,
+ error);
+ return file->priv->gfile != NULL;
+ }
+
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
+ _ ("Unsupported URI scheme '%s'"), uri->scheme);
+ return FALSE;
+}
+
+static GInputStream *
+webkit_soup_request_file_send (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+ GInputStream *stream;
+ GError *my_error = NULL;
+
+ if (!webkit_soup_request_file_ensure_file (file, cancellable, error))
+ return NULL;
+
+ stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
+ cancellable, &my_error));
+ if (stream == NULL) {
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
+ GFileEnumerator *enumerator;
+ g_clear_error (&my_error);
+ enumerator = g_file_enumerate_children (file->priv->gfile,
+ "*",
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ error);
+ if (enumerator) {
+ stream = webkit_soup_directory_input_stream_new (enumerator,
+ webkit_soup_request_get_uri (request));
+ g_object_unref (enumerator);
+ file->priv->mime_type = g_strdup ("text/html");
+ }
+ } else {
+ g_propagate_error (error, my_error);
+ }
+ } else {
+ GFileInfo *info = g_file_query_info (file->priv->gfile,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ 0, cancellable, NULL);
+ if (info) {
+ const char *content_type;
+ file->priv->size = g_file_info_get_size (info);
+ content_type = g_file_info_get_content_type (info);
+
+ if (content_type)
+ file->priv->mime_type = g_content_type_get_mime_type (content_type);
+ g_object_unref (info);
+ }
+ }
+
+ return stream;
+}
+
+static void
+webkit_soup_request_file_send_async_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GInputStream *stream;
+ WebKitSoupRequest *request;
+ GError *error = NULL;
+
+ request = WEBKIT_SOUP_REQUEST (object);
+
+ stream = webkit_soup_request_file_send (request, cancellable, &error);
+
+ if (stream == NULL) {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ } else {
+ g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+ }
+}
+
+static void
+webkit_soup_request_file_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, webkit_soup_request_file_send_async);
+
+ g_simple_async_result_run_in_thread (res, webkit_soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
+ g_object_unref (res);
+}
+
+static GInputStream *
+webkit_soup_request_file_send_finish (WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_request_file_send_async);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+webkit_soup_request_file_get_content_length (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+
+ return file->priv->size;
+}
+
+static const char *
+webkit_soup_request_file_get_content_type (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+
+ if (!file->priv->mime_type)
+ return "application/octet-stream";
+
+ return file->priv->mime_type;
+}
+
+static void
+webkit_soup_request_file_class_init (WebKitSoupRequestFileClass *request_file_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
+ WebKitSoupRequestClass *request_class =
+ WEBKIT_SOUP_REQUEST_CLASS (request_file_class);
+
+ g_type_class_add_private (request_file_class, sizeof (WebKitSoupRequestFilePrivate));
+
+ object_class->finalize = webkit_soup_request_file_finalize;
+
+ request_class->check_uri = webkit_soup_request_file_check_uri;
+ request_class->send = webkit_soup_request_file_send;
+ request_class->send_async = webkit_soup_request_file_send_async;
+ request_class->send_finish = webkit_soup_request_file_send_finish;
+ request_class->get_content_length = webkit_soup_request_file_get_content_length;
+ request_class->get_content_type = webkit_soup_request_file_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-file.h b/WebCore/platform/network/soup/cache/soup-request-file.h
new file mode 100644
index 0000000..459e82a
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-file.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_FILE_H
+#define WEBKIT_SOUP_REQUEST_FILE_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_FILE (webkit_soup_request_file_get_type ())
+#define WEBKIT_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFile))
+#define WEBKIT_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass))
+#define WEBKIT_IS_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE))
+#define WEBKIT_IS_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE))
+#define WEBKIT_SOUP_REQUEST_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass))
+
+typedef struct _WebKitSoupRequestFilePrivate WebKitSoupRequestFilePrivate;
+
+typedef struct {
+ WebKitSoupRequest parent;
+
+ WebKitSoupRequestFilePrivate *priv;
+} WebKitSoupRequestFile;
+
+typedef struct {
+ WebKitSoupRequestClass parent;
+} WebKitSoupRequestFileClass;
+
+GType webkit_soup_request_file_get_type (void);
+
+GFile *webkit_soup_request_file_get_file (WebKitSoupRequestFile *file);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_FILE_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request-http.c b/WebCore/platform/network/soup/cache/soup-request-http.c
new file mode 100644
index 0000000..f157cfc
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-http.c
@@ -0,0 +1,340 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-http.c: http: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include "soup-http-input-stream.h"
+#include "soup-request-http.h"
+
+G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestHTTPPrivate {
+ SoupMessage *msg;
+};
+
+/**
+ * webkit_soup_request_http_get_message:
+ * @http: a #WebKitSoupRequestHTTP object
+ *
+ * Gets a new reference to the #SoupMessage associated to this SoupRequest
+ *
+ * Returns: a new reference to the #SoupMessage
+ **/
+SoupMessage *
+webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http)
+{
+ g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL);
+
+ return g_object_ref (http->priv->msg);
+}
+
+static void
+webkit_soup_request_http_init (WebKitSoupRequestHTTP *http)
+{
+ http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate);
+}
+
+static gboolean
+webkit_soup_request_http_check_uri (WebKitSoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+ if (!SOUP_URI_VALID_FOR_HTTP (uri))
+ return FALSE;
+
+ http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ return TRUE;
+}
+
+static void
+webkit_soup_request_http_finalize (GObject *object)
+{
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object);
+
+ if (http->priv->msg)
+ g_object_unref (http->priv->msg);
+
+ G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object);
+}
+
+static GInputStream *
+webkit_soup_request_http_send (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupHTTPInputStream *httpstream;
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+ httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), http->priv->msg);
+ if (!webkit_soup_http_input_stream_send (httpstream, cancellable, error)) {
+ g_object_unref (httpstream);
+ return NULL;
+ }
+ return (GInputStream *)httpstream;
+}
+
+
+static void
+sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source);
+ GSimpleAsyncResult *simple = user_data;
+ GError *error = NULL;
+
+ if (webkit_soup_http_input_stream_send_finish (httpstream, result, &error)) {
+ g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+ } else {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ g_object_unref (httpstream);
+ }
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+
+typedef struct {
+ WebKitSoupRequestHTTP *req;
+ SoupMessage *original;
+ GCancellable *cancellable;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+} ConditionalHelper;
+
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ ConditionalHelper *helper = (ConditionalHelper *)user_data;
+ GSimpleAsyncResult *simple;
+ WebKitSoupHTTPInputStream *httpstream;
+
+ simple = g_simple_async_result_new (G_OBJECT (helper->req),
+ helper->callback, helper->user_data,
+ conditional_get_ready_cb);
+
+ if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+ WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+ httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg);
+ if (httpstream) {
+ const gchar *content_type;
+
+ g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+
+ soup_message_got_headers (helper->original);
+ content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+ soup_message_content_sniffed (helper->original, content_type, NULL);
+
+ g_simple_async_result_complete (simple);
+
+ soup_message_finished (helper->original);
+
+ g_object_unref (simple);
+ } else {
+ /* Ask again for the resource, somehow the cache cannot locate it */
+ httpstream = webkit_soup_http_input_stream_new (session, helper->original);
+ webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ helper->cancellable, sent_async, simple);
+ }
+ } else {
+ /* It is in the cache but it was modified remotely */
+ httpstream = webkit_soup_http_input_stream_new (session, helper->original);
+ webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ helper->cancellable, sent_async, simple);
+ }
+
+ g_object_unref (helper->req);
+ g_object_unref (helper->original);
+ g_slice_free (ConditionalHelper, helper);
+}
+
+typedef struct {
+ WebKitSoupRequestHTTP *http;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+} SendAsyncHelper;
+
+static void webkit_soup_request_http_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static gboolean
+send_async_cb (gpointer data)
+{
+ GSimpleAsyncResult *simple;
+ WebKitSoupHTTPInputStream *httpstream;
+ SoupSession *session;
+ WebKitSoupCache *cache;
+ SendAsyncHelper *helper = (SendAsyncHelper *)data;
+
+ session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http));
+ cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+ httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg));
+
+ if (httpstream) {
+ const gchar *content_type;
+
+ simple = g_simple_async_result_new (G_OBJECT (helper->http),
+ helper->callback, helper->user_data,
+ webkit_soup_request_http_send_async);
+ g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+
+ /* Update message status */
+ soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
+
+ /* Issue signals */
+ soup_message_got_headers (helper->http->priv->msg);
+ content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
+ soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
+
+ g_simple_async_result_complete (simple);
+
+ soup_message_finished (helper->http->priv->msg);
+
+ g_object_unref (simple);
+ }
+
+ g_slice_free (SendAsyncHelper, helper);
+
+ return FALSE;
+}
+
+static void
+webkit_soup_request_http_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+ WebKitSoupHTTPInputStream *httpstream;
+ GSimpleAsyncResult *simple;
+ SoupSession *session;
+ WebKitSoupCache *cache;
+
+ session = webkit_soup_request_get_session (request);
+ cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+ if (cache) {
+ WebKitSoupCacheResponse response;
+
+ response = webkit_soup_cache_has_response (cache, http->priv->msg);
+ if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) {
+ /* Do return the stream asynchronously as in
+ the other cases. It's not enough to use
+ g_simple_async_result_complete_in_idle as
+ the signals must be also emitted
+ asynchronously */
+ SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
+ helper->http = http;
+ helper->callback = callback;
+ helper->user_data = user_data;
+ g_timeout_add (0, send_async_cb, helper);
+ return;
+ } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+ SoupMessage *conditional_msg;
+ ConditionalHelper *helper;
+
+ conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg);
+
+ helper = g_slice_new0 (ConditionalHelper);
+ helper->req = g_object_ref (http);
+ helper->original = g_object_ref (http->priv->msg);
+ helper->cancellable = cancellable;
+ helper->callback = callback;
+ helper->user_data = user_data;
+ soup_session_queue_message (session, conditional_msg,
+ conditional_get_ready_cb,
+ helper);
+ return;
+ }
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (http),
+ callback, user_data,
+ webkit_soup_request_http_send_async);
+ httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request),
+ http->priv->msg);
+ webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ cancellable, sent_async, simple);
+}
+
+static GInputStream *
+webkit_soup_request_http_send_finish (WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+webkit_soup_request_http_get_content_length (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+ return soup_message_headers_get_content_length (http->priv->msg->response_headers);
+}
+
+static const char *
+webkit_soup_request_http_get_content_type (WebKitSoupRequest *request)
+{
+ WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+ return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
+}
+
+static void
+webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
+ WebKitSoupRequestClass *request_class =
+ WEBKIT_SOUP_REQUEST_CLASS (request_http_class);
+
+ g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate));
+
+ object_class->finalize = webkit_soup_request_http_finalize;
+
+ request_class->check_uri = webkit_soup_request_http_check_uri;
+ request_class->send = webkit_soup_request_http_send;
+ request_class->send_async = webkit_soup_request_http_send_async;
+ request_class->send_finish = webkit_soup_request_http_send_finish;
+ request_class->get_content_length = webkit_soup_request_http_get_content_length;
+ request_class->get_content_type = webkit_soup_request_http_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-http.h b/WebCore/platform/network/soup/cache/soup-request-http.h
new file mode 100644
index 0000000..a06a821
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request-http.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_HTTP_H
+#define WEBKIT_SOUP_REQUEST_HTTP_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_HTTP (webkit_soup_request_http_get_type ())
+#define WEBKIT_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTP))
+#define WEBKIT_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass))
+#define WEBKIT_IS_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP))
+#define WEBKIT_IS_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP))
+#define WEBKIT_SOUP_REQUEST_HTTP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass))
+
+typedef struct _WebKitSoupRequestHTTPPrivate WebKitSoupRequestHTTPPrivate;
+
+typedef struct {
+ WebKitSoupRequest parent;
+
+ WebKitSoupRequestHTTPPrivate *priv;
+} WebKitSoupRequestHTTP;
+
+typedef struct {
+ WebKitSoupRequestClass parent;
+} WebKitSoupRequestHTTPClass;
+
+GType webkit_soup_request_http_get_type (void);
+
+SoupMessage *webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_HTTP_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request.c b/WebCore/platform/network/soup/cache/soup-request.c
new file mode 100644
index 0000000..46b9f5a
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request.c
@@ -0,0 +1,312 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request.c: Protocol-independent streaming request interface
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "soup-request.h"
+#include "soup-requester.h"
+
+/**
+ * SECTION:soup-request
+ * @short_description: Protocol-independent streaming request interface
+ *
+ * FIXME
+ **/
+
+/**
+ * WebKitSoupRequest:
+ *
+ * FIXME
+ *
+ * Since: 2.30
+ **/
+
+static void webkit_soup_request_initable_interface_init (GInitableIface *initable_interface);
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupRequest, webkit_soup_request, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ webkit_soup_request_initable_interface_init))
+
+enum {
+ PROP_0,
+ PROP_URI,
+ PROP_SESSION
+};
+
+struct _WebKitSoupRequestPrivate {
+ SoupURI *uri;
+ SoupSession *session;
+};
+
+static void
+webkit_soup_request_init (WebKitSoupRequest *request)
+{
+ request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestPrivate);
+}
+
+static void
+webkit_soup_request_finalize (GObject *object)
+{
+ WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+ if (request->priv->uri)
+ soup_uri_free (request->priv->uri);
+ if (request->priv->session)
+ g_object_unref (request->priv->session);
+
+ G_OBJECT_CLASS (webkit_soup_request_parent_class)->finalize (object);
+}
+
+static void
+webkit_soup_request_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ if (request->priv->uri)
+ soup_uri_free (request->priv->uri);
+ request->priv->uri = g_value_dup_boxed (value);
+ break;
+ case PROP_SESSION:
+ if (request->priv->session)
+ g_object_unref (request->priv->session);
+ request->priv->session = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+webkit_soup_request_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ g_value_set_boxed (value, request->priv->uri);
+ break;
+ case PROP_SESSION:
+ g_value_set_object (value, request->priv->session);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+webkit_soup_request_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (initable);
+ gboolean ok;
+
+ if (!request->priv->uri) {
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+ _ ("No URI provided"));
+ return FALSE;
+ }
+
+ ok = WEBKIT_SOUP_REQUEST_GET_CLASS (initable)->
+ check_uri (request, request->priv->uri, error);
+
+ if (!ok && error) {
+ char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+ _ ("Invalid '%s' URI: %s"),
+ request->priv->uri->scheme,
+ uri_string);
+ g_free (uri_string);
+ }
+
+ return ok;
+}
+
+static gboolean
+webkit_soup_request_default_check_uri (WebKitSoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ return TRUE;
+}
+
+/* Default implementation: assume the sync implementation doesn't block */
+static void
+webkit_soup_request_default_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (request),
+ callback, user_data,
+ webkit_soup_request_default_send_async);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+}
+
+static GInputStream *
+webkit_soup_request_default_send_finish (WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_default_send_async), NULL);
+
+ return webkit_soup_request_send (request, NULL, error);
+}
+
+GInputStream *
+webkit_soup_request_send (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+ send (request, cancellable, error);
+}
+
+void
+webkit_soup_request_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+ send_async (request, cancellable, callback, user_data);
+}
+
+GInputStream *
+webkit_soup_request_send_finish (WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+ send_finish (request, result, error);
+}
+
+static void
+webkit_soup_request_class_init (WebKitSoupRequestClass *request_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_class);
+
+ g_type_class_add_private (request_class, sizeof (WebKitSoupRequestPrivate));
+
+ request_class->check_uri = webkit_soup_request_default_check_uri;
+ request_class->send_async = webkit_soup_request_default_send_async;
+ request_class->send_finish = webkit_soup_request_default_send_finish;
+
+ object_class->finalize = webkit_soup_request_finalize;
+ object_class->set_property = webkit_soup_request_set_property;
+ object_class->get_property = webkit_soup_request_get_property;
+
+ g_object_class_install_property (
+ object_class, PROP_URI,
+ g_param_spec_boxed (WEBKIT_SOUP_REQUEST_URI,
+ "URI",
+ "The request URI",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_SESSION,
+ g_param_spec_object (WEBKIT_SOUP_REQUEST_SESSION,
+ "Session",
+ "The request's session",
+ SOUP_TYPE_SESSION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+webkit_soup_request_initable_interface_init (GInitableIface *initable_interface)
+{
+ initable_interface->init = webkit_soup_request_initable_init;
+}
+
+SoupURI *
+webkit_soup_request_get_uri (WebKitSoupRequest *request)
+{
+ return request->priv->uri;
+}
+
+SoupSession *
+webkit_soup_request_get_session (WebKitSoupRequest *request)
+{
+ return request->priv->session;
+}
+
+goffset
+webkit_soup_request_get_content_length (WebKitSoupRequest *request)
+{
+ return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_length (request);
+}
+
+const char *
+webkit_soup_request_get_content_type (WebKitSoupRequest *request)
+{
+ return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_type (request);
+}
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+/* Copy&pasted from libsoup's soup-uri.c after applying the patch in
+ * https://bugzilla.gnome.org/show_bug.cgi?id=630540. We need this
+ * instead of soup_uri_decode() as it incorrectly returns NULL for
+ * incorrectly encoded URLs. TODO: remove this when required libsoup
+ * version is bumped out to 2.32.1
+ */
+gchar *
+webkit_soup_request_uri_decoded_copy (const char *part, int length)
+{
+ unsigned char *s, *d;
+ char *decoded = g_strndup (part, length);
+
+ s = d = (unsigned char *)decoded;
+ do {
+ if (*s == '%') {
+ if (!g_ascii_isxdigit (s[1]) ||
+ !g_ascii_isxdigit (s[2])) {
+ *d++ = *s;
+ continue;
+ }
+ *d++ = HEXCHAR (s);
+ s += 2;
+ } else
+ *d++ = *s;
+ } while (*s++);
+
+ return decoded;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request.h b/WebCore/platform/network/soup/cache/soup-request.h
new file mode 100644
index 0000000..837d8f4
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-request.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_H
+#define WEBKIT_SOUP_REQUEST_H 1
+
+#include <libsoup/soup.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST (webkit_soup_request_get_type ())
+#define WEBKIT_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequest))
+#define WEBKIT_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass))
+#define WEBKIT_IS_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUEST))
+#define WEBKIT_IS_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST))
+#define WEBKIT_SOUP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass))
+
+typedef struct _WebKitSoupRequest WebKitSoupRequest;
+typedef struct _WebKitSoupRequestPrivate WebKitSoupRequestPrivate;
+typedef struct _WebKitSoupRequestClass WebKitSoupRequestClass;
+
+struct _WebKitSoupRequest {
+ GObject parent;
+
+ WebKitSoupRequestPrivate *priv;
+};
+
+struct _WebKitSoupRequestClass {
+ GObjectClass parent;
+
+ gboolean (*check_uri)(WebKitSoupRequest *req_base,
+ SoupURI *uri,
+ GError **error);
+
+ GInputStream * (*send)(WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error);
+ void (*send_async)(WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GInputStream * (*send_finish)(WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error);
+
+ goffset (*get_content_length)(WebKitSoupRequest *request);
+ const char * (*get_content_type)(WebKitSoupRequest *request);
+};
+
+GType webkit_soup_request_get_type (void);
+
+#define WEBKIT_SOUP_REQUEST_URI "uri"
+#define WEBKIT_SOUP_REQUEST_SESSION "session"
+
+GInputStream *webkit_soup_request_send (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GError **error);
+void webkit_soup_request_send_async (WebKitSoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GInputStream *webkit_soup_request_send_finish (WebKitSoupRequest *request,
+ GAsyncResult *result,
+ GError **error);
+
+SoupURI *webkit_soup_request_get_uri (WebKitSoupRequest *request);
+SoupSession *webkit_soup_request_get_session (WebKitSoupRequest *request);
+
+goffset webkit_soup_request_get_content_length (WebKitSoupRequest *request);
+const char *webkit_soup_request_get_content_type (WebKitSoupRequest *request);
+
+/* Used by WebKitSoupRequestFile and WebKitSoupRequestData. Ideally
+ * should be located in some util file but I'll place it here as it
+ * will be removed with libsoup 2.32.1 that will ship fixed versions
+ * of soup_uri_decode/normalize
+ */
+gchar *webkit_soup_request_uri_decoded_copy (const char *part, int length);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_H */
diff --git a/WebCore/platform/network/soup/cache/soup-requester.c b/WebCore/platform/network/soup/cache/soup-requester.c
new file mode 100644
index 0000000..4d8a860
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-requester.c
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-requester.c:
+ *
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "soup-requester.h"
+
+#include "soup-request-data.h"
+#include "soup-request-file.h"
+#include "soup-request-http.h"
+#include <glib/gi18n.h>
+#include <libsoup/soup.h>
+
+struct _WebKitSoupRequesterPrivate {
+ GHashTable *request_types;
+};
+
+#define WEBKIT_SOUP_REQUESTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterPrivate))
+
+G_DEFINE_TYPE (WebKitSoupRequester, webkit_soup_requester, G_TYPE_OBJECT)
+
+static void webkit_soup_requester_init (WebKitSoupRequester *requester)
+{
+ requester->priv = WEBKIT_SOUP_REQUESTER_GET_PRIVATE (requester);
+
+ requester->priv->request_types = 0;
+}
+
+static void finalize (GObject *object)
+{
+ WebKitSoupRequester *requester = WEBKIT_SOUP_REQUESTER (object);
+
+ if (requester->priv->request_types)
+ g_hash_table_destroy (requester->priv->request_types);
+
+ G_OBJECT_CLASS (webkit_soup_requester_parent_class)->finalize (object);
+}
+
+static void webkit_soup_requester_class_init (WebKitSoupRequesterClass *requester_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (requester_class);
+
+ g_type_class_add_private (requester_class, sizeof (WebKitSoupRequesterPrivate));
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+}
+
+static void init_request_types (WebKitSoupRequesterPrivate *priv)
+{
+ if (priv->request_types)
+ return;
+
+ priv->request_types = g_hash_table_new_full (soup_str_case_hash,
+ soup_str_case_equal,
+ g_free, 0);
+ g_hash_table_insert (priv->request_types, g_strdup ("file"),
+ GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE));
+ g_hash_table_insert (priv->request_types, g_strdup ("data"),
+ GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_DATA));
+ g_hash_table_insert (priv->request_types, g_strdup ("http"),
+ GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP));
+ g_hash_table_insert (priv->request_types, g_strdup ("https"),
+ GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP));
+ g_hash_table_insert (priv->request_types, g_strdup ("ftp"),
+ GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE));
+}
+
+WebKitSoupRequester *webkit_soup_requester_new (void)
+{
+ return (WebKitSoupRequester *)g_object_new (WEBKIT_TYPE_SOUP_REQUESTER, NULL);
+}
+
+WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, const char *uriString, SoupSession *session, GError **error)
+{
+ SoupURI *uri = NULL;
+ WebKitSoupRequest *req;
+
+ uri = soup_uri_new (uriString);
+ if (!uri) {
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+ _ ("Could not parse URI '%s'"), uriString);
+ return 0;
+ }
+
+ req = webkit_soup_requester_request_uri (requester, uri, session, error);
+ soup_uri_free (uri);
+ return req;
+}
+
+WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, SoupURI *uri, SoupSession *session, GError **error)
+{
+ GType requestType;
+
+ g_return_val_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester), 0);
+
+ init_request_types (requester->priv);
+ requestType = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme));
+ if (!requestType) {
+ g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
+ _ ("Unsupported URI scheme '%s'"), uri->scheme);
+ return 0;
+ }
+
+ if (g_type_is_a (requestType, G_TYPE_INITABLE)) {
+ return (WebKitSoupRequest *)g_initable_new (requestType, 0, error,
+ "uri", uri,
+ "session", session,
+ NULL);
+ } else {
+ return (WebKitSoupRequest *)g_object_new (requestType,
+ "uri", uri,
+ "session", session,
+ NULL);
+ }
+}
+
+/* RFC 2396, 3.1 */
+static gboolean
+soup_scheme_is_valid (const char *scheme)
+{
+ if (scheme == NULL ||
+ !g_ascii_isalpha (*scheme))
+ return FALSE;
+
+ scheme++;
+ while (*scheme) {
+ if (!g_ascii_isalpha (*scheme) &&
+ !g_ascii_isdigit (*scheme) &&
+ *scheme != '+' &&
+ *scheme != '-' &&
+ *scheme != '.')
+ return FALSE;
+ scheme++;
+ }
+ return TRUE;
+}
+
+void
+webkit_soup_requester_add_protocol (WebKitSoupRequester *requester,
+ const char *scheme,
+ GType request_type)
+{
+ g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester));
+ g_return_if_fail (soup_scheme_is_valid (scheme));
+
+ init_request_types (requester->priv);
+ g_hash_table_insert (requester->priv->request_types, g_strdup (scheme),
+ GSIZE_TO_POINTER (request_type));
+}
+
+void
+webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester,
+ const char *scheme)
+{
+ g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester));
+ g_return_if_fail (soup_scheme_is_valid (scheme));
+
+ init_request_types (requester->priv);
+ g_hash_table_remove (requester->priv->request_types, scheme);
+}
+
+GQuark
+webkit_soup_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("webkit_soup_error_quark");
+ return error;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-requester.h b/WebCore/platform/network/soup/cache/soup-requester.h
new file mode 100644
index 0000000..71ff103
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/soup-requester.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUESTER_H
+#define WEBKIT_SOUP_REQUESTER_H 1
+
+#include "soup-request.h"
+#include <libsoup/soup.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUESTER (webkit_soup_requester_get_type ())
+#define WEBKIT_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequester))
+#define WEBKIT_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass))
+#define WEBKIT_IS_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER))
+#define WEBKIT_IS_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER))
+#define WEBKIT_SOUP_REQUESTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass))
+
+#define WEBKIT_SOUP_ERROR webkit_soup_error_quark ()
+
+typedef enum {
+ WEBKIT_SOUP_ERROR_BAD_URI,
+ WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME
+} WebKitSoupError;
+
+typedef struct _WebKitSoupRequester WebKitSoupRequester;
+typedef struct _WebKitSoupRequesterPrivate WebKitSoupRequesterPrivate;
+
+struct _WebKitSoupRequester {
+ GObject parent;
+
+ WebKitSoupRequesterPrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+} WebKitSoupRequesterClass;
+
+GType webkit_soup_requester_get_type (void);
+
+WebKitSoupRequester *webkit_soup_requester_new (void);
+
+GQuark webkit_soup_error_quark (void);
+
+WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester,
+ const char *uriString,
+ SoupSession *session,
+ GError **error);
+
+WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester,
+ SoupURI *uri,
+ SoupSession *session,
+ GError **error);
+
+void webkit_soup_requester_add_protocol (WebKitSoupRequester *requester,
+ const char *scheme,
+ GType request_type);
+
+void webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester,
+ const char *scheme);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUESTER_H */
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h
new file mode 100644
index 0000000..8af8de2
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache-private.h:
+ *
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_CACHE_PRIVATE_H
+#define WEBKIT_SOUP_CACHE_PRIVATE_H 1
+
+#include "soup-cache.h"
+#include <libsoup/soup-message.h>
+
+G_BEGIN_DECLS
+
+WebKitSoupCacheResponse webkit_soup_cache_has_response (WebKitSoupCache *cache,
+ SoupMessage *msg);
+GInputStream *webkit_soup_cache_send_response (WebKitSoupCache *cache,
+ SoupMessage *msg);
+WebKitSoupCacheability webkit_soup_cache_get_cacheability (WebKitSoupCache *cache,
+ SoupMessage *msg);
+SoupMessage *webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache,
+ SoupMessage *original);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_CACHE_PRIVATE_H */
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/WebCore/platform/network/soup/cache/webkit/soup-cache.c
new file mode 100644
index 0000000..73b15ba
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/webkit/soup-cache.c
@@ -0,0 +1,1653 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.c
+ *
+ * Copyright (C) 2009, 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO:
+ * - Need to hook the feature in the sync SoupSession.
+ * - Need more tests.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include <libsoup/soup.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+
+static SoupSessionFeatureInterface *webkit_soup_cache_default_feature_interface;
+static void webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+#define DEFAULT_MAX_SIZE 50 * 1024 * 1024
+#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size
+ of the cache that can be
+ filled by a single entry */
+
+typedef struct _WebKitSoupCacheEntry {
+ char *key;
+ char *filename;
+ guint freshness_lifetime;
+ gboolean must_revalidate;
+ GString *data;
+ gsize pos;
+ gsize length;
+ time_t corrected_initial_age;
+ time_t response_time;
+ gboolean writing;
+ gboolean dirty;
+ gboolean got_body;
+ gboolean being_validated;
+ SoupMessageHeaders *headers;
+ GOutputStream *stream;
+ GError *error;
+ guint hits;
+ GCancellable *cancellable;
+} WebKitSoupCacheEntry;
+
+struct _WebKitSoupCachePrivate {
+ char *cache_dir;
+ GHashTable *cache;
+ guint n_pending;
+ SoupSession *session;
+ WebKitSoupCacheType cache_type;
+ guint size;
+ guint max_size;
+ guint max_entry_data_size; /* Computed value. Here for performance reasons */
+ GList *lru_start;
+};
+
+typedef struct {
+ WebKitSoupCache *cache;
+ WebKitSoupCacheEntry *entry;
+ SoupMessage *msg;
+ gulong got_chunk_handler;
+ gulong got_body_handler;
+ gulong restarted_handler;
+} WebKitSoupCacheWritingFixture;
+
+enum {
+ PROP_0,
+ PROP_CACHE_DIR,
+ PROP_CACHE_TYPE
+};
+
+#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate))
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ webkit_soup_cache_session_feature_init))
+
+static gboolean webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry);
+static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add);
+static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add);
+
+static WebKitSoupCacheability
+get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
+{
+ WebKitSoupCacheability cacheability;
+ const char *cache_control;
+
+ /* 1. The request method must be cacheable */
+ if (msg->method == SOUP_METHOD_GET)
+ cacheability = WEBKIT_SOUP_CACHE_CACHEABLE;
+ else if (msg->method == SOUP_METHOD_HEAD ||
+ msg->method == SOUP_METHOD_TRACE ||
+ msg->method == SOUP_METHOD_CONNECT)
+ return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ else
+ return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+
+ cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control");
+ if (cache_control) {
+ GHashTable *hash;
+ WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+ hash = soup_header_parse_param_list (cache_control);
+
+ /* Shared caches MUST NOT store private resources */
+ if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
+ if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ }
+ }
+
+ /* 2. The 'no-store' cache directive does not appear in the
+ * headers
+ */
+ if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ }
+
+ /* This does not appear in section 2.1, but I think it makes
+ * sense to check it too?
+ */
+ if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ }
+ }
+
+ switch (msg->status_code) {
+ case SOUP_STATUS_PARTIAL_CONTENT:
+ /* We don't cache partial responses, but they only
+ * invalidate cached full responses if the headers
+ * don't match.
+ */
+ cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_NOT_MODIFIED:
+ /* A 304 response validates an existing cache entry */
+ cacheability = WEBKIT_SOUP_CACHE_VALIDATES;
+ break;
+
+ case SOUP_STATUS_MULTIPLE_CHOICES:
+ case SOUP_STATUS_MOVED_PERMANENTLY:
+ case SOUP_STATUS_GONE:
+ /* FIXME: cacheable unless indicated otherwise */
+ cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_FOUND:
+ case SOUP_STATUS_TEMPORARY_REDIRECT:
+ /* FIXME: cacheable if explicitly indicated */
+ cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_SEE_OTHER:
+ case SOUP_STATUS_FORBIDDEN:
+ case SOUP_STATUS_NOT_FOUND:
+ case SOUP_STATUS_METHOD_NOT_ALLOWED:
+ return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+
+ default:
+ /* Any 5xx status or any 4xx status not handled above
+ * is uncacheable but doesn't break the cache.
+ */
+ if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST &&
+ msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) ||
+ msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR)
+ return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+
+ /* An unrecognized 2xx, 3xx, or 4xx response breaks
+ * the cache.
+ */
+ if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT &&
+ msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) ||
+ (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT &&
+ msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR))
+ return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+ break;
+ }
+
+ return cacheability;
+}
+
+static void
+webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry, gboolean purge)
+{
+ if (purge) {
+ GFile *file = g_file_new_for_path (entry->filename);
+ g_file_delete (file, NULL, NULL);
+ g_object_unref (file);
+ }
+
+ g_free (entry->filename);
+ entry->filename = NULL;
+ g_free (entry->key);
+ entry->key = NULL;
+
+ if (entry->headers) {
+ soup_message_headers_free (entry->headers);
+ entry->headers = NULL;
+ }
+
+ if (entry->data) {
+ g_string_free (entry->data, TRUE);
+ entry->data = NULL;
+ }
+ if (entry->error) {
+ g_error_free (entry->error);
+ entry->error = NULL;
+ }
+ if (entry->cancellable) {
+ g_object_unref (entry->cancellable);
+ entry->cancellable = NULL;
+ }
+
+ g_slice_free (WebKitSoupCacheEntry, entry);
+}
+
+static void
+copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+ soup_message_headers_append (headers, name, value);
+}
+
+static void
+update_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+ if (soup_message_headers_get (headers, name))
+ soup_message_headers_replace (headers, name, value);
+ else
+ soup_message_headers_append (headers, name, value);
+}
+
+static guint
+webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry)
+{
+ time_t now = time (NULL);
+ time_t resident_time;
+
+ resident_time = now - entry->response_time;
+ return entry->corrected_initial_age + resident_time;
+}
+
+static gboolean
+webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, gint min_fresh)
+{
+ guint limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : (guint) min_fresh;
+ return entry->freshness_lifetime > limit;
+}
+
+static char *
+soup_message_get_cache_key (SoupMessage *msg)
+{
+ SoupURI *uri = soup_message_get_uri (msg);
+ return soup_uri_to_string (uri, FALSE);
+}
+
+static void
+webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache)
+{
+ const char *cache_control;
+ const char *expires, *date, *last_modified;
+ GHashTable *hash;
+
+ hash = NULL;
+
+ cache_control = soup_message_headers_get (entry->headers, "Cache-Control");
+ if (cache_control) {
+ const char *max_age, *s_maxage;
+ gint64 freshness_lifetime = 0;
+ WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+ hash = soup_header_parse_param_list (cache_control);
+
+ /* Should we re-validate the entry when it goes stale */
+ entry->must_revalidate = g_hash_table_lookup_extended (hash, "must-revalidate", NULL, NULL);
+
+ /* Section 2.3.1 */
+ if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
+ s_maxage = g_hash_table_lookup (hash, "s-maxage");
+ if (s_maxage) {
+ freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10);
+ if (freshness_lifetime) {
+ /* Implies proxy-revalidate. TODO: is it true? */
+ entry->must_revalidate = TRUE;
+ soup_header_free_param_list (hash);
+ return;
+ }
+ }
+ }
+
+ /* If 'max-age' cache directive is present, use that */
+ max_age = g_hash_table_lookup (hash, "max-age");
+ if (max_age)
+ freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10);
+
+ if (freshness_lifetime) {
+ entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32);
+ soup_header_free_param_list (hash);
+ return;
+ }
+ }
+
+ if (hash != NULL)
+ soup_header_free_param_list (hash);
+
+ /* If the 'Expires' response header is present, use its value
+ * minus the value of the 'Date' response header
+ */
+ expires = soup_message_headers_get (entry->headers, "Expires");
+ date = soup_message_headers_get (entry->headers, "Date");
+ if (expires && date) {
+ SoupDate *expires_d, *date_d;
+ time_t expires_t, date_t;
+
+ expires_d = soup_date_new_from_string (expires);
+ if (expires_d) {
+ date_d = soup_date_new_from_string (date);
+
+ expires_t = soup_date_to_time_t (expires_d);
+ date_t = soup_date_to_time_t (date_d);
+
+ soup_date_free (expires_d);
+ soup_date_free (date_d);
+
+ if (expires_t && date_t) {
+ entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0);
+ return;
+ }
+ } else {
+ /* If Expires is not a valid date we should
+ treat it as already expired, see section
+ 3.3 */
+ entry->freshness_lifetime = 0;
+ return;
+ }
+ }
+
+ /* Otherwise an heuristic may be used */
+
+ /* Heuristics MUST NOT be used with these status codes
+ (section 2.3.1.1) */
+ if (msg->status_code != SOUP_STATUS_OK &&
+ msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE &&
+ msg->status_code != SOUP_STATUS_PARTIAL_CONTENT &&
+ msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES &&
+ msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY &&
+ msg->status_code != SOUP_STATUS_GONE)
+ goto expire;
+
+ /* TODO: attach warning 113 if response's current_age is more
+ than 24h (section 2.3.1.1) when using heuristics */
+
+ /* Last-Modified based heuristic */
+ last_modified = soup_message_headers_get (entry->headers, "Last-Modified");
+ if (last_modified) {
+ SoupDate *soup_date;
+ time_t now, last_modified_t;
+
+ soup_date = soup_date_new_from_string (last_modified);
+ last_modified_t = soup_date_to_time_t (soup_date);
+ now = time (NULL);
+
+#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */
+
+ entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR);
+ soup_date_free (soup_date);
+ }
+
+ return;
+
+ expire:
+ /* If all else fails, make the entry expire immediately */
+ entry->freshness_lifetime = 0;
+}
+
+static WebKitSoupCacheEntry *
+webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time)
+{
+ WebKitSoupCacheEntry *entry;
+ SoupMessageHeaders *headers;
+ const char *date;
+ char *md5;
+
+ entry = g_slice_new0 (WebKitSoupCacheEntry);
+ entry->dirty = FALSE;
+ entry->writing = FALSE;
+ entry->got_body = FALSE;
+ entry->being_validated = FALSE;
+ entry->data = g_string_new (NULL);
+ entry->pos = 0;
+ entry->error = NULL;
+
+ /* key & filename */
+ entry->key = soup_message_get_cache_key (msg);
+ md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1);
+ entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL);
+ g_free (md5);
+
+ /* Headers */
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ soup_message_headers_foreach (msg->response_headers,
+ (SoupMessageHeadersForeachFunc)copy_headers,
+ headers);
+ entry->headers = headers;
+
+ /* LRU list */
+ entry->hits = 0;
+
+ /* Section 2.3.1, Freshness Lifetime */
+ webkit_soup_cache_entry_set_freshness (entry, msg, cache);
+
+ /* Section 2.3.2, Calculating Age */
+ date = soup_message_headers_get (entry->headers, "Date");
+
+ if (date) {
+ SoupDate *soup_date;
+ const char *age;
+ time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0;
+
+ soup_date = soup_date_new_from_string (date);
+ date_value = soup_date_to_time_t (soup_date);
+ soup_date_free (soup_date);
+
+ age = soup_message_headers_get (entry->headers, "Age");
+ if (age)
+ age_value = g_ascii_strtoll (age, NULL, 10);
+
+ entry->response_time = response_time;
+ apparent_age = MAX (0, entry->response_time - date_value);
+ corrected_received_age = MAX (apparent_age, age_value);
+ response_delay = entry->response_time - request_time;
+ entry->corrected_initial_age = corrected_received_age + response_delay;
+ } else {
+ /* Is this correct ? */
+ entry->corrected_initial_age = time (NULL);
+ }
+
+ return entry;
+}
+
+static void
+webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture)
+{
+ /* Free fixture. And disconnect signals, we don't want to
+ listen to more SoupMessage events as we're finished with
+ this resource */
+ if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
+ if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
+ if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
+ g_object_unref (fixture->msg);
+ g_object_unref (fixture->cache);
+ g_slice_free (WebKitSoupCacheWritingFixture, fixture);
+}
+
+static void
+close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+ WebKitSoupCacheEntry *entry = fixture->entry;
+ WebKitSoupCache *cache = fixture->cache;
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ goffset content_length;
+
+ g_warn_if_fail (entry->error == NULL);
+
+ /* FIXME: what do we do on error ? */
+
+ if (stream) {
+ g_output_stream_close_finish (stream, result, NULL);
+ g_object_unref (stream);
+ }
+ entry->stream = NULL;
+
+ content_length = soup_message_headers_get_content_length (entry->headers);
+
+ /* If the process was cancelled, then delete the entry from
+ the cache. Do it also if the size of a chunked resource is
+ too much for the cache */
+ if (g_cancellable_is_cancelled (entry->cancellable)) {
+ entry->dirty = FALSE;
+ webkit_soup_cache_entry_remove (cache, entry);
+ webkit_soup_cache_entry_free (entry, TRUE);
+ entry = NULL;
+ } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
+ entry->length != (gsize) content_length) {
+ /** Two options here:
+ *
+ * 1. "chunked" data, entry was temporarily added to
+ * cache (as content-length is 0) and now that we have
+ * the actual size we have to evaluate if we want it
+ * in the cache or not
+ *
+ * 2. Content-Length has a different value than actual
+ * length, means that the content was encoded for
+ * transmission (typically compressed) and thus we
+ * have to substract the content-length value that was
+ * added to the cache and add the unencoded length
+ **/
+ gint length_to_add = entry->length - content_length;
+
+ /* Make room in cache if needed */
+ if (cache_accepts_entries_of_size (cache, length_to_add)) {
+ make_room_for_new_entry (cache, length_to_add);
+
+ cache->priv->size += length_to_add;
+ } else {
+ entry->dirty = FALSE;
+ webkit_soup_cache_entry_remove (cache, entry);
+ webkit_soup_cache_entry_free (entry, TRUE);
+ entry = NULL;
+ }
+ }
+
+ if (entry) {
+ /* Get rid of the GString in memory for the resource now */
+ if (entry->data) {
+ g_string_free (entry->data, TRUE);
+ entry->data = NULL;
+ }
+
+ entry->dirty = FALSE;
+ entry->writing = FALSE;
+ entry->got_body = FALSE;
+ entry->pos = 0;
+
+ g_object_unref (entry->cancellable);
+ entry->cancellable = NULL;
+ }
+
+ cache->priv->n_pending--;
+
+ /* Frees */
+ webkit_soup_cache_writing_fixture_free (fixture);
+}
+
+static void
+write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ GError *error = NULL;
+ gssize write_size;
+ WebKitSoupCacheEntry *entry = fixture->entry;
+
+ if (g_cancellable_is_cancelled (entry->cancellable)) {
+ g_output_stream_close_async (stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ return;
+ }
+
+ write_size = g_output_stream_write_finish (stream, result, &error);
+ if (write_size <= 0 || error) {
+ if (error)
+ entry->error = error;
+ g_output_stream_close_async (stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ /* FIXME: We should completely stop caching the
+ resource at this point */
+ } else {
+ entry->pos += write_size;
+
+ /* Are we still writing and is there new data to write
+ already ? */
+ if (entry->data && entry->pos < entry->data->len) {
+ g_output_stream_write_async (entry->stream,
+ entry->data->str + entry->pos,
+ entry->data->len - entry->pos,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)write_ready_cb,
+ fixture);
+ } else {
+ entry->writing = FALSE;
+
+ if (entry->got_body) {
+ /* If we already received 'got-body'
+ and we have written all the data,
+ we can close the stream */
+ g_output_stream_close_async (entry->stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ }
+ }
+ }
+}
+
+static void
+msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture)
+{
+ WebKitSoupCacheEntry *entry = fixture->entry;
+
+ g_return_if_fail (chunk->data && chunk->length);
+ g_return_if_fail (entry);
+
+ /* Ignore this if the writing or appending was cancelled */
+ if (!g_cancellable_is_cancelled (entry->cancellable)) {
+ g_string_append_len (entry->data, chunk->data, chunk->length);
+ entry->length = entry->data->len;
+
+ if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
+ /* Quickly cancel the caching of the resource */
+ g_cancellable_cancel (entry->cancellable);
+ }
+ }
+
+ /* FIXME: remove the error check when we cancel the caching at
+ the first write error */
+ /* Only write if the entry stream is ready */
+ if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
+ GString *data = entry->data;
+ entry->writing = TRUE;
+ g_output_stream_write_async (entry->stream,
+ data->str + entry->pos,
+ data->len - entry->pos,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)write_ready_cb,
+ fixture);
+ }
+}
+
+static void
+msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture)
+{
+ WebKitSoupCacheEntry *entry = fixture->entry;
+ g_return_if_fail (entry);
+
+ entry->got_body = TRUE;
+
+ if (!entry->stream && entry->pos != entry->length)
+ /* The stream is not ready to be written but we still
+ have data to write, we'll write it when the stream
+ is opened for writing */
+ return;
+
+
+ if (entry->pos != entry->length) {
+ /* If we still have data to write, write it,
+ write_ready_cb will close the stream */
+ if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
+ g_output_stream_write_async (entry->stream,
+ entry->data->str + entry->pos,
+ entry->data->len - entry->pos,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)write_ready_cb,
+ fixture);
+ }
+ return;
+ }
+
+ if (entry->stream && !entry->writing)
+ g_output_stream_close_async (entry->stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+}
+
+static gboolean
+webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry)
+{
+ GList *lru_item;
+
+ /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */
+ if (entry->dirty) {
+ g_cancellable_cancel (entry->cancellable);
+ return FALSE;
+ }
+
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ /* Remove from cache */
+ if (!g_hash_table_remove (cache->priv->cache, entry->key))
+ return FALSE;
+
+ /* Remove from LRU */
+ lru_item = g_list_find (cache->priv->lru_start, entry);
+ cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item);
+
+ /* Adjust cache size */
+ cache->priv->size -= entry->length;
+
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ return TRUE;
+}
+
+static gint
+lru_compare_func (gconstpointer a, gconstpointer b)
+{
+ WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a;
+ WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b;
+
+ /** The rationale of this sorting func is
+ *
+ * 1. sort by hits -> LRU algorithm, then
+ *
+ * 2. sort by freshness lifetime, we better discard first
+ * entries that are close to expire
+ *
+ * 3. sort by size, replace first small size resources as they
+ * are cheaper to download
+ **/
+
+ /* Sort by hits */
+ if (entry_a->hits != entry_b->hits)
+ return entry_a->hits - entry_b->hits;
+
+ /* Sort by freshness_lifetime */
+ if (entry_a->freshness_lifetime != entry_b->freshness_lifetime)
+ return entry_a->freshness_lifetime - entry_b->freshness_lifetime;
+
+ /* Sort by size */
+ return entry_a->length - entry_b->length;
+}
+
+static gboolean
+cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add)
+{
+ /* We could add here some more heuristics. TODO: review how
+ this is done by other HTTP caches */
+
+ return length_to_add <= cache->priv->max_entry_data_size;
+}
+
+static void
+make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add)
+{
+ GList *lru_entry = cache->priv->lru_start;
+
+ /* Check that there is enough room for the new entry. This is
+ an approximation as we're not working out the size of the
+ cache file or the size of the headers for performance
+ reasons. TODO: check if that would be really that expensive */
+
+ while (lru_entry &&
+ (length_to_add + cache->priv->size > cache->priv->max_size)) {
+ WebKitSoupCacheEntry *old_entry = (WebKitSoupCacheEntry *)lru_entry->data;
+
+ /* Discard entries. Once cancelled resources will be
+ * freed in close_ready_cb
+ */
+ if (webkit_soup_cache_entry_remove (cache, old_entry)) {
+ webkit_soup_cache_entry_free (old_entry, TRUE);
+ lru_entry = cache->priv->lru_start;
+ } else
+ lru_entry = g_list_next (lru_entry);
+ }
+}
+
+static gboolean
+webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache,
+ const char *key,
+ WebKitSoupCacheEntry *entry,
+ gboolean sort)
+{
+ guint length_to_add = 0;
+
+ if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
+ length_to_add = soup_message_headers_get_content_length (entry->headers);
+
+ /* Check if we are going to store the resource depending on its size */
+ if (length_to_add) {
+ if (!cache_accepts_entries_of_size (cache, length_to_add))
+ return FALSE;
+
+ /* Make room for new entry if needed */
+ make_room_for_new_entry (cache, length_to_add);
+ }
+
+ g_hash_table_insert (cache->priv->cache, g_strdup (key), entry);
+
+ /* Compute new cache size */
+ cache->priv->size += length_to_add;
+
+ /* Update LRU */
+ if (sort)
+ cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func);
+ else
+ cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry);
+
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ return TRUE;
+}
+
+static void
+msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry)
+{
+ /* FIXME: What should we do here exactly? */
+}
+
+static void
+append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+ GFile *file = (GFile *)source;
+ GOutputStream *stream;
+ WebKitSoupCacheEntry *entry = fixture->entry;
+
+ stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error);
+
+ if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
+ fixture->cache->priv->n_pending--;
+ entry->dirty = FALSE;
+ webkit_soup_cache_entry_remove (fixture->cache, entry);
+ webkit_soup_cache_entry_free (entry, TRUE);
+ webkit_soup_cache_writing_fixture_free (fixture);
+ return;
+ }
+
+ entry->stream = g_object_ref (stream);
+ g_object_unref (file);
+
+ /* If we already got all the data we have to initiate the
+ writing here, since we won't get more 'got-chunk'
+ signals */
+ if (entry->got_body) {
+ GString *data = entry->data;
+
+ /* It could happen that reading the data from server
+ was completed before this happens. In that case
+ there is no data */
+ if (data) {
+ entry->writing = TRUE;
+ g_output_stream_write_async (entry->stream,
+ data->str + entry->pos,
+ data->len - entry->pos,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)write_ready_cb,
+ fixture);
+ }
+ }
+}
+
+typedef struct {
+ time_t request_time;
+ SoupSessionFeature *feature;
+ gulong got_headers_handler;
+} RequestHelper;
+
+static void
+msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
+{
+ WebKitSoupCache *cache;
+ WebKitSoupCacheability cacheable;
+ RequestHelper *helper;
+ time_t request_time, response_time;
+
+ response_time = time (NULL);
+
+ helper = (RequestHelper *)user_data;
+ cache = WEBKIT_SOUP_CACHE (helper->feature);
+ request_time = helper->request_time;
+ g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
+ g_slice_free (RequestHelper, helper);
+
+ cacheable = webkit_soup_cache_get_cacheability (cache, msg);
+
+ if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) {
+ WebKitSoupCacheEntry *entry;
+ char *key;
+ GFile *file;
+ WebKitSoupCacheWritingFixture *fixture;
+
+ /* Check if we are already caching this resource */
+ key = soup_message_get_cache_key (msg);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+ g_free (key);
+
+ if (entry && entry->dirty)
+ return;
+
+ /* Create a new entry, deleting any old one if present */
+ if (entry) {
+ webkit_soup_cache_entry_remove (cache, entry);
+ webkit_soup_cache_entry_free (entry, TRUE);
+ }
+
+ entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time);
+ entry->hits = 1;
+
+ /* Do not continue if it can not be stored */
+ if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, TRUE)) {
+ webkit_soup_cache_entry_free (entry, TRUE);
+ return;
+ }
+
+ fixture = g_slice_new0 (WebKitSoupCacheWritingFixture);
+ fixture->cache = g_object_ref (cache);
+ fixture->entry = entry;
+ fixture->msg = g_object_ref (msg);
+
+ /* We connect now to these signals and buffer the data
+ if it comes before the file is ready for writing */
+ fixture->got_chunk_handler =
+ g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
+ fixture->got_body_handler =
+ g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
+ fixture->restarted_handler =
+ g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
+
+ /* Prepare entry */
+ file = g_file_new_for_path (entry->filename);
+ cache->priv->n_pending++;
+
+ entry->dirty = TRUE;
+ entry->cancellable = g_cancellable_new ();
+ g_file_append_to_async (file, 0,
+ G_PRIORITY_LOW, entry->cancellable,
+ (GAsyncReadyCallback)append_to_ready_cb,
+ fixture);
+ } else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) {
+ char *key;
+ WebKitSoupCacheEntry *entry;
+
+ key = soup_message_get_cache_key (msg);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+ g_free (key);
+
+ if (entry) {
+ if (webkit_soup_cache_entry_remove (cache, entry))
+ webkit_soup_cache_entry_free (entry, TRUE);
+ }
+ } else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) {
+ char *key;
+ WebKitSoupCacheEntry *entry;
+
+ key = soup_message_get_cache_key (msg);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+ g_free (key);
+
+ g_return_if_fail (entry);
+
+ entry->being_validated = FALSE;
+
+ /* We update the headers of the existing cache item,
+ plus its age */
+ soup_message_headers_foreach (msg->response_headers,
+ (SoupMessageHeadersForeachFunc)update_headers,
+ entry->headers);
+ webkit_soup_cache_entry_set_freshness (entry, msg, cache);
+ }
+}
+
+GInputStream *
+webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg)
+{
+ char *key;
+ WebKitSoupCacheEntry *entry;
+ char *current_age;
+ GInputStream *stream = NULL;
+ GFile *file;
+
+ g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ key = soup_message_get_cache_key (msg);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+ g_return_val_if_fail (entry, NULL);
+
+ /* If we are told to send a response from cache any validation
+ in course is over by now */
+ entry->being_validated = FALSE;
+
+ /* Headers */
+ soup_message_headers_foreach (entry->headers,
+ (SoupMessageHeadersForeachFunc)update_headers,
+ msg->response_headers);
+
+ /* Add 'Age' header with the current age */
+ current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry));
+ soup_message_headers_replace (msg->response_headers,
+ "Age",
+ current_age);
+ g_free (current_age);
+
+ /* TODO: the original idea was to save reads, but current code
+ assumes that a stream is always returned. Need to reach
+ some agreement here. Also we have to handle the situation
+ were the file was no longer there (for example files
+ removed without notifying the cache */
+ file = g_file_new_for_path (entry->filename);
+ stream = (GInputStream *)g_file_read (file, NULL, NULL);
+
+ return stream;
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ RequestHelper *helper = g_slice_new0 (RequestHelper);
+ helper->request_time = time (NULL);
+ helper->feature = feature;
+ helper->got_headers_handler = g_signal_connect (msg, "got-headers",
+ G_CALLBACK (msg_got_headers_cb),
+ helper);
+}
+
+static void
+attach (SoupSessionFeature *feature, SoupSession *session)
+{
+ WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature);
+ cache->priv->session = session;
+
+ webkit_soup_cache_default_feature_interface->attach (feature, session);
+}
+
+static void
+webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ webkit_soup_cache_default_feature_interface =
+ g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+ feature_interface->attach = attach;
+ feature_interface->request_started = request_started;
+}
+
+static void
+webkit_soup_cache_init (WebKitSoupCache *cache)
+{
+ WebKitSoupCachePrivate *priv;
+
+ priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+ priv->cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ NULL);
+
+ /* LRU */
+ priv->lru_start = NULL;
+
+ /* */
+ priv->n_pending = 0;
+
+ /* Cache size */
+ priv->max_size = DEFAULT_MAX_SIZE;
+ priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+ priv->size = 0;
+}
+
+static void
+remove_cache_item (gpointer key,
+ gpointer value,
+ WebKitSoupCache *cache)
+{
+ WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
+ if (webkit_soup_cache_entry_remove (cache, entry))
+ webkit_soup_cache_entry_free (entry, FALSE);
+}
+
+static void
+webkit_soup_cache_finalize (GObject *object)
+{
+ WebKitSoupCachePrivate *priv;
+
+ priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+ g_hash_table_foreach (priv->cache, (GHFunc)remove_cache_item, object);
+ g_hash_table_destroy (priv->cache);
+ g_free (priv->cache_dir);
+
+ g_list_free (priv->lru_start);
+ priv->lru_start = NULL;
+
+ G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object);
+}
+
+static void
+webkit_soup_cache_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+ switch (prop_id) {
+ case PROP_CACHE_DIR:
+ priv->cache_dir = g_value_dup_string (value);
+ /* Create directory if it does not exist (FIXME: should we?) */
+ if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+ g_mkdir_with_parents (priv->cache_dir, 0700);
+ break;
+ case PROP_CACHE_TYPE:
+ priv->cache_type = g_value_get_enum (value);
+ /* TODO: clear private entries and issue a warning if moving to shared? */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+webkit_soup_cache_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+ switch (prop_id) {
+ case PROP_CACHE_DIR:
+ g_value_set_string (value, priv->cache_dir);
+ break;
+ case PROP_CACHE_TYPE:
+ g_value_set_enum (value, priv->cache_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+webkit_soup_cache_constructed (GObject *object)
+{
+ WebKitSoupCachePrivate *priv;
+
+ priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+ if (!priv->cache_dir) {
+ /* Set a default cache dir, different for each user */
+ priv->cache_dir = g_build_filename (g_get_user_cache_dir (),
+ "httpcache",
+ NULL);
+ if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+ g_mkdir_with_parents (priv->cache_dir, 0700);
+ }
+
+ if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed)
+ G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object);
+}
+
+#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ())
+static GType
+webkit_soup_cache_type_get_type (void)
+{
+ static GType cache_type = 0;
+
+ static const GEnumValue cache_types[] = {
+ { WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" },
+ { WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" },
+ { 0, NULL, NULL }
+ };
+
+ if (!cache_type) {
+ cache_type = g_enum_register_static ("WebKitSoupCacheTypeType", cache_types);
+ }
+ return cache_type;
+}
+
+static void
+webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class)
+{
+ GObjectClass *gobject_class = (GObjectClass *)cache_class;
+
+ gobject_class->finalize = webkit_soup_cache_finalize;
+ gobject_class->constructed = webkit_soup_cache_constructed;
+ gobject_class->set_property = webkit_soup_cache_set_property;
+ gobject_class->get_property = webkit_soup_cache_get_property;
+
+ cache_class->get_cacheability = get_cacheability;
+
+ g_object_class_install_property (gobject_class, PROP_CACHE_DIR,
+ g_param_spec_string ("cache-dir",
+ "Cache directory",
+ "The directory to store the cache files",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_CACHE_TYPE,
+ g_param_spec_enum ("cache-type",
+ "Cache type",
+ "Whether the cache is private or shared",
+ WEBKIT_SOUP_CACHE_TYPE_TYPE,
+ WEBKIT_SOUP_CACHE_SINGLE_USER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate));
+}
+
+/**
+ * webkit_soup_cache_new:
+ * @cache_dir: the directory to store the cached data, or %NULL to use the default one
+ * @cache_type: the #WebKitSoupCacheType of the cache
+ *
+ * Creates a new #WebKitSoupCache.
+ *
+ * Returns: a new #WebKitSoupCache
+ *
+ * Since: 2.28
+ **/
+WebKitSoupCache *
+webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type)
+{
+ return g_object_new (WEBKIT_TYPE_SOUP_CACHE,
+ "cache-dir", cache_dir,
+ "cache-type", cache_type,
+ NULL);
+}
+
+/**
+ * webkit_soup_cache_has_response:
+ * @cache: a #WebKitSoupCache
+ * @msg: a #SoupMessage
+ *
+ * This function calculates whether the @cache object has a proper
+ * response for the request @msg given the flags both in the request
+ * and the cached reply and the time ellapsed since it was cached.
+ *
+ * Returns: whether or not the @cache has a valid response for @msg
+ **/
+WebKitSoupCacheResponse
+webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg)
+{
+ char *key;
+ WebKitSoupCacheEntry *entry;
+ const char *cache_control;
+ GHashTable *hash;
+ gpointer value;
+ gboolean must_revalidate;
+ int max_age, max_stale, min_fresh;
+ GList *lru_item, *item;
+
+ key = soup_message_get_cache_key (msg);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+
+ /* 1. The presented Request-URI and that of stored response
+ * match
+ */
+ if (!entry)
+ return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+ /* Increase hit count. Take sorting into account */
+ entry->hits++;
+ lru_item = g_list_find (cache->priv->lru_start, entry);
+ item = lru_item;
+ while (item->next && lru_compare_func (item->data, item->next->data) > 0)
+ item = g_list_next (item);
+
+ if (item != lru_item) {
+ cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item);
+ item = g_list_insert_sorted (item, lru_item->data, lru_compare_func);
+ g_list_free (lru_item);
+ }
+
+ if (entry->dirty || entry->being_validated)
+ return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+ /* 2. The request method associated with the stored response
+ * allows it to be used for the presented request
+ */
+
+ /* In practice this means we only return our resource for GET,
+ * cacheability for other methods is a TODO in the RFC
+ * (TODO: although we could return the headers for HEAD
+ * probably).
+ */
+ if (msg->method != SOUP_METHOD_GET)
+ return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+ /* 3. Selecting request-headers nominated by the stored
+ * response (if any) match those presented.
+ */
+
+ /* TODO */
+
+ /* 4. The presented request and stored response are free from
+ * directives that would prevent its use.
+ */
+
+ must_revalidate = FALSE;
+ max_age = max_stale = min_fresh = -1;
+
+ cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control");
+ if (cache_control) {
+ hash = soup_header_parse_param_list (cache_control);
+
+ if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+ g_hash_table_destroy (hash);
+ return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+ }
+
+ if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+ entry->must_revalidate = TRUE;
+ }
+
+ if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) {
+ max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+ }
+
+ /* max-stale can have no value set, we need to use _extended */
+ if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) {
+ if (value)
+ max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+ else
+ max_stale = G_MAXINT32;
+ }
+
+ value = g_hash_table_lookup (hash, "min-fresh");
+ if (value)
+ min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+
+ g_hash_table_destroy (hash);
+
+ if (max_age != -1) {
+ guint current_age = webkit_soup_cache_entry_get_current_age (entry);
+
+ /* If we are over max-age and max-stale is not
+ set, do not use the value from the cache
+ without validation */
+ if ((guint) max_age <= current_age && max_stale == -1)
+ return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+ }
+ }
+
+ /* 5. The stored response is either: fresh, allowed to be
+ * served stale or succesfully validated
+ */
+ /* TODO consider also proxy-revalidate & s-maxage */
+ if (entry->must_revalidate)
+ return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+
+ if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
+ /* Not fresh, can it be served stale? */
+ if (max_stale != -1) {
+ /* G_MAXINT32 means we accept any staleness */
+ if (max_stale == G_MAXINT32)
+ return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+
+ if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= (guint) max_stale)
+ return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+ }
+
+ return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+ }
+
+ return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+}
+
+/**
+ * webkit_soup_cache_get_cacheability:
+ * @cache: a #WebKitSoupCache
+ * @msg: a #SoupMessage
+ *
+ * Calculates whether the @msg can be cached or not.
+ *
+ * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not.
+ **/
+WebKitSoupCacheability
+webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
+{
+ g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE);
+
+ return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg);
+}
+
+static gboolean
+force_flush_timeout (gpointer data)
+{
+ gboolean *forced = (gboolean *)data;
+ *forced = TRUE;
+
+ return FALSE;
+}
+
+/**
+ * webkit_soup_cache_flush:
+ * @cache: a #WebKitSoupCache
+ * @session: the #SoupSession associated with the @cache
+ *
+ * This function will force all pending writes in the @cache to be
+ * committed to disk. For doing so it will iterate the #GMainContext
+ * associated with the @session (which can be the default one) as long
+ * as needed.
+ **/
+void
+webkit_soup_cache_flush (WebKitSoupCache *cache)
+{
+ GMainContext *async_context;
+ SoupSession *session;
+ guint timeout_id;
+ gboolean forced = FALSE;
+
+ g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
+
+ session = cache->priv->session;
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ async_context = soup_session_get_async_context (session);
+
+ /* We give cache 10 secs to finish */
+ timeout_id = g_timeout_add (10000, force_flush_timeout, &forced);
+
+ while (!forced && cache->priv->n_pending > 0)
+ g_main_context_iteration (async_context, FALSE);
+
+ if (!forced)
+ g_source_remove (timeout_id);
+ else
+ g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending);
+}
+
+static void
+clear_cache_item (gpointer key,
+ gpointer value,
+ WebKitSoupCache *cache)
+{
+ WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
+ if (webkit_soup_cache_entry_remove (cache, entry))
+ webkit_soup_cache_entry_free (entry, TRUE);
+}
+
+/**
+ * webkit_soup_cache_clear:
+ * @cache: a #WebKitSoupCache
+ *
+ * Will remove all entries in the @cache plus all the cache files
+ * associated with them.
+ **/
+void
+webkit_soup_cache_clear (WebKitSoupCache *cache)
+{
+ GHashTable *hash;
+
+ g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
+
+ hash = cache->priv->cache;
+ g_return_if_fail (hash);
+
+ g_hash_table_foreach (hash, (GHFunc)clear_cache_item, cache);
+}
+
+SoupMessage *
+webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original)
+{
+ SoupMessage *msg;
+ SoupURI *uri;
+ WebKitSoupCacheEntry *entry;
+ char *key;
+ const char *value;
+
+ g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
+
+ /* First copy the data we need from the original message */
+ uri = soup_message_get_uri (original);
+ msg = soup_message_new_from_uri (original->method, uri);
+
+ soup_message_headers_foreach (original->request_headers,
+ (SoupMessageHeadersForeachFunc)copy_headers,
+ msg->request_headers);
+
+ /* Now add the validator entries in the header from the cached
+ data */
+ key = soup_message_get_cache_key (original);
+ entry = g_hash_table_lookup (cache->priv->cache, key);
+ g_free (key);
+
+ g_return_val_if_fail (entry, NULL);
+
+ entry->being_validated = TRUE;
+
+ value = soup_message_headers_get (entry->headers, "Last-Modified");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "If-Modified-Since",
+ value);
+ value = soup_message_headers_get (entry->headers, "ETag");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "If-None-Match",
+ value);
+ return msg;
+}
+
+#define WEBKIT_SOUP_CACHE_FILE "soup.cache"
+
+#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}"
+#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")"
+#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT
+
+/* Basically the same format than above except that some strings are
+ prepended with &. This way the GVariant returns a pointer to the
+ data instead of duplicating the string */
+#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
+
+static void
+pack_entry (gpointer data,
+ gpointer user_data)
+{
+ WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data;
+ SoupMessageHeadersIter iter;
+ const gchar *header_key, *header_value;
+ GVariantBuilder *headers_builder;
+ GVariantBuilder *entries_builder = (GVariantBuilder *)user_data;
+
+ /* Do not store non-consolidated entries */
+ if (entry->dirty || entry->writing || !entry->key)
+ return;
+
+ /* Pack headers */
+ headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+ soup_message_headers_iter_init (&iter, entry->headers);
+ while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) {
+ if (g_utf8_validate (header_value, -1, NULL))
+ g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT,
+ header_key, header_value);
+ }
+
+ /* Entry data */
+ g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
+ entry->key, entry->filename, entry->must_revalidate,
+ entry->freshness_lifetime, entry->corrected_initial_age,
+ entry->response_time, entry->hits, entry->length, headers_builder);
+
+ g_variant_builder_unref (headers_builder);
+}
+
+void
+webkit_soup_cache_dump (WebKitSoupCache *cache)
+{
+ WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+ gchar *filename;
+ GVariantBuilder *entries_builder;
+ GVariant *cache_variant;
+
+ if (!g_list_length (cache->priv->lru_start))
+ return;
+
+ /* Create the builder and iterate over all entries */
+ entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+ g_list_foreach (cache->priv->lru_start, pack_entry, entries_builder);
+
+ /* Serialize and dump */
+ cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder);
+ g_variant_builder_unref (entries_builder);
+
+ filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
+ g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant),
+ g_variant_get_size (cache_variant), NULL);
+ g_free (filename);
+ g_variant_unref (cache_variant);
+}
+
+void
+webkit_soup_cache_load (WebKitSoupCache *cache)
+{
+ gchar *filename = NULL, *contents = NULL;
+ GVariant *cache_variant;
+ GVariantIter *entries_iter, *headers_iter;
+ GVariantType *variant_format;
+ gsize length;
+ WebKitSoupCacheEntry *entry;
+ WebKitSoupCachePrivate *priv = cache->priv;
+
+ filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
+ if (!g_file_get_contents (filename, &contents, &length, NULL)) {
+ g_free (filename);
+ return;
+ }
+ g_free (filename);
+
+ variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT);
+ cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, NULL, NULL);
+ g_variant_type_free (variant_format);
+
+ g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter);
+ entry = g_slice_new0 (WebKitSoupCacheEntry);
+
+ while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
+ &entry->key, &entry->filename, &entry->must_revalidate,
+ &entry->freshness_lifetime, &entry->corrected_initial_age,
+ &entry->response_time, &entry->hits, &entry->length,
+ &headers_iter)) {
+ const gchar *header_key, *header_value;
+
+ /* SoupMessage Headers */
+ entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value))
+ soup_message_headers_append (entry->headers, header_key, header_value);
+
+ /* Insert in cache */
+ if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, FALSE))
+ webkit_soup_cache_entry_free (entry, TRUE);
+
+ /* New entry for the next iteration. This creates an
+ extra object the last iteration but it's worth it
+ as we save several if's */
+ entry = g_slice_new0 (WebKitSoupCacheEntry);
+ }
+ /* Remove last created entry */
+ g_slice_free (WebKitSoupCacheEntry, entry);
+
+ /* Sort LRU (shouldn't be needed). First reverse as elements
+ * are always prepended when inserting
+ */
+ cache->priv->lru_start = g_list_reverse (cache->priv->lru_start);
+ cache->priv->lru_start = g_list_sort (cache->priv->lru_start, lru_compare_func);
+
+ /* frees */
+ g_variant_iter_free (entries_iter);
+ g_variant_unref (cache_variant);
+}
+
+void
+webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
+ guint max_size)
+{
+ cache->priv->max_size = max_size;
+ cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+}
+
+guint
+webkit_soup_cache_get_max_size (WebKitSoupCache *cache)
+{
+ return cache->priv->max_size;
+}
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/WebCore/platform/network/soup/cache/webkit/soup-cache.h
new file mode 100644
index 0000000..e447cfc
--- /dev/null
+++ b/WebCore/platform/network/soup/cache/webkit/soup-cache.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.h:
+ *
+ * Copyright (C) 2009, 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_CACHE_H
+#define WEBKIT_SOUP_CACHE_H 1
+
+#if BUILDING_GTK__
+#include <webkit/webkitdefines.h>
+#else
+#ifndef WEBKIT_API
+#define WEBKIT_API
+#endif
+#endif
+
+#include <libsoup/soup-types.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_CACHE (webkit_soup_cache_get_type ())
+#define WEBKIT_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCache))
+#define WEBKIT_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
+#define WEBKIT_IS_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
+#define WEBKIT_IS_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
+#define WEBKIT_SOUP_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
+
+typedef struct _WebKitSoupCache WebKitSoupCache;
+typedef struct _WebKitSoupCachePrivate WebKitSoupCachePrivate;
+
+typedef enum {
+ WEBKIT_SOUP_CACHE_CACHEABLE = (1 << 0),
+ WEBKIT_SOUP_CACHE_UNCACHEABLE = (1 << 1),
+ WEBKIT_SOUP_CACHE_INVALIDATES = (1 << 2),
+ WEBKIT_SOUP_CACHE_VALIDATES = (1 << 3)
+} WebKitSoupCacheability;
+
+typedef enum {
+ WEBKIT_SOUP_CACHE_RESPONSE_FRESH,
+ WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION,
+ WEBKIT_SOUP_CACHE_RESPONSE_STALE
+} WebKitSoupCacheResponse;
+
+typedef enum {
+ WEBKIT_SOUP_CACHE_SINGLE_USER,
+ WEBKIT_SOUP_CACHE_SHARED
+} WebKitSoupCacheType;
+
+struct _WebKitSoupCache {
+ GObject parent_instance;
+
+ WebKitSoupCachePrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* methods */
+ WebKitSoupCacheability (*get_cacheability)(WebKitSoupCache *cache, SoupMessage *msg);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1)(void);
+ void (*_libsoup_reserved2)(void);
+ void (*_libsoup_reserved3)(void);
+} WebKitSoupCacheClass;
+
+WEBKIT_API GType webkit_soup_cache_get_type (void);
+WEBKIT_API WebKitSoupCache *webkit_soup_cache_new (const char *cache_dir,
+ WebKitSoupCacheType cache_type);
+WEBKIT_API void webkit_soup_cache_flush (WebKitSoupCache *cache);
+WEBKIT_API void webkit_soup_cache_clear (WebKitSoupCache *cache);
+
+WEBKIT_API void webkit_soup_cache_dump (WebKitSoupCache *cache);
+WEBKIT_API void webkit_soup_cache_load (WebKitSoupCache *cache);
+
+WEBKIT_API void webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
+ guint max_size);
+WEBKIT_API guint webkit_soup_cache_get_max_size (WebKitSoupCache *cache);
+
+G_END_DECLS
+
+
+#endif /* WEBKIT_SOUP_CACHE_H */
+
diff --git a/WebCore/platform/network/win/CookieJarWin.cpp b/WebCore/platform/network/win/CookieJarWin.cpp
index 2bdd6b3..a2afbc6 100644
--- a/WebCore/platform/network/win/CookieJarWin.cpp
+++ b/WebCore/platform/network/win/CookieJarWin.cpp
@@ -48,13 +48,17 @@ String cookies(const Document* /*document*/, const KURL& url)
{
String str = url.string();
- DWORD count = str.length() + 1;
- InternetGetCookie(str.charactersWithNullTermination(), 0, 0, &count);
+ DWORD count = 0;
+ if (!InternetGetCookie(str.charactersWithNullTermination(), 0, 0, &count))
+ return String();
+
if (count <= 1) // Null terminator counts as 1.
return String();
Vector<UChar> buffer(count);
- InternetGetCookie(str.charactersWithNullTermination(), 0, buffer.data(), &count);
+ if (!InternetGetCookie(str.charactersWithNullTermination(), 0, buffer.data(), &count))
+ return String();
+
buffer.shrink(count - 1); // Ignore the null terminator.
return String::adopt(buffer);
}
diff --git a/WebCore/platform/network/win/ResourceHandleWin.cpp b/WebCore/platform/network/win/ResourceHandleWin.cpp
index 5de2e1b..28035b9 100644
--- a/WebCore/platform/network/win/ResourceHandleWin.cpp
+++ b/WebCore/platform/network/win/ResourceHandleWin.cpp
@@ -27,6 +27,7 @@
#include "config.h"
#include "ResourceHandle.h"
+#include "DataURL.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "MainThread.h"
@@ -259,7 +260,7 @@ bool ResourceHandle::onRequestComplete()
bool ResourceHandle::start(NetworkingContext* context)
{
- if (request().url().isLocalFile()) {
+ if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) {
ref(); // balanced by deref in fileLoadTimer
if (d->m_loadSynchronously)
fileLoadTimer(0);
@@ -349,6 +350,11 @@ void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*)
RefPtr<ResourceHandle> protector(this);
deref(); // balances ref in start
+ if (firstRequest().url().protocolIsData()) {
+ handleDataURL(this);
+ return;
+ }
+
String fileName = firstRequest().url().fileSystemPath();
HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);