diff options
Diffstat (limited to 'WebCore/platform/network')
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); |